- PR -

【C# RichTextBoxのRTFプロパティを固定するには】

投稿者投稿内容
mia
会議室デビュー日: 2005/10/16
投稿数: 12
投稿日時: 2006-02-12 22:31
いつもお世話になっております。
ただいまプログラミングエディタを作成しているのですが、たとえば次のように

int hoge;
hoge = 100;

と入力するとき、「int」の部分だけが自動的に特定の色になるようにしようとしています。今までに似たようなことを実現しようとした人のレスを見る限り、

・ RichTextBox.RTF
・ RichTextBox.SelectedRTF
・ RichTextBox.SelectionColor

のプロパティのいずれかを編集することで、色の変更が可能なことは分かりました。
しかし、次のように一部の文字色を変更し、テキスト内容をInsertなどで更新すると、すべての文字列が青色に変わってしまいました。


【例1】
int ; // ←これだと黒色(デフォルト)で表示される


ここで・・・
rich_box.Select(0, 3);
rich_box.SelectionColor = Color.Blue;
rich_box.Text = rich_box.Text.Insert(4, "hoge");


のように、intの文字色を変更して、"hoge"を追加すると・・・


【例2】
int hoge; // この行全部青色に変わる!


となるのです。
色が変わる前後での、rich_box.RTF の値を調べたところ、



●デフォルト
{
\rtf1\ansi\deff0{\fonttbl{\f0\fnil\fcharset128 MS UI Gothic;}}
\viewkind4\uc1\pard\lang1041\f0\fs24hoge\par
}

●intを青に変更して、hogeをInsertすると
{
\rtf1\ansi\deff0{\fonttbl{\f0\fnil\fcharset128 MS UI Gothic;}}
{\colortbl ;\red0\green0\blue255;}
\viewkind4\uc1\pard\cf1\lang1041\f0\fs24int hoge\par
}


となっていました。
つまりテキストの最初の文字色が、全体の設定色として表示されるみたいです。
そこで、\colortblを、

{\colortbl ;\red0\green0\blue0;\red0\green0\blue255;}

のようにRTFを一度書き換え、必要に応じて

\cf2 int \cf1

と、色を変えたい文字を、\cf(int)で囲むようにしたのですが、
テキスト文中のどこかでInsertやRemove、Replaceを実行すると、その都度、以前編集したRTF値が初期状態に戻されてしまいます。。。


分かりやすく書くと、


【最初】
int hoge // 全部黒

【RTFを一回編集して、\cf2 int \cf1 hoge 】
int hoge // intは青 hogeは黒

--------------------------------------------------
rich_box.Text = rich_box.Text.Insert(4, "hoge"); を実行。
--------------------------------------------------

int hogehoge // これが全部青に戻ってしまう!


// RTFを確認すると

{
\rtf1\ansi\deff0{\fonttbl{\f0\fnil\fcharset128 MS UI Gothic;}}
{\colortbl ;\red0\green0\blue255;}
\viewkind4\uc1\pard\cf1\lang1041\f0\fs24int hogehoge\par
}

に戻ってる。。。
この調子だと、一箇所の色を変更するたびに、他の全部の文字の色を変更しなおさなくてはなりません。一度変更したRTF値をそのまま固定するような方法はないでしょうか?

どなたかご教授いただけると幸いです。よろしくお願いします。
実は、自分なりに解決を試みた方法がありますので、このカキコの次に書きます。

[ メッセージ編集済み 編集者: mia 編集日時 2006-02-12 23:27 ]
mia
会議室デビュー日: 2005/10/16
投稿数: 12
投稿日時: 2006-02-12 22:50
自分で行った上記の解決方法として、

・ RTF値を保持しておくstatic変数(static_RTF)を宣言。
・ rich_box.Textが更新される度に、static_RTFを編集。
・ rich_box.RTFをKeyPressイベント毎に、static_RTFに置き換える。

を行いました。
そうすると、処理がわずかに重くなりますが、いちおう色の変換は予定通りに行うことができました。

しかし、RTFを直接編集するので、
テキスト上のカレット位置と、RTF上のカレット位置の統制を取ることが難しく、
特に日本語を入力したときに混乱を招く事態となりました。

このまま強引にソースを書いていくと、泥沼にはまりそうになったので、この度質問をさせていただくに至ったわけです。RTF値を保護できる方法や、RTFを直接編集するときに
カレット位置などの統制を取る良い方法など、皆様の知恵をお借りしたいです。
よろしくお願いしますm(__)m

