WPFのRichTextBoxで、特定の文字の文字色を変更してみた。

どもです。

今回は、WPFのRichTextBoxコントロールの文字色を変更する方法について紹介します。

WindowsフォームアプリケーションのRichTextBoxコントロールではなく、WPFのRichTextBoxコントロールですので、そこは間違えないようお願いします。

0. 作業環境

今回のエントリでは、以下の環境で作業を行います。

開発環境
項目 内容
OS Windows10 Pro(22H2)
CPU i7-8700
メモリ 16GB
IDE Visual Studio Commnuity 2022(64bit)
version 17.3.6
対象のフレームワーク .NET Framework 4.7.2

1. やってみる内容

今回は、RichTextBox内の特定の文字列の文字色を変更してみます。

具体的には、RichTextBoxコントロール内にgoogletest実行時にコンソール上に表示される文字列を表示し、(極力)同じ色になるよう文字に色をつけてみます。

こんなカンジに色付けします。



2.やってみる

RichTextBox内の特定の文字列に色を付けるためには、以下の手順を実施します。

  1. 特定の文字列の出現位置/範囲を決定する。
  2. 決定した出現位置/範囲に対して文字色を設定する。

各手順について、使用する関数や使用方法、実装を示していきます。

2.1. 特定の文字列の出現位置/範囲を決定する

「特定の文字列の出現位置/範囲を決定する」ためには、以下の手順を実施します。

  1. RichTextBox内の任意の文字列の開始位置を取得する。
  2. RichTextBox内の任意の文字列の終了位置を取得する。
  3. RichTextBox内の任意の文字列に対する「範囲」取得する。
  4. 取得した「範囲」の文字列と「特定の文字列」を比較、一致しているか確認する。
    • 一致していた場合には、「任意の文字列の開始位置」が「特定の文字列の出現位置」とする。
    • 一致していない場合には、次の「任意の文字列の開始位置を取得する」処理を実行する。

これらの処理を実行するコードは、以下のようになります。

//RichTextBox内の任意の文字列の開始位置を取得する。
TextPointer start = richTextBox.Document.ContentStart;
//RichTextBox内の任意の文字列の終了位置を取得する。
TextPointer stop = start.GetPositionAtOffset(textData.Length);

ここで、変数textDataは、「特手の文字列」に対応する変数です。

//RichTextBox内の任意の文字列に対する「範囲」取得する。
var wrapRange = new TextRange(start, stop);
//取得した「範囲」の文字列と「特定の文字列」を比較、一致しているか確認する。
var wrapText = wrapRange.Text;	//「範囲」の文字列を取得
if (wrapText.Equals(textData))
{
	//一致していた場合...
}
else
{
	//一致していなかった場合
}

少し補足をすると、「RichTextBox内の任意の文字列の開始位置を取得する」際にアクセスしているプロパティ「Document.ContentStart」は、RichTextBoxの内容の先頭位置に対応します。先頭位置から任意の文字数だけ後ろに移動した位置は、GetPositionAtOffset()メソッドを使用します。「任意の文字列の終了位置」は、「開始位置」に対応する変数startGetPositionAtOffset()メソッドにアクセスして取得しています。

2.2. 決定した出現位置/範囲に対して文字色を設定する

これは、先述のコード内wrapRange変数のApplyPropertyValue()メソッドを使用します。

具体的なコードは、以下の通りです。

wrapRange.ApplyPropertyValue(TextElement.ForegroundProperty, color);

ApplyPropertyValue()メソッドの対1引数に指定している値により、設定したい内容を指定します。今回は選択範囲の文字色を変更するため、TextElement.ForegroundPropertyを指定します。設定可能な値と、それに対応する設定内容は、Micorosoftのページを参照してください。

2.3. 処理全体

ここまでで紹介したコードをまとめたものが、以下のモノです。

private void HighLightText(string textData, SolidColorBrush color)
{
	int leftLen = 0;
	TextPointer start = richTextBox.Document.ContentStart;
	do
	{
		TextPointer stop = start.GetPositionAtOffset(textData.Length);
		var wrapRange = new TextRange(start, stop);
		var wrapText = wrapRange.Text;
		if (wrapText.Equals(textData))
		{
			wrapRange.ApplyPropertyValue(TextElement.ForegroundProperty, color);
			start = stop;
		}
		else
		{
			start = start.GetPositionAtOffset(1);
		}
		leftLen = start.GetOffsetToPosition(richTextBox.Document.ContentEnd);
	} while ((0 < leftLen) && (textData.Length < leftLen));
}

上述のコードでは、RichTextBoxの内容全体を走査して、該当した文字列全てに対して同じ文字色を設定するため、do~while文でループしています。

3. 実行結果

先述のコードを用いて、googletest実行時にコンソール上に表示される文字列の結果を色付けした結果が、以下です。



色そのものを完全に再現はできていませんが、色付けする箇所は同一にできていると思います。

4. まとめ

今回は、WPFのRichTextBoxを用いて、指定した文字列の色を変更する方法について紹介しました。

RichTextBoxの部分的な文字列の書式の変更を行うために必要な処理は、基本的には

  1. 変更範囲を特定・取得する
  2. 変更範囲に対して、書式を設定する

です。

WPFの場合には、「変更範囲を特定・取得する」際には、RichTextBoxのDocumentプロパティ(FlowDocumentオブジェクト)GetPositionAtOffset()メソッド、およびGetPositionAtOffset()メソッドを使用し、TextRangeオブジェクトで特定します。

また、書式の設定には、ApplyPropertyValue()メソッドを使用します。

これらのプロパティ、メソッド、およびオブジェクトを使用することで、書式の変更を行う手順を実現できるようになります。

この記事が、誰かの助けになれば幸いです。

ではっ!

Ex.公開しています

本エントリの内容、コードやサンプルをGitHunにて公開しています。エントリ中には書ききれなかった内容がありますので、コチラも是非さんしょうして下さい。