Search code examples
c#.netwpfflowdocument

How can I determine coordinates of a Hyperlink in WPF


I have WPF window with a FlowDocument with several hyperlinks in it:

<FlowDocumentScrollViewer>
  <FlowDocument TextAlignment="Left" >
     <Paragraph>Some text here
       <Hyperlink Click="Hyperlink_Click">open form</Hyperlink>
     </Paragraph>           
  </FlowDocument>
</FlowDocumentScrollViewer>

In the C# code I handle Click event to create and show a new WPF Window:

private void Hyperlink_Click(object sender, RoutedEventArgs e)
{
    if (sender is Hyperlink)
    {
        var wnd = new SomeWindow();
        //wnd.Left = ???
        //wnd.Top = ???
        wnd.Show();
    }
}

I need this window to appear next to hyperlink's actual position. So I assume it requires assigning values to the window's Left and Top properties. But I have no idea how to obtain hyperlink position.


Solution

  • You can use ContentStart or ContentEnd to get a TextPointer for the start or end of the hyperlink and then call GetCharacterRect to get the bounding box relative to the FlowDocumentScrollViewer. If you get a reference to the FlowDocumentScrollViewer, you can use PointToScreen to convert it to screen coordinates.

    private void Hyperlink_Click(object sender, RoutedEventArgs e)
    {
        var hyperlink = sender as Hyperlink;
        if (hyperlink != null)
        {
            var rect = hyperlink.ContentStart.GetCharacterRect(
                LogicalDirection.Forward);
            var viewer = FindAncestor(hyperlink);
            if (viewer != null)
            {
                var screenLocation = viewer.PointToScreen(rect.Location);
    
                var wnd = new Window();
                wnd.WindowStartupLocation = WindowStartupLocation.Manual;
                wnd.Top = screenLocation.Y;
                wnd.Left = screenLocation.X;
                wnd.Show();
            }
        }
    }
    
    private static FrameworkElement FindAncestor(object element)
    {
        while(element is FrameworkContentElement)
        {
            element = ((FrameworkContentElement)element).Parent;
        }
        return element as FrameworkElement;
    }