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>