RichTexBoxで文字に色付けする

タグの編集
投稿者 XPMAX2008  (中学生) 投稿日時 2010/10/10 18:35:21
で、ソフトを作っているのですが、
RichTextboxで、書き込み中に、指定の文字(.Train , .FreeObjectなど)に色付けすることはできないでしょうか。
”abc”を青
”efg”を赤
にする方法を教えてください。
投稿者 るきお  (社会人) 投稿日時 2010/10/10 19:54:27
こんにちは。


RichTextBoxの表示で"abc"の部分だけを青くするには次のようにします。
    
Dim pattern As String = "abc"
Dim matches = System.Text.RegularExpressions.Regex.Matches(RichTextBox1.Text, pattern)

For Each match As System.Text.RegularExpressions.Match In matches
    Dim start As Integer = match.Index
    Dim length As Integer = match.Length
    RichTextBox1.Select(start, length)
    RichTextBox1.SelectionColor = Color.Blue
Next


"efg"を赤くするには次のようにします。
    
Dim pattern As String = "efg"
Dim matches = System.Text.RegularExpressions.Regex.Matches(RichTextBox1.Text, pattern)

For Each match As System.Text.RegularExpressions.Match In matches
    Dim start As Integer = match.Index
    Dim length As Integer = match.Length
    RichTextBox1.Select(start, length)
    RichTextBox1.SelectionColor = Color.Red
Next


以上の例では文字列"abc"や"efg"の検索に正規表現を使っているので対象の文字列に記号が含まれている場合は一工夫必要な場合があります。(正規表現のことがわかっていれば特別な工夫は必要ないです。)
参考に、".Tranin"を赤くする例を紹介します。
    
Dim pattern As String = "\.Train"
Dim matches = System.Text.RegularExpressions.Regex.Matches(RichTextBox1.Text, pattern)

For Each match As System.Text.RegularExpressions.Match In matches
    Dim start As Integer = match.Index
    Dim length As Integer = match.Length
    RichTextBox1.Select(start, length)
    RichTextBox1.SelectionColor = Color.Red
Next
投稿者 るきお  (社会人) 投稿日時 2010/10/10 21:38:10
失礼しました。「書き込み中に」色を付けたいのですね。
上述の例はボタンなどをクリックしたタイミングで色を付ける場合です。

しかしながら、RichTextBoxはどういう意地悪なのか書き込み中に簡単に色を変える方法が提供されていません。
がんばってプログラムすれば可能なのですがなかなか難しいですし、プログラムの量もかなり多くなります。
少しチャレンジしてみましたが、どうも妙です。色が変わる単語(abc, efg, .Train)を打った直後にはスペースが入力できなくなってしまいました。ほかにももっといろいろやると問題がたくさんありそうです。
そんなプログラムでも多少の参考にはなると思いますので載せておきます。
誰か完成させてください。

  
Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.ObjectByVal e As System.EventArgs) Handles MyBase.Load

        Dim adapter As New Rtf.RichTextBoxAdapter(RichTextBox1)

        adapter.ColorTexts.Add("abc", Color.Blue)
        adapter.ColorTexts.Add("efg", Color.Red)
        adapter.ColorTexts.Add("\.Train", Color.Green)

    End Sub
End Class


