Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I "restore" the Caret Position in a Wpf RichTextBox?

After setting my RichTextBox's text to the string T, the Caret Position in the RichTextBox is "lost" (it goes to the start of it). Here's what I'm doing to try to "restore" it after it is "lost":

public static int GetCaretIndex(RichTextBox C)
{
    return new TextRange(C.Document.ContentStart, C.CaretPosition).Text.Length;
}
...
int CaretIndex = GetCaretIndex(C); // Get the Caret position before setting the text of the RichTextBox
new TextRange(C.Document.ContentStart, C.Document.ContentEnd).Text = T; // Set the text of the RichTextBox
C.CaretPosition = C.Document.ContentStart.GetPositionAtOffset(CaretIndex, LogicalDirection.Forward); // Set the Caret Position based on the "Caret Index" variable

This code, however, does not work. The "restored" Caret is at a different position than the "original" one (always behind the "original" one for some reason).

"Saving" the RichTextBox's CaretPosition as a TextPointer doesn't seem to work either.

Can anyone provide me with an alternative way of "restoring" the Caret, or a way to fix the code above?

like image 948
Polygons Avatar asked Feb 04 '23 09:02

Polygons


2 Answers

Seems to work (for me): C.CaretPosition = C.Document.ContentStart; C.CaretPosition = C.CaretPosition.GetPositionAtOffset(CaretIndex, LogicalDirection.Forward);

(I hate RichTextBox by the way.)

like image 105
Maciek Świszczowski Avatar answered Feb 06 '23 22:02

Maciek Świszczowski


I was dealing with a similar issue recently and there is my solution. In my case, I'm creating a new RichTextBox.Document content and when I do this, I want to keep the caret position.

My idea was that caret offset functions are biased thanks to data structures used for text representation (Paragraphs, Runs, ...) which are also somehow calculated to offset position.

TextRange is a good approach to get exact caret position in the text. The problem lays in its restoration. But it gets easy when I know from which components my document is constructed. In my case, there are just Paragraphs and Runs.

What remains is to visit document structure, find an exact run where the caret should be and set the caret to correct position of found run.

Code:

// backup caret position in text
int backPosition = 
    new TextRange(RichTextBox.CaretPosition.DocumentStart, RichTextBox.CaretPosition).Text.Length;

// set new content (caret position is lost there)
RichTextBox.Document.Blocks.Clear();
SetNewDocumentContent(RichTextBox.Document);

// find position and run to which place caret
int pos = 0; Run caretRun = null;
foreach (var block in RichTextBox.Document.Blocks)
{
    if (!(block is Paragraph para))
        continue;

    foreach (var inline in para.Inlines){
    {
        if (!(inline is Run run))
            continue;

        // find run to which place caret
        if (caretRun == null && backPosition > 0)
        {
            pos += run.Text.Length;
            if (pos >= backPosition){
                 caretRun = run;
                 break;
            }
        }
    }

    if (caretRun!=null)
        break;
}

// restore caret position
if (caretRun != null)
    RichTextBox.CaretPosition = 
        caretRun.ContentEnd.GetPositionAtOffset(backPosition - pos, LogicalDirection.Forward);

The code is not tested. I assembled it from various parts of my application. Let me know if you find any issue.

like image 31
Dave Avatar answered Feb 06 '23 21:02

Dave