[ メッセージ編集済み 編集者: mia 編集日時 2006-02-12 23:26 ]
じゃんぬねっと
ぬし
会議室デビュー日: 2004/12/22
投稿数: 7811
お住まい・勤務地: 愛知県名古屋市
投稿日時: 2006-02-12 22:56
引用:

miaさんの書き込み (2006-02-12 22:31) より:

rich_box.Text = rich_box.Text.Insert(0, "int ");
rich_box.Select(0, 3);
rich_box.SelectionColor = Color.Blue;
のように、"int"を追加して色を変更すると・・・
int hoge; // この行全部青色に変わる!


以下のコードで試しましたが、初回では再現しませんでした。
2 回目なら再現するでしょうけど。(^-^;)

コード:

    private void button1_Click(object sender, System.EventArgs e) {
        this.richTextBox1.Text = "int hoge";
        this.richTextBox1.Text = this.richTextBox1.Text.Insert(0, "int ");
        this.richTextBox1.Select(0, 3);
        this.richTextBox1.SelectionColor = Color.Blue;
    }


_________________
C# と VB.NET の入門サイト
じゃんぬねっと日誌
mia
会議室デビュー日: 2005/10/16
投稿数: 12
投稿日時: 2006-02-12 23:01
>じゃんぬさん
さっそくのレスありがとうございます。

はい。。すみません。
ちょっと書き間違えたようでして、すぐに書き直したつもりが、
またすれ違いになってしまいました(苦笑)

誤解させてしまいました。
もう修正しましたが、本当は・・・


【例1】
int ; // ←これだと黒色(デフォルト)で表示される

ここで・・・
rich_box.Select(0, 3);
rich_box.SelectionColor = Color.Blue;
rich_box.Text = rich_box.Text.Insert(4, "hoge");

のように、intの文字色を変更して、"hoge"を追加すると・・・

【例2】
int hoge; // この行全部青色に変わる!


ということでしたm(_)m
ジャンヌさんのコードから修正すると

private void button1_Click(object sender, System.EventArgs e) {
this.richTextBox1.Text = "int ";
this.richTextBox1.Select(0, 3);
this.richTextBox1.SelectionColor = Color.Blue;
this.richTextBox1.Text = this.richTextBox1.Text.Insert(4, "hoge");
}

で再現できるはずです。
よろしくお願いします。

[ メッセージ編集済み 編集者: mia 編集日時 2006-02-12 23:12 ]
mia
会議室デビュー日: 2005/10/16
投稿数: 12
投稿日時: 2006-02-15 14:22
直接的な解決法は見つかりませんでしたが、RTFプロパティを直接書き換えて別の場所に保存しておき、InsertやRemove文でプログラムからテキストを更新する際にRTFをごっそり入れ替えることにしました。

また何かありましたら、質問させていただきたいと思います。
コメントいただいたじゃんぬさん、ありがとうございましたm(_)m
Jitta
ぬし
会議室デビュー日: 2002/07/05
投稿数: 6267
お住まい・勤務地: 兵庫県・海手
投稿日時: 2006-02-15 21:44
引用:

miaさんの書き込み(2006-02-12 23:01)より:
コード:
private void button1_Click(object sender, System.EventArgs e) {
	this.richTextBox1.Text = "int ";
	this.richTextBox1.Select(0, 3);
	this.richTextBox1.SelectionColor = Color.Blue;
	this.richTextBox1.Text = this.richTextBox1.Text.Insert(4, "hoge");
}




 テキスト全体を書き換えているから、じゃないですかねぇ?
 Text プロパティを書き換えると、それを元に RTF 文字列を再構築します。その為、書式は失われます。え?「Insert メソッドで、挿入している」って?だったら、上のコードの太字にした部分は、なんでしょう?今ある文字列に挿入するなら、「this.richTextBox1.Text.Insert(4, "hoge");」だけのはずですよね。

 SelectedRtf プロパティを使ってください。これ、RTF 形式でなければ無視されますから(たぶん…おそらく…きっと…)、注意してください。こんな感じかな?
コード:
	this.richTextBox1.Select(0, 3);
	this.richTextBox1.SelectionColor = Color.Blue;
	this.richTextBox1.Select(4, 0);
	string selectedRtf = this.richTextxBox1.SelectedRtf;
	int index = selectedRtf.LastIndexOf('}');
	this.richTextxBox1.SelectedRtf = selectedRtf.Insert(index, "hoge");


 4文字目から0文字選択することでキャレットを移動し、何も選択していない RTF 文字列を取得します。{\rtf1...} となっていますので、ブレースの内側に文字列を入れてやる必要があります(たぶん。そうだと思い込んでいる)。最後から } を探して、その前に挿入したい文字列を挿入し、出来た RTF 文字列を SelectedRtf プロパティに設定してやります。

