Search code examples
c#user-interfaceaccessibilitywinui-3winui

How do I prevent a ToolTip from obscuring the MainWindow Title Bar?


In a WinUI 3 application, the MainWindow consists of a Grid containing an Image control which takes up the majority of the space. As part of the Image control, there is a tooltip with information about the displayed image which is shown on hover. However, if the user hovers over the top-right of the image, the Window's title bar may be obscured, which is an obvious accessibility issue.

I've tried changing the placement of the tooltip to Bottom in OnOpened() if its Y-position relative to the image is less than its own height, but this isn't working, perhaps because the tooltip isn't part of the main visual hierarchy, or maybe because OnOpened is too late in the event chain.

The issue is illustrated below:

enter image description here

Current XAML for the control is:

    <Image
        x:Name="ImagePreview"
        MaxWidth="{x:Bind ImagePreviewer.MaxImageSize.Width, Mode=OneWay}"
        MaxHeight="{x:Bind ImagePreviewer.MaxImageSize.Height, Mode=OneWay}"
        Source="{x:Bind ImagePreviewer.Preview, Mode=OneWay}"
        Visibility="{x:Bind IsPreviewVisible(ImagePreviewer, Previewer.State), Mode=OneWay}">
        <ToolTipService.ToolTip>
            <ToolTip x:Name="TooltipInfo" Content="{x:Bind InfoTooltip, Mode=OneWay}" />
        </ToolTipService.ToolTip>
    </Image>

Solution

  • This ended up being more straightforward than I was making it. The XAML is very similar. I've wired up a handler for PointerMoved:

    <Image
        x:Name="ImagePreview"
        MaxWidth="{x:Bind ImagePreviewer.MaxImageSize.Width, Mode=OneWay}"
        MaxHeight="{x:Bind ImagePreviewer.MaxImageSize.Height, Mode=OneWay}"
        Source="{x:Bind ImagePreviewer.Preview, Mode=OneWay}"
        Visibility="{x:Bind IsPreviewVisible(ImagePreviewer, Previewer.State), Mode=OneWay}"
        PointerMoved="ToolTipParentControl_PointerMoved">
        <ToolTipService.ToolTip>
            <ToolTip x:Name="TooltipInfo" Content="{x:Bind InfoTooltip, Mode=OneWay}" />
        </ToolTipService.ToolTip>
    </Image>
    

    In the code behind, we can calculate the tooltip placement dependent on the current mouse position relative to the parent control:

    private void ToolTipParentControl_PointerMoved(object sender, PointerRoutedEventArgs e)
    {
        var previewControl = sender as FrameworkElement;
        if (previewControl != null)
        {
            var placement = (e.GetCurrentPoint(previewControl).Position.Y < previewControl.ActualHeight / 2) ?
                PlacementMode.Bottom : PlacementMode.Top;
    
            var toolTip = ToolTipService.GetToolTip(previewControl) as ToolTip;
            if (toolTip != null)
            {
                toolTip.Placement = placement;
            }
        }
    }
    

    The code behind is deliberately generic here, as the application has several controls with similar tooltip behaviour which can now be wired up to the same handler.