ComponentOne RichTextBox for WPF
Working with WPF RichTextBox / Hit-Testing
In This Topic
    Hit-Testing
    In This Topic

    The C1RichTextBox supports hyperlinks, which provide a standard mechanism for implementing user interactivity. In some cases, you may want to go beyond that and provide additional, custom mouse interactions. For example, you may want to apply some custom formatting or show a context menu when the user clicks an element.

    To enable these scenarios, the C1RichTextBox exposes ElementMouse* events and a C1RichTextBox.GetPositionFromPoint method.

    If all you need to know is the element that triggered the mouse event, you can get it from the source parameter in the event handler. If you need more detailed information (the specific word that was clicked within the element for example), then you need the C1RichTextBox.GetPositionFromPoint method. C1RichTextBox.GetPositionFromPoint takes a point in client coordinates and returns a C1TextPosition object that expresses the position in document coordinates.

    The C1TextPosition object has two main properties: Element and Offset. The Element property represents an element within the document; Offset is a character index (if the element is a C1Run) or the index of the child element at the given point.

    In the following section learn how to implement Hit Testing in RichTextBox .NET 4.5.2 and .NET 5 versions.

     For example, the code below creates a C1RichTextBox and attaches a handler to the C1RichTextBox.ElementMouseLeftButtonDown event:
    Visual Basic
    Copy Code
    Public Sub New()
        ' Default initialization
        InitializeComponent()
    
        ' Create a C1RichTextBox and add it to the page
        _rtb = New C1RichTextBox()
        LayoutRoot.Children.Add(_rtb)
    
        ' Attach event handler
        Add Handler _rtb.ElementMouseLeftButtonDown AddressOf rtb_ElementMouseLeftButtonDown
    End Sub
    
    C#
    Copy Code
    public MainPage()
    {
      // Default initialization
      InitializeComponent();
    
      // Create a C1RichTextBox and add it to the page
      _rtb = new C1RichTextBox();
      LayoutRoot.Children.Add(_rtb);
    
      // Attach event handler
      _rtb.ElementMouseLeftButtonDown += rtb_ElementMouseLeftButtonDown;
    }
    

    The event handler below toggles the C1TextElement.FontWeight property for the entire element that was clicked. This could be a word, a sentence, or a whole paragraph:

    Visual Basic
    Copy Code
    Private Sub _rtb_ElementMouseLeftButtonDown(sender As Object, e As MouseButtonEventArgs)
        If Keyboard.Modifiers <> 0 Then
            Dim run = TryCast(sender, C1Run)
            If run IsNot Nothing Then
                run.FontWeight = If(run.FontWeight = FontWeights.Bold, FontWeights.Normal, FontWeights.Bold)
            End If
        End If
    End Sub
    
    C#
    Copy Code
    void _rtb_ElementMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
      if (Keyboard.Modifiers != 0)
      {
        var run = sender as C1Run;
        if (run != null)
        {
          run.FontWeight = run.FontWeight == FontWeights.Bold
            ? FontWeights.Normal
            : FontWeights.Bold;
        }
      }
    }
    

    The code gets the element that was clicked by casting the sender parameter to a C1Run object.

    If you wanted to toggle the C1TextElement.FontWeight value of a single word instead, then you would need to determine which character was clicked and expand the selection to the whole word. This is where the C1RichTextBox.GetPositionFromPoint method becomes necessary. Here is a revised version of the event handler that accomplishes that:

    Visual Basic
    Copy Code
    Private Sub _rtb_ElementMouseLeftButtonDown(sender As Object, e As MouseButtonEventArgs)
        If Keyboard.Modifiers <> 0 Then
            ' Get position in control coordinates
            Dim pt = e.GetPosition(_rtb)
    
            ' Get text pointer at position
            Dim pointer = _rtb.GetPositionFromPoint(pt)
    
            ' Check that the pointer is pointing to a C1Run
            Dim run = TryCast(pointer.Element, C1Run)
            If run IsNot Nothing Then
                ' Get the word within the C1Run
                Dim text = run.Text
                Dim start = pointer.Offset
                Dim [end] = pointer.Offset
                While start > 0 AndAlso Char.IsLetterOrDigit(text, start - 1)
                    start -= 1
                End While
                While [end] < text.Length - 1 AndAlso Char.IsLetterOrDigit(text, [end] + 1)
                    [end] += 1
                End While
    
                ' Toggle the bold property for the run that was clicked
                Dim word = New C1TextRange(pointer.Element, start, [end] - start + 1)
                word.FontWeight = If(word.FontWeight.HasValue AndAlso word.FontWeight.Value = FontWeights.Bold, FontWeights.Normal, FontWeights.Bold)
            End If
        End If
    End Sub
    
    C#
    Copy Code
    void _rtb_ElementMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
      if (Keyboard.Modifiers != 0)
      {
        // Get position in control coordinates
        var pt = e.GetPosition(_rtb);
    
        // Get text pointer at position
        var pointer = _rtb.GetPositionFromPoint(pt);
    
        // Check that the pointer is pointing to a C1Run
        var run = pointer.Element as C1Run;
        if (run != null)
        {
          // Get the word within the C1Run
          var text = run.Text;
          var start = pointer.Offset;
          var end = pointer.Offset;
          while (start > 0 && char.IsLetterOrDigit(text, start - 1))
            start--;
          while (end < text.Length - 1 && char.IsLetterOrDigit(text, end + 1))
            end++;
    
          // Toggle the bold property for the run that was clicked
          var word = new C1TextRange(pointer.Element, start, end - start + 1);
          word.FontWeight =
            word.FontWeight.HasValue && word.FontWeight.Value == FontWeights.Bold
              ? FontWeights.Normal
              : FontWeights.Bold;
        }
      }
    }
    

    Notice that the C1TextElement.FontWeight property returns a nullable value. If the range contains a mix of values for this attribute, the property returns null. The code used to toggle the FontWeight property is the same we used earlier when implementing the formatting toolbar.

    The GetPositionFromPoint allows you to get a C1TextPosition object from a point on the screen. The GetRectFromPosition method performs the reverse operation, returning a Rect that represents the screen position of a C1TextPosition object. This is useful in situations where you want to present a UI element near a specific portion of a document.

    For example, the code below creates a C1RichTextBox and attaches a handler to the C1RichTextBox.ElementMouseLeftButtonDown event:

    C#
    Copy Code
    InitializeComponent();
    richTextbox.ElementMouseLeftButtonDown += RichTextboxElementMouseLeftButtonDown;
                
    richTextbox.Text = " Ctrl + LeftMouseClick on the word to make it bold.\r\r" +
        "WPF RichTextBox and Silverlight is the most complete rich text editor available for WPF or Silverlight." +
        " Load, edit, and save formatted text as HTML or RTF documents." +
        " The C1RichTextBox control provides rich formatting, automatic line wrapping, HTML and RTF import/export, table support, images, annotations, and more.";
    

     

    The event handler below toggles the C1TextElement.FontWeight property for the entire element that was clicked. This could be a word, a sentence, or a whole paragraph.

    The code gets the element that was clicked by casting the sender parameter to a C1Run object.

    If you wanted to toggle the C1TextElement.FontWeight value of a single word instead, then you would need to determine which character was clicked and expand the selection to the whole word. This is where the C1RichTextBox.GetPositionFromPoint method becomes necessary. Here is a revised version of the event handler that accomplishes that:

    C#
    Copy Code
    private void RichTextboxElementMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        if (Keyboard.Modifiers != 0)
        {
            // Get position in control coordinates
            var pt = e.GetPosition(richTextbox);
    
            // Get text pointer at position
            var pointer = richTextbox.GetPositionFromPoint(pt);
    
            // Check that the pointer is pointing to a C1Run
            var run = pointer.Element as C1Run;
            if (run != null)
            {
                // Get the word within the C1Run
                var text = run.Text;
                var start = pointer.Offset;
                var end = pointer.Offset;
                while (start > 0 && char.IsLetterOrDigit(text, start - 1))
                    start--;
                while (end < text.Length - 1 && char.IsLetterOrDigit(text, end + 1))
                    end++;
    
                // Toggle the bold property for the run that was clicked
                var word = new C1TextRange(pointer.Element, start, end - start + 1);
                word.FontWeight =
                  word.FontWeight.HasValue && word.FontWeight.Value == FontWeights.Bold
                    ? FontWeights.Normal
                    : FontWeights.Bold;
            }
        }
    

    Notice that the C1TextElement.FontWeight property returns a nullable value. If the range contains a mix of values for this attribute, the property returns null. The code used to toggle the FontWeight property is the same we used earlier when implementing the formatting toolbar.

    The GetPositionFromPoint allows you to get a C1TextPosition object from a point on the screen. The GetRectFromPosition method performs the reverse operation, returning a Rect that represents the screen position of a C1TextPosition object. This is useful in situations where you want to present a UI element near a specific portion of a document.