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

Time:11-18

My goal is to double click the entire part of "i.a." like this:

like this

However the default for WPF is it selects each part of the string individually:

selects each part of the string individually

Sample:

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

From the users perspective, there may or may not be formatting involved in text to highlight key words, but to simplify this for "i.a.". I don't think any formatting will be involved. The entire "word" may be formatted, but not like the "i" is in red and the "a" is in blue.

What I've found so far

I've attempted to use SpellCheck.CustomDictionaries, word selection is unaffected by these.

The TextEditorMouse class is responsible starting the selection in SetCaretPositionOnMouseEvent that calls into IsAtCaretUnitBoundary to possibly check word boundaries, but I'm unable to comprehend TextPointer's magic. To me, there doesn't appear to be a way into this as it is guarded off by private and internal methods.

I suspect TextPointer of CaretPosition is involved by going forwards and backwards by looking at the calls into 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);
}

CodePudding user response:

If suppose that the analyzing text does not have formatting consider to use the following code:

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

Fragments of the .xaml:

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

If it's necessary modify the pattern to use any set of delimiters.

  • Related