Reputation: 620
I am using a WPF richtextbox to create a syntax highlighter for code provided in a textbox. I want to have it automatically adjust the rich textbox as I am typing, using a thread timer I reset the contents of the rich textbox. I am trying to keep the cursor where it is suppose to be. I don't loose any text characters during the parse (except line breaks). I don't know how to keep the caret position where the user has left it in the text. It defaults to the end of the document.
I attempt to store the current caret position in the text and then set it to the document after I make my changes, however the error I receive is "Cannot set CaretPosition to be outside of RichTextBox." So I set the caret position to the bottom of the document.
WPF, VB.net, .net Framework 4.0
Here is the code.
Public Sub FormatText()
If IsNothing(rtfContent.Document) Then
Exit Sub
End If
Me.rtfContent.IsEnabled = False
Me.rtfContent.Refresh()
Me.Refresh()
System.Threading.Thread.Sleep(50)
Dim curRange As TextRange = New TextRange(rtfContent.Document.ContentStart, rtfContent.CaretPosition)
Dim caretBefore As TextPointer = rtfContent.CaretPosition
caretBefore = New TextRange(caretBefore, caretBefore.DocumentEnd).Start
Dim caretBeforeOffset As Integer = rtfContent.Document.ContentStart.GetOffsetToPosition(caretBefore)
Dim docRange As TextRange = New TextRange(rtfContent.Document.ContentStart, rtfContent.Document.ContentEnd)
docRange.ClearAllProperties()
Dim myText As String = docRange.Text
Dim Lines As List(Of String) = Split(myText, vbCrLf).ToList
Dim MyBlocks As New List(Of Block)
Dim count As Integer = 1
MyVars.Clear()
myText = ""
For Each Line In Lines
If count = Lines.Count Then
Exit For
End If
If Mid(Line, 1, 1) = "$" Then
Dim words As String() = Split(Line, " ", 2)
MyVars.Add(words(0))
myText &= "<Paragraph Margin=""0,0,0,0"" Padding=""0,0,0,2"">" & Line & "</Paragraph>"
ElseIf Mid(Line, 1, 1) = "#" Then
myText &= "<Paragraph Margin=""0,0,0,0"" Padding=""0,0,0,2""><Run Foreground=""#FFFFA500"">" & Line & "</Run></Paragraph>"
Else
myText &= "<Paragraph Margin=""0,0,0,0"" Padding=""0,0,0,2"">" & Line & "</Paragraph>"
End If
count += 1
Next
myText = Regex.Replace(myText, "\$(\w*)", "<Run Foreground=""#FFFF0000"">$$$1</Run>")
For Each var In MyVars
myText = Regex.Replace(myText, "<Run Foreground=""#FFFF0000"">\" & var & "</Run>", "<Run Foreground=""#FF32CD32"">" & var & "</Run>")
Next
myText = myText.Replace("copy", "<Run Foreground=""#FF87CEFA"">copy</Run>")
myText = myText.Replace("delete", "<Run Foreground=""#FF87CEFA"">delete</Run>")
myText = myText.Replace("output", "<Run Foreground=""#FF87CEFA"">output</Run>")
myText = myText.Replace("clear", "<Run Foreground=""#FF87CEFA"">clear</Run>")
myText = "<FlowDocument PagePadding=""5,0,5,0"" AllowDrop=""True"" NumberSubstitution.CultureSource=""User"" xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"">" & myText & "</FlowDocument>"
Me.rtfContent.Document.Blocks.Clear()
Me.rtfContent.Document = TryCast(XamlReader.Parse(myText), FlowDocument)
caretBefore = caretBefore.DocumentStart.GetPositionAtOffset(caretBeforeOffset, LogicalDirection.Forward)
Try
Me.rtfContent.CaretPosition = caretBefore
Catch
Me.rtfContent.CaretPosition = rtfContent.Document.ContentEnd
End Try
Me.rtfContent.IsEnabled = True
Me.rtfContent.Refresh()
Me.Refresh()
End Sub
Upvotes: 1
Views: 1334
Reputation: 641
I have an idea which might help. Does Point GetPositionFromCharIndex(int index)
, PointToClient
and PointToScreen
functions help? You can store which char index the mouse cursor was over and after generating the code put it back in the mentioned position. You can find usage of those functions here.
Upvotes: 1