VB.NET: Syntax Highlighting in a RichTextBox control
This last weekend I experimented a little bit with extending the functionality of the RichTextBox control. Below you’ll find an example of a small class that inherits from the RichTextBox control and allows you to implement syntax highlighting (with the use of a couple Win32 API call to smooth over the process of course.) The code pretty much speaks for itself.
Public Class SyntaxRTB
Inherits System.Windows.Forms.RichTextBox
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" _
(ByVal hWnd As IntPtr, ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
Private Declare Function LockWindowUpdate Lib "user32" (ByVal hWnd As Integer) As Integer
Private _SyntaxHighlight_CaseSensitive As Boolean = False
Private Words As New DataTable
'Contains Windows Messages for the SendMessage API call
Private Enum EditMessages
LineIndex = 187
LineFromChar = 201
GetFirstVisibleLine = 206
CharFromPos = 215
PosFromChar = 1062
End Enum
Protected Overrides Sub OnTextChanged(ByVal e As System.EventArgs)
ColorVisibleLines()
End Sub
Public Sub ColorRtb()
Dim FirstVisibleChar As Integer
Dim i As Integer = 0
While i < Me.Lines.Length
FirstVisibleChar = GetCharFromLineIndex(i)
ColorLineNumber(i, FirstVisibleChar)
i += 1
End While
End Sub
Public Sub ColorVisibleLines()
Dim FirstLine As Integer = FirstVisibleLine()
Dim LastLine As Integer = LastVisibleLine()
Dim FirstVisibleChar As Integer
If (FirstLine = 0) And (LastLine = 0) Then
'If there is no text it will error, so exit the sub
Exit Sub
Else
While FirstLine < LastLine
FirstVisibleChar = GetCharFromLineIndex(FirstLine)
ColorLineNumber(FirstLine, FirstVisibleChar)
FirstLine += 1
End While
End If
End Sub
Public Sub ColorLineNumber(ByVal LineIndex As Integer, ByVal lStart As Integer)
Dim i As Integer = 0
Dim Instance As Integer
Dim LeadingChar, TrailingChar As String
Dim SelectionAt As Integer = Me.SelectionStart
Dim MyRow As DataRow
Dim Line() As String, MyI As Integer, MyStr As String
' Lock the update
LockWindowUpdate(Me.Handle.ToInt32)
MyI = lStart
If CaseSensitive Then
Line = Split(Me.Lines(LineIndex).ToString, " ")
Else
Line = Split(Me.Lines(LineIndex).ToLower, " ")
End If
For Each MyStr In Line
Me.SelectionStart = MyI
Me.SelectionLength = MyStr.Length
If Words.Rows.Contains(MyStr) Then
MyRow = Words.Rows.Find(MyStr)
If (Not CaseSensitive) Or (CaseSensitive And MyRow("Word") = MyStr) Then
Me.SelectionColor = Color.FromName(MyRow("Color"))
End If
Else
Me.SelectionColor = Color.Black
End If
MyI += MyStr.Length + 1
Next
' Restore the selectionstart
Me.SelectionStart = SelectionAt
Me.SelectionLength = 0
Me.SelectionColor = Color.Black
' Unlock the update
LockWindowUpdate(0)
End Sub
Public Function GetCharFromLineIndex(ByVal LineIndex As Integer) As Integer
Return SendMessage(Me.Handle, EditMessages.LineIndex, LineIndex, 0)
End Function
Public Function FirstVisibleLine() As Integer
Return SendMessage(Me.Handle, EditMessages.GetFirstVisibleLine, 0, 0)
End Function
Public Function LastVisibleLine() As Integer
Dim LastLine As Integer = FirstVisibleLine() + (Me.Height / Me.Font.Height)
If LastLine > Me.Lines.Length Or LastLine = 0 Then
LastLine = Me.Lines.Length
End If
Return LastLine
End Function
Public Sub New()
Dim MyRow As DataRow
Dim arrKeyWords() As String, strKW As String
Me.AcceptsTab = True
''Load all the keywords and the colors to make them
Words.Columns.Add("Word")
Words.PrimaryKey = New DataColumn() {Words.Columns(0)}
Words.Columns.Add("Color")
arrKeyWords = New String() {"select", "insert", "delete", _
"truncate", "from", "where", "into", "inner", "update", _
"outer", "on", "is", "declare", "set", "use", "values", "as", _
"order", "by", "drop", "view", "go", "trigger", "cube", _
"binary", "varbinary", "image", "char", "varchar", "text", _
"datetime", "smalldatetime", "decimal", "numeric", "float", _
"real", "bigint", "int", "smallint", "tinyint", "money", _
"smallmoney", "bit", "cursor", "timestamp", "uniqueidentifier", _
"sql_variant", "table", "nchar", "nvarchar", "ntext", "left", _
"right", "like","and","all","in","null","join","not","or"}
For Each strKW In arrKeyWords
MyRow = Words.NewRow()
MyRow("Word") = strKW
MyRow("Color") = Color.LightCoral.Name
Words.Rows.Add(MyRow)
Next
End Sub
Public Property CaseSensitive() As Boolean
Get
Return _SyntaxHighlight_CaseSensitive
End Get
Set(ByVal Value As Boolean)
_SyntaxHighlight_CaseSensitive = Value
End Set
End Property
End Class
Update July 8th, 2008: Here’s a link that shows a couple tips that may help in writing your own Syntax Highlighting RichTextBox control:
http://codebetter.com/blogs/patricksmacchia/archive/2008/07/07/some-richtextbox-tricks.aspx