Search code examples
c#wpfblockrichtextboxvisible

WPF RichTextBox: Find the first block/paragraph that is currently visible in the RichTextBox


Situation

I have a 95% working application (WPF Application / .Net 4.8) that does the following:

  • On the left side a WPF RichTextBox
  • On the right side a WebBrowser Control
  • Left side: User writes MarkDown text in the RichTextBox
  • Right side: User sees HTML text in realtime

Goal

  • When the user scrolls the RichTextBox (left side), THE FIRST VISIBLE block/paragraph on the left side should also be the first visible block/paragraph on the right side.

Problem

  • The goal can NOT be reached by synchronizing the ScrollViewers. (FontSizes/Styles are too different)
  • The goal can, but SHOULD NOT be reached by using the RichTextBox.CaretPosition (MouseClick => CaretPosition => GetTextUnderCaret => ScrollHTML => not user friendly enough)
  • The System.Windows.Documents.Block class is not a FrameworkElement. That means, I can NOT do this:
string text = null;
foreach (var block in RichTextBox.Document.Blocks)
{
      //This does not work. Block is not a FrameworkElement...shit...
      var blockIsVisible = IsUserVisible(block, RichTextBox);
      if (blockIsVisible)
      {
          var text = new TextRange(block.ContentStart, block.ContentEnd).Text;
          break;
      }
}

if (!string.IsNullOrWhiteSpace(text))
{
    //Scroll to given text in HTML with JQuery...
}

private bool IsUserVisible(FrameworkElement element, FrameworkElement container)
{
    if (!element.IsVisible) return false;
   
    Rect bounds = element.TransformToAncestor(container).TransformBounds(new Rect(0.0, 0.0, element.ActualWidth, element.ActualHeight));
    Rect rect = new Rect(0.0, 0.0, container.ActualWidth, container.ActualHeight);
    return rect.Contains(bounds.TopLeft) || rect.Contains(bounds.BottomRight);
}

Finally my question

How can I determine the first visible line/block/paragraph in WPF RichTextBox?


Solution

  • This code gets the first visible paragraph (not necessarily the first visible line!)

        TextPointer tp = richTextBox.GetPositionFromPoint(new Point(0, 0), true);
        FirstParagraphLine = ((Run)tp.Paragraph.Inlines.FirstInline).Text;