如何在Wpf RichTextBox中“恢复”插入位置?

时间:2017-05-28 21:08:48

标签: c# .net wpf richtextbox caret

将RichTextBox的文本设置为字符串T后,RichTextBox中的插入位置“丢失”(它将转到它的开头)。这就是我正在做的事情,试图在“丢失”之后“恢复”它:

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

但是,此代码不起作用。 “恢复的”Caret处于与“原始”Caret不同的位置(由于某种原因,总是位于“原始”的位置之后)。

将RichTextBox的CaretPosition“保存”为TextPointer似乎也不起作用。

任何人都可以为我提供“恢复”Caret的替代方法,或者修复上述代码的方法吗?

3 个答案:

答案 0 :(得分:3)

我最近正在处理类似的问题,并且有我的解决方案。就我而言,我正在创建一个新的RichTextBox.Document内容,当我这样做时,我想保持插入位置。

我的想法是,由于用于文本表示的数据结构(段落,运行,......),插入符号偏移函数存在偏差,这些数据结构也以某种方式计算以抵消位置。

TextRange是一种在文本中获得精确插入位置的好方法。问题在于它的恢复。但是当我知道构建文档的组件时,它变得容易了。就我而言,只有段落和运行。

剩下的就是访问文档结构,找到插入符所在的确切位置,并将插入符号设置为找到运行的正确位置。

代码:

// 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);

代码未经过测试。我从应用程序的各个部分组装了它。如果您发现任何问题,请告诉我。

答案 1 :(得分:2)

似乎工作(对我来说): C.CaretPosition = C.Document.ContentStart; C.CaretPosition = C.CaretPosition.GetPositionAtOffset(CaretIndex, LogicalDirection.Forward);

(顺便说一句,我讨厌RichTextBox。)

答案 2 :(得分:0)

在我的情况下,我有一个带有单个段落的RichTextBox,该段落仅允许输入文本和换行符。我更改了RichTextBox的结构(通过创建不同颜色的Run实例),但未更改文本,并在更改后还原。

public static class CaretRestorer
{
    public static void Restore(RichTextBox richTextBox, Action changer)
    {
        var caretPosition = GetCaretPosition(richTextBox);
        changer();
        Restore(richTextBox, caretPosition);
    }
    private static string GetFullText(RichTextBox richTextBox)
    {
        return new TextRange(richTextBox.Document.ContentStart, richTextBox.Document.ContentEnd).Text;
    }
    private static int GetInlineTextLength(Inline inline)
    {
        if(inline is LineBreak)
        {
            return 2;
        }
        return new TextRange(inline.ContentStart, inline.ContentEnd).Text.Length;
    }
    private static void Restore(RichTextBox richTextBox,int caretPosition)
    {
        var inlines = GetInlines(richTextBox);
        var accumulatedTextLength = 0;
        foreach (var inline in inlines)
        {
            var inlineTextLength = GetInlineTextLength(inline);
            var newAccumulatedTextLength = accumulatedTextLength + inlineTextLength;
            if (newAccumulatedTextLength >= caretPosition)
            {
                TextPointer newCaretPosition = null;
                if(inline is LineBreak)
                {
                    newCaretPosition = inline.ContentEnd;
                }
                else
                {
                    var diff = caretPosition - accumulatedTextLength;
                    newCaretPosition = inline.ContentStart.GetPositionAtOffset(diff);
                }
                
                richTextBox.CaretPosition = newCaretPosition;
                break;
            }
            else
            {
                accumulatedTextLength = newAccumulatedTextLength;
            }
        }
    }
    private static int GetCaretPosition(RichTextBox richTextBox)
    {
        return new TextRange(richTextBox.Document.ContentStart, richTextBox.CaretPosition).Text.Length;
    }

    

    private static Paragraph GetParagraph(RichTextBox RichTextBox)
    {
        return RichTextBox.Document.Blocks.FirstBlock as Paragraph;
    }
    private static InlineCollection GetInlines(RichTextBox RichTextBox)
    {
        return GetParagraph(RichTextBox).Inlines;
    }
}