This last weekend I expirimented a little bit with extending the functionality of the RichTextBox control. Below you'll find an example of a small class that enherits 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.
PublicClass SyntaxRTB
Inherits System.Windows.Forms.RichTextBox
PrivateDeclareFunction SendMessage Lib "user32" Alias "SendMessageA" _
(ByVal hWnd As IntPtr, ByVal wMsg AsInteger, ByVal wParam AsInteger, ByVal lParam AsInteger) AsInteger
PrivateDeclareFunction LockWindowUpdate Lib "user32" (ByVal hWnd AsInteger) AsInteger
Private _SyntaxHighlight_CaseSensitive AsBoolean = False
Private Words AsNew DataTable
'Contains Windows Messages for the SendMessage API call
PrivateEnum EditMessages
LineIndex = 187
LineFromChar = 201
GetFirstVisibleLine = 206
CharFromPos = 215
PosFromChar = 1062
EndEnum
ProtectedOverridesSub OnTextChanged(ByVal e As System.EventArgs)
ColorVisibleLines()
EndSub
PublicSub ColorRtb()
Dim FirstVisibleChar AsInteger
Dim i AsInteger = 0
While i < Me.Lines.Length
FirstVisibleChar = GetCharFromLineIndex(i)
ColorLineNumber(i, FirstVisibleChar)
i += 1
EndWhile
EndSub
PublicSub ColorVisibleLines()
Dim FirstLine AsInteger = FirstVisibleLine()
Dim LastLine AsInteger = LastVisibleLine()
Dim FirstVisibleChar AsInteger
If (FirstLine = 0) And (LastLine = 0) Then
'If there is no text it will error, so exit the sub
ExitSub
Else
While FirstLine < LastLine
FirstVisibleChar = GetCharFromLineIndex(FirstLine)
ColorLineNumber(FirstLine, FirstVisibleChar)
FirstLine += 1
EndWhile
EndIf
EndSub
PublicSub ColorLineNumber(ByVal LineIndex AsInteger, ByVal lStart AsInteger)
Dim i AsInteger = 0
Dim Instance AsInteger
Dim LeadingChar, TrailingChar AsString
Dim SelectionAt AsInteger = Me.SelectionStart
Dim MyRow As DataRow
Dim Line() AsString, MyI AsInteger, MyStr AsString
' 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, " ")
EndIf
ForEach 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"))
EndIf
Else
Me.SelectionColor = Color.Black
EndIf
MyI += MyStr.Length + 1
Next
' Restore the selectionstart
Me.SelectionStart = SelectionAt
Me.SelectionLength = 0
Me.SelectionColor = Color.Black
' Unlock the update
LockWindowUpdate(0)
EndSub
PublicFunction GetCharFromLineIndex(ByVal LineIndex AsInteger) AsInteger
Return SendMessage(Me.Handle, EditMessages.LineIndex, LineIndex, 0)
EndFunction
PublicFunction FirstVisibleLine() AsInteger
Return SendMessage(Me.Handle, EditMessages.GetFirstVisibleLine, 0, 0)
EndFunction
PublicFunction LastVisibleLine() AsInteger
Dim LastLine AsInteger = FirstVisibleLine() + (Me.Height / Me.Font.Height)
If LastLine > Me.Lines.Length Or LastLine = 0 Then
LastLine = Me.Lines.Length
EndIf
Return LastLine
EndFunction
PublicSubNew()
Dim MyRow As DataRow
Dim arrKeyWords() AsString, strKW AsString
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 = NewString() {"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"}
ForEach strKW In arrKeyWords
MyRow = Words.NewRow()
MyRow("Word") = strKW
MyRow("Color") = Color.LightCoral.Name
Words.Rows.Add(MyRow)
Next
EndSub
PublicProperty CaseSensitive() AsBoolean
Get
Return _SyntaxHighlight_CaseSensitive
EndGet
Set(ByVal Value AsBoolean)
_SyntaxHighlight_CaseSensitive = Value
EndSet
EndProperty
EndClass
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