プログラミング・動画編集 備忘録

プログラミングや動画編集についての備忘録です

C#のRichTextBoxで描画処理を追加する

やりたいこと

背景色を1行全体に表示されるようにしたい。
当然ですが、SelectionBackColorで背景色を指定した場合、選択範囲のみに背景が適用されます。
しかし、その行の背景色を指定して、行全体を指定の背景にしたくてもできません。Rich Text Format(RTF)的にそのような考えはないのでしょう。
この記事では、自前ですべての描画を作るのではなく、RichTextEditの描画に追記する形で実現する方法を記載しています。

背景

ログビューワーの作成(RSkyrimLogViewer)を行っていて、"error:"という文字列があった場合、該当箇所の背景色を赤くするなど書式変更をする機能の実装を行いました。

実現方法がわかったので、実際に実装を行いました。


とりあえずの体裁は整えたのですが、今一つ気に入りません。
RichTextEditを継承したコントロールにて自前で描画処理を作れば、実現できることはわかりますので、やることにしました。

実現方法

リストボックスなりコンボボックスはDrawItemで必要な書き換えができたと思っていたので、それなりに簡単に行けると安易に思っていたのですが、どうやらTextBoxやRichTextBoxはオーナードローがなく、簡単にはできません。
このため、WM_PAINをフックして、OnPaintされたものに追加描画するような形になります。
テキストボックス内の改行位置について: DOBON.NETプログラミング掲示板過去ログ
ここでの、2つめの題目の2004/09/24 深山さんのサンプルがわかりやすく、ほぼそのまま利用しました。
改行位置に改行マークを追加する処理です。コード部分のみ抜粋します。

    private const Int32 WM_PAINT = 0x000F;

    protected override void WndProc(ref System.Windows.Forms.Message m)
    {
        base.WndProc(ref m);
        switch (m.Msg)
        {
            case WM_PAINT:
                Marking();
                break;
        }
    }

    private void Marking()
    {
        using (Graphics g = this.CreateGraphics())
        using (Brush brush = new SolidBrush(Color.DarkGreen))
        {
            // 改行検索
            Regex regex = new Regex("\n");
            foreach (Match match in regex.Matches(this.Text))
            {
                // 改行位置取得
                Point point = this.GetPositionFromCharIndex(match.Index);
                point.Offset(-2, 2);    // 出力位置微調整(お好みに応じて)

                // 改行記号(ここでは"↓")描画
                g.DrawString("↓", this.Font, brush, point);
            }
        }
    }

改行コードを検索し、描画上での左上の原点を取得して書き出す方法です。
ここから、実際に背景色を1行に適用する処理は、例えば背景色を変更した際の改行コードの位置をリストに記録し、描画時に改行位置からクライアント領域の端まで塗りつぶせは一応実現できます。
ただし、このままではデータが増えると処理が遅くなるかと思います。場合によってはチラつくかもしれません。
そのような場合、不要な描画をしないようにするなど対処が必要になってきます。

また、参考にした情報は古いため、現在ではもっとうまい方法があるかもしれません。

Qiitaで作り直した記事

qiita.com