I have a shared ToggleButton
style defined that shows one image when IsChecked
is true
, and a different image when IsChecked
is false
. Binding IsChecked
to a boolean property on the ViewModel works fine to show the desired image, so I know that my style is being applied and the DataTrigger
bound to IsChecked
works.
I would like to change the ToolTip
text when IsChecked
changes, but I'm having quite a bit of trouble getting that to work. Ideally I'd like the ToolTip
to say something like "Turn On" when unchecked and "Turn Off" when checked.
The issue is that the ToolTip
is not connected to the ToggleButton
in the visual tree, so I need another way to find the ToggleButton.IsChecked
. I'm trying to do this entirely in a style, because I've got quite a lot of these buttons scattered around, so no changes to the VMs are permitted.
Ideally the solution would allow me to use the ToggleButton.Tag
to store the text, and apply a Converter
and StringFormat
(or ContentStringFormat
). I've already overridden the ContentTemplate
and replaced it with a barebones ContentPresenter
to get rid of the typical ToggleButton
effects, so it can be changed further if necessary. (And maybe I've removed something important for this to work? idk.)
Here is my style that works to change image, but the ToolTip
never changes.
<!--Toggle button where content is a switch image that's "On" when checked and "Off" when anything else-->
<Style TargetType="ToggleButton" x:Key="OnOffToggleButton" x:Shared="False">
<!-- this worked to format the initial tooltip contents, but no way to bind.
<Style.Resources>
<Style TargetType="ToolTip">
<Setter Property="ContentStringFormat" Value="Turn on {0}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding ???}" Value="True">
<Setter Property="ContentStringFormat" Value="Turn off {0}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Style.Resources> -->
<Setter Property="Content" Value="{StaticResource ToggleOffImage}"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<ContentPresenter>
<ContentPresenter.ToolTip>
<TextBlock>
<TextBlock.Style>
<Style TargetType="TextBlock">
<!-- this works -->
<Setter Property="Text" Value="{Binding Tag, RelativeSource={RelativeSource Self}, StringFormat=Turn on {0}}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked}" Value="True">
<!-- trigger has no effect -->
<Setter Property="Text" Value="{Binding Tag, RelativeSource={RelativeSource Self}, StringFormat=Turn off {0}}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</ContentPresenter.ToolTip>
</ContentPresenter>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, RelativeSource={RelativeSource Self}}" Value="True">
<Setter Property="Content" Value="{StaticResource ToggleOnImage}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="ToolTip">
<Setter.Value>
<!-- another thing that does not work -->
<TextBlock Text="{Binding Tag, RelativeSource={RelativeSource Self}, StringFormat=Turn off {0}}" />
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
The buttons themselves are defined similar to this:
<ToggleButton Style="{StaticResource OnOffToggleButton}"
IsChecked="{Binding IsPoweredOn, Mode=OneWay}"
Command="{Binding TogglePowerCommand}"
Tag="Device Power"/>
CodePudding user response:
The StringFormat
does not work with a ToolTip
, as it can contain arbitrary content of type object
, not just plain text. However, the ToolTip
exposes a property ContentStringFormat
that you can use.
Gets or sets a composite
string
that specifies how to format theContent
property if it is displayed as astring
.
Since a ToolTip
is hosted in a separate popup window, it is not part of the same visual tree as the ToggleButton
, but you can refer to it using the PlacementTarget
of the ToolTip
.
Gets or sets the
UIElement
relative to which theToolTip
is positioned when it opens.
<Style TargetType="ToggleButton" x:Key="OnOffToggleButton" x:Shared="False">
<Setter Property="Content" Value="{StaticResource ToggleOffImage}"/>
<Setter Property="FocusVisualStyle" Value="{x:Null}"/>
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip Content="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource Self}}"
ContentStringFormat="Turn on {0}">
</ToolTip>
</Setter.Value>
</Setter>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="ToggleButton">
<ContentPresenter/>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<DataTrigger Binding="{Binding IsChecked, RelativeSource={RelativeSource Self}}" Value="True">
<Setter Property="Content" Value="{StaticResource ToggleOnImage}"/>
<Setter Property="Background" Value="Transparent"/>
<Setter Property="ToolTip">
<Setter.Value>
<ToolTip Content="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource Self}}"
ContentStringFormat="Turn off {0}">
</ToolTip>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
A converter can still be applies in the Content
binding as usual.
<ToolTip Content="{Binding PlacementTarget.Tag, RelativeSource={RelativeSource Self}, Converter={StaticResource YourConverter}}"
ContentStringFormat="Turn on {0}">