WPF Tooltip positioning

前端 未结 1 1370
旧时难觅i
旧时难觅i 2021-01-05 05:34

I have a style on my WPF Tooltip which basically makes it look like a speech bubble. The pointy part of the bubble stops aligning properly when the control is to the right o

相关标签:
1条回答
  • 2021-01-05 06:05

    Standard WPF Windows 7 style

    Standard WPF Windows 7 style

    The standard WPF tooltip positions at the mouse pointer, which look perfect in my opinion.

    The picture below illustrates your problem

    Styled ToolTip with no code-behind

    If you really want to do what you are asking, it is possible: you need code-behind on the style to calculate the horizontal adjustment, put this into ToolTip.Tag, and bind the pointy part border to the calculated adjustment:

    Revised ToolTip style -

        <Style TargetType="{x:Type ToolTip}">
            <!-- As before, except Margin of the pointy part is now bound to calculated ToolTip.Tag -->
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate TargetType="{x:Type ToolTip}">
                        <Grid x:Name="Grid">
                            <Grid.RowDefinitions>
                                <RowDefinition Height="20" />
                                <RowDefinition Height="*" />
                            </Grid.RowDefinitions>
                            <Rectangle Fill="#fff" Stroke="#FF000000" RadiusX="4" RadiusY="4" Grid.Row="1" />
                            <Path Fill="#fff" Stretch="Fill" Stroke="#FF000000" HorizontalAlignment="Left" 
                                  Margin="{TemplateBinding Tag}" Width="20" Grid.Row="0"
                        Data="M 0,21 L 10,0 20,21" />
                            <ContentPresenter Margin="8" Grid.Row="1" />
                        </Grid>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
            <Setter Property="Placement" Value="Bottom" />
            <!-- Event to adjust horizontal position of the pointy part -->
            <EventSetter Event="Opened" Handler="ToolTipOpenedHandler" />
        </Style>
    

    Code-behind

    private void ToolTipOpenedHandler(object sender, RoutedEventArgs e)
    {
        ToolTip toolTip = (ToolTip) sender;
        UIElement target = toolTip.PlacementTarget;
        Point adjust = target.TranslatePoint(new Point(8, 0), toolTip);
        toolTip.Tag = new Thickness(adjust.X, 0, 0, -1.5);
    }
    

    This answers your question as asked,

    Styled ToolTip - Top Right

    but is not sufficient when tooltip is near the bottom of the screen:

    Styled ToolTip - Bottom Right

    To fix this you can amend the code-behind to detect that the tooltip is above the target and set the tooltip Position to Top, and a Property Trigger style the tooltip with the pointy part below the rectangle -

    Complete XAML (includes wide, narrow and multi-line tooptips)

    <Window x:Class="WpfToolTip.MainWindow"
            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    
            Title="MainWindow" Height="250" Width="250">
        <Window.Resources>
            <Style TargetType="{x:Type ToolTip}">
                <!-- As before, except Margin of the pointy part is now bound to calculated ToolTip.Tag -->
                <Setter Property="Template">
                    <Setter.Value>
                        <ControlTemplate TargetType="{x:Type ToolTip}">
                            <Grid x:Name="Grid">
                                <Grid.RowDefinitions>
                                    <RowDefinition Height="20" />
                                    <RowDefinition Height="*" />
                                </Grid.RowDefinitions>
                                <Rectangle MinWidth="40" Fill="#fff" Stroke="#FF000000" RadiusX="4" RadiusY="4" Grid.Row="1" />
                                <Path Fill="#fff" Stretch="Fill" Stroke="#FF000000" HorizontalAlignment="Left" 
                                      Margin="{TemplateBinding Tag}" Width="20" Grid.Row="0"
                            Data="M 0,21 L 10,0 20,21" />
                                <ContentPresenter Margin="8" Grid.Row="1" />
                            </Grid>
                        </ControlTemplate>
                    </Setter.Value>
                </Setter>
                <Setter Property="Placement" Value="Bottom" />
                <!-- Event to adjust horizontal position of the pointy part -->
                <EventSetter Event="Opened" Handler="ToolTipOpenedHandler" />
                <Style.Triggers>
                    <Trigger Property="Placement" Value="Top">
                        <!-- When placement is Top, place the pointy part below the rectangle part -->
                        <Setter Property="Template">
                            <Setter.Value>
                                <ControlTemplate TargetType="{x:Type ToolTip}">
                                    <Grid>
                                        <Grid.RowDefinitions>
                                            <RowDefinition Height="*" />
                                            <RowDefinition Height="20" />
                                        </Grid.RowDefinitions>
                                        <Rectangle MinWidth="40" Fill="#fff" Stroke="#FF000000" RadiusX="4" RadiusY="4" Grid.Row="0" />
                                        <Path Fill="#fff" Stretch="None" Stroke="#FF000000" HorizontalAlignment="Left" Width="20" Grid.Row="1" 
                                            Data="M 0,0 L 10,20 20,0" Margin="{TemplateBinding Tag}" />
                                        <ContentPresenter Margin="8" Grid.Row="0" />
                                    </Grid>
                                </ControlTemplate>
                            </Setter.Value>
                        </Setter>
                    </Trigger>
                </Style.Triggers>
            </Style>
        </Window.Resources>
        <Grid>
            <TextBlock VerticalAlignment="Top" HorizontalAlignment="Left" Background="Aqua" ToolTipService.ToolTip="ToolTip for TopLeft - MMMMMMMMMWWWWWWWWWW">TopLeft</TextBlock>
            <TextBlock VerticalAlignment="Top" HorizontalAlignment="Right" Background="Aqua" ToolTipService.ToolTip="ToolTip for TopRight - MMMMMMMMMWWWWWWWWWW">TopRight</TextBlock>
            <TextBlock VerticalAlignment="Center" HorizontalAlignment="Left" Background="Aqua" ToolTipService.ToolTip="i">CenterLeft</TextBlock>
            <TextBlock VerticalAlignment="Center" HorizontalAlignment="Right" Background="Aqua" ToolTipService.ToolTip="i">CenterRight</TextBlock>
            <TextBlock VerticalAlignment="Bottom" HorizontalAlignment="Left" Background="Aqua" Text="BottomLeft">
                <TextBlock.ToolTip>
                    <TextBlock>Multi-line ToolTip for Bottomleft - MMMMMMMMMWWWWWWWWWW<LineBreak/>x<LineBreak/>y<LineBreak/>z</TextBlock>
                </TextBlock.ToolTip>
            </TextBlock>
            <TextBlock VerticalAlignment="Bottom" HorizontalAlignment="Right" Background="Aqua" ToolTipService.ToolTip="ToolTip for BottomRight - MMMMMMMMMWWWWWWWWWW">BottomRight</TextBlock>
        </Grid>
    </Window>
    

    Code-behind

    private void ToolTipOpenedHandler(object sender, RoutedEventArgs e)
    {
        ToolTip toolTip = (ToolTip)sender;
        UIElement target = toolTip.PlacementTarget;
        Point adjust = target.TranslatePoint(new Point(8, 0), toolTip);
        if (adjust.Y > 0)
        {
            toolTip.Placement = PlacementMode.Top;
        }
        toolTip.Tag = new Thickness(adjust.X, -1.5, 0, -1.5);
    }
    

    Final result

    Final result - Top left

    Final fixed result - Bottom right

    The pointy part now adjusts horizontally when tooltip is near the right of the screen, and vertically when tooltip is near the bottom of the screen.

    0 讨论(0)
提交回复
热议问题