当 Run.Text 仅包含一个 space 字符时,如何将 RichTextBox.Caret 设置为 Run.ContentStart

How to set RichTextBox.Caret to a Run.ContentStart, when Run.Text contain only an space-character

我有一个这样的 FlowDocument:

<FlowDocument>
  <Paragraph>
    <Span>
      <Run Background="#FFFFDAB9">{</Run>
      <Run Background="#FFB0E0E6">a</Run>
      <Run FontWeight="Bold" Background="#FFFFDAB9">||</Run>
      <Run Background="#FFB0E0E6">b</Run>
      <Run Background="#FFFFDAB9">}</Run>
    </Span>
  </Paragraph>
</FlowDocument>

当我将 RichTextBox.Caret 设置为第二个 Run.ContentStart(在“a”字符之前)时,一切正常。但是,当我将字符“a”替换为“”(简单的 space 字符)时,我无法再将插入符号放置到 运行.

的 ContentStart 中
<FlowDocument>
  <Paragraph>
    <Span>
      <Run Background="#FFFFDAB9">{</Run>
      <Run Background="#FFB0E0E6" xml:space="preserve"> </Run>
      <Run FontWeight="Bold" Background="#FFFFDAB9">||</Run>
      <Run Background="#FFB0E0E6">b</Run>
      <Run Background="#FFFFDAB9">}</Run>
    </Span>
  </Paragraph>
</FlowDocument>

插入符将保留在“}”(第一个 Run.ContentEnd)后面。

我制作了一个简单的“调试”功能来显示和设置 RichTextBox.Caret。要对此进行测试,您必须替换换行符,因此您有一个单行 XAML。我刚刚格式化了 XAML 以提高可读性。

Private Sub SetRichTextBoxCarret(thisRTB As RichTextBox, thisTP As TextPointer, Optional additionalOffset As Int32 = 0)
    Dim myDirection = If(CBool(thisRTB.CaretPosition.LogicalDirection), LogicalDirection.Backward, LogicalDirection.Forward)

    Debug.WriteLine("")
    Debug.WriteLine(String.Format("SetRichTextBoxCarret - TextPointerOffset: {0}", thisRTB.Document.ContentStart.GetOffsetToPosition(thisTP)))
    Debug.WriteLine(String.Format("SetRichTextBoxCarret - TextPointerText: {0}", thisTP.GetTextInRun(myDirection)))

    Debug.WriteLine(String.Format("SetRichTextBoxCarret - CaretOffset Before: {0}", thisRTB.Document.ContentStart.GetOffsetToPosition(thisRTB.CaretPosition)))


    Dim myDestinationPosition = thisTP.GetPositionAtOffset(additionalOffset, myDirection)
    thisRTB.CaretPosition = myDestinationPosition

    Debug.WriteLine(String.Format("SetRichTextBoxCarret - CaretOffset After: {0}", thisRTB.Document.ContentStart.GetOffsetToPosition(thisRTB.CaretPosition)))
End Sub

我是这样称呼这个套路的(举例)

Private Sub Set1_Click(sender As Object, e As RoutedEventArgs)
    Dim myRTB = Me.rtbSearchCriteria1
    Dim myParagraph = DirectCast(myRTB.Document.Blocks(0), Paragraph)
    Dim mySpan = DirectCast(myParagraph.Inlines(0), Span)
    Dim myRun = DirectCast(mySpan.Inlines(1), Run)
    myRTB.Focus()
    SetRichTextBoxCarret(myRTB, myRun.ContentStart)
End Sub

在输出中显示以下内容 window:

SetRichTextBoxCarret - TextPointerOffset: 6
SetRichTextBoxCarret - TextPointerText: a
SetRichTextBoxCarret - CaretOffset Before: 3
SetRichTextBoxCarret - CaretOffset After: 6

但是,当我有第二个 Flowdocument(使用 space 字符而不是“a”字符)时,我会得到这个

SetRichTextBoxCarret - TextPointerOffset: 6
SetRichTextBoxCarret - TextPointerText: 
SetRichTextBoxCarret - CaretOffset Before: 3
SetRichTextBoxCarret - CaretOffset After: 4

插入符号距离“myRun.ContentStart”有 2 个符号。没有办法到达这个位置。

我已经阅读了很多关于 TextPointers 的文章,文档中说“文本 运行 元素中的每个 16 位 Unicode 字符”。据我所知,space 字符是一个 Unicode 字符,它显然位于 运行 元素内。

我是不是遗漏了什么,或者这只是一个错误?有没有办法禁用 RichTextBox 中的“插入符号位置重新定位”?

当我调用“RichTextBox.Selection.Select(myDestinationPosition, myDestinationPosition) 时,它的行为方式相同。而且 .NextInsertPosition 或 .NextContextPosition 也不可能。然后插入符号将越过 Run.ContentStart。

是的 - 我尝试做一些简单的语法高亮显示

我在这里做了一个简单的DemoApp:DemoApp

此问题的根源是 RichTextBox Class。 RichTextBox.CaretPosition 获取一个 TextPointer 并调用具有以下定义的 Selection.SetCaretToPositionvoid SetCaretToPosition(ITextPointer caretPosition, LogicalDirection direction, bool allowStopAtLineEnd, bool allowStopNearSpace);

但是在 RichTextBox Class 中,它被硬编码为不设置在 space-字符附近:Selection.SetCaretToPosition(value, value.LogicalDirection, true, falsesee here

我提到 MS 在 RichTextBox Class 中创建属性,因此我们可以在需要时配置 Selection.SetCaretToPosition 的最后 2 个参数。

解决方案: 经过漫长的“试错”之路后,我最好的解决方案是用引号替换 space。例如<Run Background="#FFB0E0E6">""</Run>.

一方面,这解决了 space-chars 的问题,另一方面,对于用户来说,写在哪里更清楚。 此外,当用户使用光标键输入 Run 或单击错误的位置时,我添加了一些代码以将插入符放在 2 个引号之间。