Namespace Rtf

    'RTFの仕様 
    'http://msdn.microsoft.com/en-us/library/aa140301(v=office.10).aspx#rtfspec_12 

    'RTFの例 
    '{\rtf1\ansi\ansicpg932\deff0\deflang1033\deflangfe1041{\fonttbl{\f0\fswiss\fcharset0 Arial;}{\f1\fnil\fcharset128 MS UI Gothic;}} 
    '{\colortbl ;\red0\green0\blue255;\red255\green0\blue0;} 
    '\viewkind4\uc1\pard\lang1041\f0\fs18 AAAAA\par 
    '\cf1 abc\cf0\par 
    'BBBBB\par 
    '\cf2 efg\cf0\par 
    'CCCCC\f1\par 
    '} 

    Public Class RichTextBoxAdapter

        Public WithEvents Source As RichTextBox

        Public ColorTexts As New Dictionary(Of String, Color)

        Public Sub New(ByVal source As RichTextBox)
            Me.Source = source
        End Sub

        Private Sub source_TextChanged(ByVal sender As System.ObjectByVal e As System.EventArgs) Handles Source.TextChanged

            If ColorTexts.Count = 0 Then
                Return
            End If

            'TextBox1.Text = RichTextBox1.Rtf 

            Static stopper As Boolean

            If stopper Then
                Return
            End If

            stopper = True

            Dim typedSender As RichTextBox = sender

            Dim currentPos As Integer = typedSender.SelectionStart

            Dim rtf As New Rtf.Document(typedSender.Rtf)
            rtf.ClearTextMark()

            For Each Text As String In ColorTexts.Keys
                Dim color As Color = ColorTexts(Text)
                rtf.MarkText(Text, color)
            Next

            typedSender.Rtf = rtf

            typedSender.SelectionStart = currentPos

            stopper = False

        End Sub

    End Class

    Public Class Document

        Public Rtf As String
        Public ColorTable As ColorTable

        Public Sub New(ByVal rtf As String)
            Me.Rtf = rtf
            Me.ColorTable = New ColorTable(rtf)
        End Sub

        'テキストの色をすべてクリアする。 
        Public Sub ClearTextMark()
            Dim pattern As String = "\\cf[1-9]+?\s+?"
            Me.Rtf = System.Text.RegularExpressions.Regex.Replace(Me.Rtf, pattern, "")

            pattern = "\\cf0"
            Me.Rtf = System.Text.RegularExpressions.Regex.Replace(Me.Rtf, pattern, "")
        End Sub
        ''' <summary> 
        ''' Textは正規表現のエスケープ付きで指定してください。 
        ''' たとえば、「Dim」が対象の時は単に"Dim"で良いですが、 
        ''' .Trainが対象の時は"\.Train"とする必要があります。 
        ''' </summary> 
        ''' <param name="text"></param> 
        ''' <param name="color"></param> 
        ''' <remarks></remarks> 
        Public Sub MarkText(ByVal text As StringByVal color As Color)

            'colorが現在のColorTableにあるか確認 
            Dim colorIndex As Integer = Me.ColorTable.IndexOf(color) + 1

            If colorIndex < 1 Then
                '存在しなければ追加する 
                Me.ColorTable.Add(color)
                colorIndex = Me.ColorTable.IndexOf(color) + 1
            End If

            '色がついたテキストは次のようになっている。 
            'ここでcf1の1がColorIndex 
            '\cf1 abc\par 
            Dim result As String = "\cf" & colorIndex & " " & text & "\cf0"
            Me.Rtf = System.Text.RegularExpressions.Regex.Replace(Me.Rtf, text, result)

        End Sub
投稿者 るきお  (社会人) 投稿日時 2010/10/10 21:39:16
長いので分割しています。上の続きです。

  
        Public Sub RenderAll()
            RenderColorTable(Me.ColorTable)
        End Sub
        Public Sub RenderColorTable(ByVal table As ColorTable)

            Dim newTable As String = table.ToString

            'ColorTableの存在チェック 
            Dim match = System.Text.RegularExpressions.Regex.Match(Me.Rtf, ColorTable.RegExPattern)

            If match.Success Then
                'カラーテーブルが存在する場合は置換 
                Me.Rtf = System.Text.RegularExpressions.Regex.Replace(Me.Rtf, ColorTable.RegExPattern, newTable)
            Else
                '存在しない場合は挿入 
                Dim pos As String = Me.Rtf.IndexOf("}}")    'これでいいんだろうか?RTFの仕様がよくわからない。 
                Me.Rtf = Me.Rtf.Insert(pos + 2, newTable)
            End If


        End Sub

        Public Overrides Function ToString() As String

            RenderAll()
            Return Me.Rtf

        End Function

        Shared Narrowing Operator CType(ByVal value As Rtf.Document) As String

            Return value.ToString

        End Operator


    End Class

    'ColorTableの例 
    '    {\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255; 
    '\red0\green255\blue0; 
    '\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0; 
    '\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128; 
    '\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0; 
    '\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;} 

    Public Class ColorTable
        Inherits List(Of Color)

        Public Const RegExPattern As String = "\{\\colortbl\s*?;(.*?)\}"
        'Public Colors As New List(Of Color) 

        Public Sub New(ByVal rtf As String)

            'ColorTable部分の抜出 
            Dim pattern As String = RegExPattern
            Dim match = System.Text.RegularExpressions.Regex.Match(rtf, pattern)

            If Not match.Success Then
                Return
            End If

            Dim entries As New List(Of String)(Split(match.Groups(1).Value, ";"))

            pattern = "\\red(?<red>[0-9]+)\\green(?<green>[0-9]+)\\blue(?<blue>[0-9]+)"
            For Each entry In entries

                If Len(entry) = 0 Then
                    Continue For
                End If

                'このときentryは次のような形式 
                '\red0\green255\blue0 
                Dim params = System.Text.RegularExpressions.Regex.Match(entry, pattern)
                Me.Add(Color.FromArgb(params.Groups("red").Value, params.Groups("green").Value, params.Groups("blue").Value))
            Next

        End Sub

        Public Overrides Function ToString() As String

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

            Dim result As New System.Text.StringBuilder(16)

            For Each Color In Me
                result.Append(";\red" & Color.R & "\green" & Color.G & "\blue" & Color.B)
            Next

            If result.Length = 0 Then
                Return ""
            End If

            Return "{\colortbl " & result.ToString & ";}"

        End Function

    End Class

