2010-06-15 134 views
7

我想让我的头在WPF RichTextBox中的TextPointer类。如何跟踪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的好工作,如果您能告诉我,我将不胜感激。

回答

19

当您添加和删除从文本的FlowDocument,所有TextPointers基于一些旨在使他们留在接近相同的“地方”尽可能启发式的调整自己的位置。

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

你的情况发生了什么,你正在捕获的“caretBefore”位置恰好是插入了类型字符的TextPosition,在你的测试用例中你的LogicalDirection是LogicalDirection.Forward。因此,插入字符时,您的“caretBefore”将在之后以结尾,插入的字符与TextPosition一致,并为您提供空TextRange。

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

试试这个实验:

  1. 设置你的字号=“40”,并在构造函数中
  2. 点击B的右侧的文本“ABCD”预填充的RichTextBox并键入“X “B和C之间的LogicalDirection是向后的,所以你的”beforeCaret“在”X“之前结束并且你的MessageBox显示”X“。
  3. 点击C的左侧,并键入B和C. LogicalDirection之间的“X”是正向,因此,“X”之后的“beforeCaret”结束和你的MessageBox是空的。

这种行为是违反直觉的:当你不知道LogicalDirection存在时,你会认为单击B的右侧或C的左侧会给你完全相同的光标位置。

注:一个简单的方法来可视化正在发生的事情是指挥你的MessageBox.Show,而是做一个caretBefore.InsertTextInRun("^");

你如何达到你所需要的结果呢? LogicalDirection是只读的。一种方法是使用的TextRange强制的TextPointer的建设与向后LogicalDirection:

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有LogicalDirection of Backward:在文档前面的PreviewKeyDown和PreviewKeyUp之间的任何文本更改将导致符号偏移计算找到​​错误的位置,这是TextPointers设计要首先解决的问题。

我不知道有什么好的资源用于学习TextPointers,除了阅读文档并与之一起玩,这正是您已经在做的。

+0

嗨Ray, 非常感谢这样的信息回复。非常感谢,并且肯定有助于为我理清关于逻辑方向的事情 - 我尝试了您的实验。 我最终设想的是逐行处理文档。我知道GetLineStartPosition方法,但想知道是否知道如何从TextPointer中获取行尾的位置? 再次感谢, Alan – 2010-06-16 10:58:41

+0

您可以通过更改您传入的计数来控制GetLineStartPosition的移动方式。如果您想了解更多细节,了解它如何工作以及如何使用它来获得行结束位置,请在此处张贴另一个问题堆栈溢出。足以解释它需要一个单独的问题和答案。这些评论中的格式化能力也非常有限。 – 2010-06-16 15:27:02

+0

嗨Ray, 谢谢你回到我身边。我很欣赏评论的局限性,并在此发布了一个新问题:http://stackoverflow.com/questions/3060108/using-getlinestartposition-to-get-the-end-of-a-line-in-wpf- RichTextBox的 – 2010-06-17 08:38:57