如何在WPF RichTextBox中跟踪TextPointer?

时间:2010-06-15 14:35:30

标签: c# wpf richtextbox

我正试图绕过WPF RichTextBox中的TextPointer类。

我希望能够跟踪它们,以便我可以将信息与文本中的区域相关联。

我目前正在使用一个非常简单的示例来尝试找出正在发生的事情。在PreviewKeyDown事件中,我存储了插入位置,然后在PreviewKeyUp事件中,我根据插入符号位置之前和之后创建了一个TextRange。这是一个代码示例,说明了我要做的事情:

// The caret position before typing
private TextPointer caretBefore = null;

private void rtbTest_PreviewKeyDown(object sender, KeyEventArgs e)
{
    // Store caret position
    caretBefore = rtbTest.CaretPosition;
}

private void rtbTest_PreviewKeyUp(object sender, KeyEventArgs e)
{
    // Get text between before and after caret positions
    TextRange tr = new TextRange(caretBefore, rtbTest.CaretPosition);
    MessageBox.Show(tr.Text);
}

问题是我得到的文字是空白的。例如,如果我输入字符'a',那么我希望在TextRange中找到文本“a”。

有谁知道出了什么问题?这可能是非常简单的事情,但我已经度过了一个下午无处可去。

我正在尝试采用新的WPF技术,但发现特别是RichTextBox非常复杂,甚至使这样的简单事情变得困难。如果有人有任何链接可以很好地解释TextPointer,如果你能告诉我,我将不胜感激。

2 个答案:

答案 0 :(得分:20)

当您从FlowDocument添加和删除文本时,所有TextPointers都会根据一系列启发式调整其位置,以使它们尽可能靠近相同的“位置”。

对于删除,这很简单:如果TextPointer位于已删除的文本中,则最终会删除已删除文本周围的字符。但是对于插入来说并不是那么简单:当文本或其他元素恰好在现有TextPointer中插入到FlowDocument中时,TextPointer应该在插入文本之前还是之后结束? TextPointer有一个名为“LogicalDirection”的属性来控制它。

在你的情况下发生的事情是你捕获的“caretBefore”位置正是插入了类型字符的TextPosition,在你的测试用例中你的LogicalDirection是LogicalDirection.Forward。因此,当插入字符时,你的“caretBefore”在插入的字符之后结束,这与TextPosition一致,为你提供一个空的TextRange。

TextPointer如何获取分配给它的LogicalDirection?如果单击RichTextBox以设置插入位置,则单击将被解释为位于两个字符之间。如果您单击的实际点位于第二个字符上,则LogicalDirection设置为Forward,但如果您单击的实际点是第一个字符,则LogicalDirection设置为Backward。

尝试此实验:

  1. 设置FontSize =“40”并在构造函数中使用文本“ABCD”预填充RichTextBox
  2. 单击B的右侧,在B和C之间键入“X”.LogicalDirection为Backward,因此“beforeCaret”在“X”之前结束,而MessageBox显示“X”。< / LI>
  3. 单击C的左侧,在B和C之间键入“X”.LogicalDirection为Forward,因此“beforeCaret”在“X”之后结束,并且MessageBox为空。
  4. 这种行为是违反直觉的:当您不知道LogicalDirection存在时,您会认为单击B的右侧或C的左侧会给您完全相同的插入位置。

    注意:可视化正在发生的事情的一种简单方法是命令你的MessageBox.Show而不是caretBefore.InsertTextInRun("^");

    您如何实现所需的结果? LogicalDirection是只读的。一种方法是使用TextRange强制构造具有LogicalDirection of Backward的TextPointer:

    caretBefore = new TextRange(caretBefore, caretBefore.DocumentEnd).Start;
    

    在PreviewKeyDown中执行此操作。如果你等到PreviewKeyUp已经太晚了:caretBefore已经移动了。这是有效的,因为据我所知,非空TextRange的开始始终具有向后的LogicalDirection。

    另一个选项是保存文档开头的符号偏移量(请注意,这不是字符偏移!)。在这种情况下,您可以在PreviewKeyDown中存储偏移量:

    caretBeforeOffset = caretBefore.DocumentStart.OffsetToPosition(caretBefore);
    

    并将caretBefore重置为PreviewKeyUp中的相同符号偏移量:

    caretBefore = caretBefore.DocumentStart.GetPositionAtOffset(caretBeforeOffset,
                                                                LogicalDirection.Forward);
    

    虽然这样做有点不像强制你的TextPointer有一个Backward的LogicalDirection:在PreviewKeyDown和PreviewKeyUp之间的文档中更早的任何文本更改都会导致符号偏移计算找到​​错误的位置,这就是TextPointers旨在解决这个问题。

    我不知道有什么好的资源可以学习TextPointers,除了阅读文档和玩它们,这正是你一直在做的。

答案 1 :(得分:0)

对我来说,TextPointer before = yourRichTextBox.CaretPosition.GetPositionAtOffset(-1, LogicalDirection.Backward);的工作是为了获得字符的位置,该位置恰好在插入符号前面。然后,您可以使用TextRange获得插入字符的TextRange range = new TextRange(before, yourRichTextBox.CaretPosition);(使用null时应检查before,因为如果插入符号前没有任何内容,{ {1}})