Home > Back-end >  WPF - ToolTip in TextBlock that use the Text Property from the TextBlock
WPF - ToolTip in TextBlock that use the Text Property from the TextBlock

Time:03-08

We have many TextBlocks in our application and many of them must have ToolTips.
Often the ToolTip should display the same as the TextBlock.Text property.
Also the ToolTip should not be displayed if the TextBlock.Text = "" or null.

So we have this solution in many many places:

<TextBlock Text="{Binding SomeTextProperty}">
    <TextBlock.ToolTip>
        <ToolTip Visibility="{Binding SomeTextProperty}, Converter={StaticResource StringToVisibilityConverter}">
            <TextBlock Text="{Binding SomeTextProperty}" />
    </TextBlock.ToolTip>
</TextBlock>

Notice:

  • I have to specify the SomeTextProperty three times on each TextBlock that need a TooLTip with this functionality.
    This seems very redundant!
  • The ToolTip.Content is a TextBlock itself.
    This is because I need to have a Style on the TextBlock.
    I have omitted that style to keep this post as simple as possible.

So I have tried to invent a Style for TextBlocks that use Bindings with RelativeSource to get the TextBlock.Text property for the ToolTip. I came up with this solution:

MainWindow (Just copy-paste more or less)

<Window x:Class="Main.Views.ToolTips"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:Main.Views"
        xmlns:converters="clr-namespace:Main.Converters"
        mc:Ignorable="d"
        Title="ToolTips" Height="450" Width="800">

    <Window.Resources>
        <Style TargetType="TextBlock" x:Key="TextBlockWithToolTipStyle">
            <Style.Resources>
                <converters:StringToVisibilityConverter x:Key="StringToVisibilityConverter" />
            </Style.Resources>
            <Setter Property="ToolTip">
                <Setter.Value>
                    <ToolTip Visibility="{Binding Text, RelativeSource={RelativeSource AncestorType={x:Type TextBlock}}, Converter={StaticResource StringToVisibilityConverter}}">
                        <ToolTip.Content>
                            <TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType={x:Type TextBlock}}}" />
                        </ToolTip.Content>
                    </ToolTip>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>

    <StackPanel Orientation="Vertical" HorizontalAlignment="Center" VerticalAlignment="Center">

        <TextBlock Text="This should display a tooltip" />
        <TextBlock Text="asdf" 
                   Background="Gray"
                   Style="{StaticResource TextBlockWithToolTipStyle}" />

        <Separator Height="50" Visibility="Hidden" />

        <TextBlock Text="This should not display a tooltip" />
        <TextBlock Text="" 
                   Background="LightGray"
                   Style="{StaticResource TextBlockWithToolTipStyle}" />
    </StackPanel>
</Window>

StringToVisibilityConverter

public class StringToVisibilityConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var input = value is null ? string.Empty : value.ToString();
        return string.IsNullOrWhiteSpace(input) ? Visibility.Collapsed : Visibility.Visible;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

This, of course, don't work!
I put a breakpoint in the StringToVisibilityConverter and that breakpoint is not hit when I hover both TextBlocks in the Window.

In VS's XAML Binding Failures view I see two binding errors that regards ToolTip.Visibility and TextBlock.Text:

  • ToolTip.Visibility: Cannot find source: RelativeSource FindAncestor, AncestorType='System.Windows.Controls.TextBlock', AncestorLevel='1'
  • TextBlock.Text: Cannot find source: RelativeSource FindAncestor, AncestorType='System.Windows.Controls.TextBlock', AncestorLevel='1'

Is this possible to solve?
If so, how?

/BR, Steffe

CodePudding user response:

The ToolTip is not part of the visual tree. That's why your Binding.RelativeSource does not resolve.

To reference the element that is decorated by the ToolTip you must reference the ToolTip.PlacementTarget property:

<TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=ToolTip}, Path=PlacementTarget.Text}" />

Furthermore, you should not try to toggle the ToolTip.Visibility. It won't work. Instead use a Trigger to set the ToolTip.

The correct solution would be as followed:

<Style TargetType="TextBlock"
       x:Key="TextBlockWithToolTipStyle">
  <Style.Resources>
    <ToolTip x:Key="ToolTip">
      <TextBlock Text="{Binding RelativeSource={RelativeSource AncestorType=ToolTip}, Path=PlacementTarget.Text}" />
    </ToolTip>
  </Style.Resources>

  <Setter Property="ToolTip"
          Value="{StaticResource ToolTip}" />

  <Style.Triggers>
    <Trigger Property="Text" Value="">
      <Setter Property="ToolTip" Value="{x:Null}" />
    </Trigger>
  </Style.Triggers>
</Style>
  • Related