〆 written by Jitta on 2006/02/15
mia
会議室デビュー日: 2005/10/16
投稿数: 12
投稿日時: 2006-02-16 01:40
Jittaさん、書き込みどうもです。

引用:

Jittaさんの書き込み (2006-02-15 21:44) より:
今ある文字列に挿入するなら、「this.richTextBox1.Text.Insert(4, "hoge");」だけのはずですよね。


String.Insert()はvoid型ではなく、string型を返す関数なので、

コード:

this.richTextBox1.Text = this.richTextBox1.Text.Insert(4, "hoge");



としないと変更が画面に反映されません。
Jittaさんのおっしゃることを試すために、以下のようなサンプルプログラムを書きました。


コード:

private void button2_Click(object sender, EventArgs e)
{
this.richTextBox1.Text = this.richTextBox1.Text.Insert(0, "int ");
this.richTextBox1.Select(0, 3);
this.richTextBox1.SelectionColor = Color.Blue;
this.richTextBox1.Select(4, 0);
string selectedRtf = this.richTextBox1.SelectedRtf;
int index = selectedRtf.LastIndexOf('}');
this.richTextBox1.SelectedRtf = selectedRtf.Insert(index, "hoge");
}

private void button3_Click(object sender, EventArgs e)
{
this.richTextBox1.Select(4, 4);
this.richTextBox1.SelectionColor = Color.Red;
this.richTextBox1.Select(8, 0);

string selectedRtf2 = this.richTextBox1.SelectedRtf;
int index2 = selectedRtf2.LastIndexOf('}');
this.richTextBox1.SelectedRtf = selectedRtf2.Insert(index2, "hoge");
}


ボタン2をクリックすると、「int(青)hoge(黒)」と表示され、続いて
ボタン3をクリックすると、「int(青)hogehoge(赤)」と表示されました。

hogehogeのうち、前半のhogeだけを赤色にするようプログラムを組んだのですが、
どうやら色を指定した文字に連続して文字を入力すると、直前の文字の色が次の文字に継承されるみたいです。後半のhogeまで赤色になります。

「this.richTextBox1.SelectionColor = Color.Black;」みたいなのを間にいれるなど、工夫しないとならないみたいですね。

コード:

private void button4_Click(object sender, EventArgs e)
{
this.richTextBox1.Select(4, 4);
this.richTextBox1.SelectionColor = Color.Red;
this.richTextBox1.Select(8, 0);

this.richTextBox1.SelectionColor = Color.Black;
string selectedRtf2 = this.richTextBox1.SelectedRtf;
int index2 = selectedRtf2.LastIndexOf('}');
this.richTextBox1.SelectedRtf = selectedRtf2.Insert(index2, "hoge");
}


だと、ボタン2⇒ボタン4で、int(青)hoge(赤)hoge(黒) となりました。
でも、これに1行コードを追加したらおかしな現象が起きました。


コード:

private void button5_Click(object sender, EventArgs e)
{
this.richTextBox1.Select(4, 4);
this.richTextBox1.SelectionColor = Color.Red;
this.richTextBox1.Select(8, 0);

this.richTextBox1.SelectionColor = Color.Black;
string selectedRtf2 = this.richTextBox1.SelectedRtf;
int index2 = selectedRtf2.LastIndexOf('}');
this.richTextBox1.SelectedRtf = selectedRtf2.Insert(index2, "hoge");

this.richTextBox1.Text = this.richTextBox1.Text.Remove(4, 2);
}


前半のhogeのhoを削除するコードです。
予定では、int(青)geho(赤)ge(黒)にでもなるのかな?と思ったら・・・

int gehoge (全部青!)

となりました・・・w
Removeするときも、SelectedRTFで必要な部分だけ削除するみたいにしないとならないのでしょうね。やっぱり選択してから色を変える方法だと問題が多々ありそうなので、当初の解決法のままでいこうと思います。今のトコ、問題なく表示できているので・・・(でも本質的にはRTFの直接編集なので同じでしょうが)。

Jittaさんも、ありがとうございました。
この問題についてこの先も議論するのであれば、またこの板を覗いてみようと思います。

[ メッセージ編集済み 編集者: mia 編集日時 2006-02-16 01:56 ]
Kazuki
ぬし
会議室デビュー日: 2004/10/13
投稿数: 298
投稿日時: 2006-02-16 07:12
InsertやRemoveを使わなくてもSelectedTextが使えると思いますよ

スキルアップ/キャリアアップ(JOB@IT)