RichTextBox 双击选择由 "i.a." 等句点分隔的单词

RichTextBox double click selecting words separated by periods like "i.a."

我的目标是双击“i.a”的整个部分。像这样:

但是 WPF 的默认设置是单独选择字符串的每个部分:

样本:

<RichTextBox>
    <FlowDocument>
        <Paragraph>Reason for contact: i.a.</Paragraph>
    </FlowDocument>
</RichTextBox>

从用户的角度来看,文本中可能会或可能不会涉及格式以突出显示关键字,但为“i.a”简化此操作。我认为不会涉及任何格式。可以格式化整个“word”,但不能像“i”是红色的,“a”是蓝色的。

到目前为止我发现了什么

我尝试使用 SpellCheck.CustomDictionaries,选词不受这些影响。

TextEditorMouse class 负责在 SetCaretPositionOnMouseEvent 中启动选择,调用 IsAtCaretUnitBoundary 可能会检查单词边界,但我无法理解 TextPointer 的魔力。对我来说,似乎没有办法解决这个问题,因为它受到私有和内部方法的保护。

我怀疑 CaretPositionTextPointer 通过查看对 System.Windows.Documents.TextPointerBase.GetWordRange(System.Windows.Documents.ITextPointer thisPosition, System.Windows.Documents.LogicalDirection direction)4:

的调用来向前和向后涉及
System.Windows.Documents.TextPointer selectionStart = rtb.CaretPosition;
while (selectionStart.GetPointerContext(LogicalDirection.Backward) != TextPointerContext.Text)
{
    selectionStart = selectionStart.GetNextContextPosition(LogicalDirection.Backward);
}
System.Windows.Documents.TextPointer selectionEnd = rtb.CaretPosition;
while (selectionEnd.GetPointerContext(LogicalDirection.Forward) != TextPointerContext.Text)
{
    selectionEnd = selectionEnd.GetNextContextPosition(LogicalDirection.Forward);
}

如果假设分析文本没有格式可以考虑使用下面的代码:

private void Rtb_PreviewMouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
    var tr = GetWordByDelimiters();

    // Color the selected text 
    tr.ApplyPropertyValue(TextElement.BackgroundProperty, Brushes.Yellow);
    rtb.Focus();

   e.Handled = true;
}

public TextRange GetWordByDelimiters()
{
    TextPointer current = rtb.CaretPosition;

    // \s matches any whitespace character (equivalent to [\r\n\t\f\v ])
    string pattern = @"[^\s]+"; // Delimiters 

    // The run content before the current caret position
    string backward = current.GetTextInRun(LogicalDirection.Backward);

    // Scan text before caret
    Match match = Regex.Match(backward, pattern, RegexOptions.RightToLeft);

    TextPointer start = current;
    if (match.Success && match.Index + match.Value.Length == backward.Length)
    {
        start = start.GetPositionAtOffset(-backward.Length + match.Index);
    }

    // The run content after the current caret position
    string forward = current.GetTextInRun(LogicalDirection.Forward);

    // Scan text after caret
    match = Regex.Match(forward, pattern);

    TextPointer end = current;
    if (match.Success && match.Index == 0)
    {
        end = end.GetPositionAtOffset(match.Value.Length, LogicalDirection.Forward);
    }
    return new TextRange(start, end);
}     

.xaml 的片段:

  <RichTextBox x:Name="rtb" AllowDrop="True" VerticalScrollBarVisibility="Auto" 
          PreviewMouseDoubleClick="Rtb_PreviewMouseDoubleClick">
      <FlowDocument>
            ...
      </FlowDocument>
  </RichTextBox>

如果有必要修改 pattern 以使用任何分隔符集。