End Namespace
投稿者 (削除されました)  () 投稿日時 2010/10/12 17:09:13
(削除されました)
投稿者 XPMAX2008  (中学生) 投稿日時 2010/10/14 21:06:03
るきおさん、ありがとうございます。
まさに僕が探し求めていたのは、こういうことです。
調べてもなかなか出てこないので・・・
ここで質問してよかったです。
ちなみに、.Trainや、abcの後の色が赤や緑などに変更されたままだったので、この文を足して直しました。
Private Sub RichTextBox1_TextChanged(ByVal sender As System.ObjectByVal e As System.EventArgs) Handles RichTextBox1.TextChanged
        RichTextBox1.SelectionColor = Color.Black
End Sub
 
投稿者 XPMAX2008  (中学生) 投稿日時 2011/1/9 13:34:28
Dim pattern As String = "efg"
Dim matches = System.Text.RegularExpressions.Regex.Matches(RichTextBox1.Text, pattern)

For Each match As System.Text.RegularExpressions.Match In matches
    Dim start As Integer = match.Index
    Dim length As Integer = match.Length
    RichTextBox1.Select(start, length)
    RichTextBox1.SelectionColor = Color.Red
Next

このコードで、「"」の色を茶色(Color.Brown)に変えるにはどうすればよいですか?
投稿者 よねKEN  (社会人) 投稿日時 2011/1/9 14:03:43
> このコードで、「"」の色を茶色(Color.Brown)に変えるにはどうすればよいですか? 

Dim pattern As String = """"

文字列リテラルで「"」を表現するときは二重にした「""」を使います。
投稿者 xvmm  (中学生) 投稿日時 2011/1/9 14:37:01
よねKENさん。ありがとうございます。
あと、
VB.NETの、「'」や、HTMLの「<!-- *** -->」間を緑にする方法を教えてください。
投稿者 よねKEN  (社会人) 投稿日時 2011/1/9 16:41:33
> VB.NETの、「'」や、HTMLの「<!-- *** -->」間を緑にする方法を教えてください。 

「"」じゃなくて「'」ですか?

' HTMLのコメント用(「<!-- あいう -->」などにマッチする)
Dim HtmlComment As String = "<!--.*?-->"  ' ポイントは「?」で最短一致にしているところ。

' VBの文字用 (「'あ'」などにマッチする)
' 「'」で囲まれた任意の文字列(ただし「'」自身は含まない)
Dim VBChar As String = "'[^']*'" 

ついでに、VBの文字列の抽出用も以下に記載しておきます。

' VBの文字列用
' ("あいう"や"あいう""えお"などにマッチする)
Dim VBString As String = """(""""|[^""])*"""

上記の各正規表現を理解するには・・・

MSDNの「正規表現言語要素」(下記URL)の解説を読んでみてください。
※正直説明がわかりやすいとは言えないので他のサイトも含めて
 「正規表現」でいろいろ調べてみてください。
http://msdn.microsoft.com/ja-jp/library/az24scfc.aspx
投稿者 よねKEN  (社会人) 投稿日時 2011/1/9 16:54:33
勘違いしていたので、補足です。

> VB.NETの、「'」や、HTMLの「<!-- *** -->」間を緑にする方法を教えてください。 

勘違いしてました。「'」の方はVBのコメントのことをお聞きになりたいのですね。
(VB.NETの文字の表現は"あ"cのように書きます。C#の文字の表現'あ'と勘違いしてました)

Dim VBComment As String = "'.*\n"  ' 「'」で始まって改行までがコメント

(ちなみにVBのコメントにはREMキーワードを使った書き方もありますが、
 最近使う人はあまりいないので無視しちゃっても構わないと思います。)