C# “我该怎么做?”;“恢复”;Wpf RichTextBox中的插入符号位置?

C# “我该怎么做?”;“恢复”;Wpf RichTextBox中的插入符号位置?,c#,.net,wpf,richtextbox,caret,C#,.net,Wpf,Richtextbox,Caret,将我的RichTextBox的文本设置为字符串T后,RichTextBox中的插入符号位置将“丢失”(它将转到它的开头)。以下是我在“丢失”后尝试“恢复”它所做的工作: 但是,此代码不起作用。“恢复的”插入符号与“原始”插入符号处于不同的位置(出于某些原因,始终位于“原始”插入符号的后面) 将RichTextBox的CaretPosition“保存”为文本指针似乎也不起作用 有谁能为我提供一种“恢复”插入符号的替代方法,或修复上述代码的方法吗?似乎对我有效: C.CaretPosition=C

将我的RichTextBox的文本设置为字符串T后,RichTextBox中的插入符号位置将“丢失”(它将转到它的开头)。以下是我在“丢失”后尝试“恢复”它所做的工作:

但是,此代码不起作用。“恢复的”插入符号与“原始”插入符号处于不同的位置(出于某些原因,始终位于“原始”插入符号的后面)

将RichTextBox的CaretPosition“保存”为文本指针似乎也不起作用

有谁能为我提供一种“恢复”插入符号的替代方法,或修复上述代码的方法吗?

似乎对我有效:

C.CaretPosition=C.Document.ContentStart;
C.CaretPosition=C.CaretPosition.GetPositionAtOffset(CaretIndex,LogicalDirection.Forward)


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

我最近处理了一个类似的问题,这就是我的解决方案。在我的例子中,我正在创建一个新的RichTextBox.documentcontent,当我这样做时,我希望保持插入符号的位置

我的想法是,插入符号偏移函数是有偏差的,这要感谢用于文本表示(段落、运行…)的数据结构,它们也以某种方式计算偏移位置

TextRange是获得文本中插入符号精确位置的好方法。问题在于它的恢复。但当我知道我的文档是从哪个组件构建的时,它就变得容易了。在我的例子中,只有段落和段落

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

代码:


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

在我的情况下,我有一个RichTextBox,其中只有一个段落,只允许输入文本和换行符。我更改了RichTextBox的结构(通过创建不同颜色的运行实例),但没有更改文本并在更改后恢复

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;
    }
}

检索索引并设置位置。根据文献记载,它们并不相同。尝试保存插入符号位置而不是插入符号索引。您似乎正在替换整个内容-如果有新文本,恢复插入符号有什么意义?特别是,如果插入符号在接近尾端时较暗,而新文本较短,该怎么办?@dlatikay尝试将插入符号位置保存为文本指针,会使“还原”的指针转到RichTextBox的开头。我正在替换撤消/重做系统的全部内容(请参阅)。为了回答你的第二个问题,似乎没有什么“不同”,插入符号只是转到“原始”插入符号行上的段落,或者返回几个字符。这(出于某种奇怪的原因)“效果更好”。当新文本改变文本大小时仍然有一些问题,但我非常确定,通过一些游戏,我可以使它工作(我一开始工作,就会把它张贴在这里)。“顺便说一句,我讨厌RichTextBox。”-你不是唯一一个。
// 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);
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;
    }
}