Trigger for IsChecked does not Trigger - wpf

I want to change the Icon of a ToggleButton (Content of Fluent RibbonBar) Depending on it's IsChecked Property. Now I have written the following style snippet:
<Fluent:ToggleButton.Style>
<Style BasedOn="{StaticResource RibbonButtonStyle}" TargetType="{x:Type Fluent:ToggleButton}">
<Setter Property="OverridesDefaultStyle" Value="True"/>
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Fluent:ToggleButton}}, Path=IsChecked}" Value="False">
<Setter Property="Icon" Value="{StaticResource ResourceKey=Style.Images.Pined}"/>
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Fluent:ToggleButton}}, Path=IsChecked}" Value="True">
<Setter Property="Icon" Value="{StaticResource ResourceKey=Style.Images.Unpined}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Fluent:ToggleButton.Style>
The problem is that the Trigger doesn't load the Image well. The problem is not, that IsChecked doesn't actualize itself, I've already tested this. And also I don't set the icon property anywhere else. The image resources also works fine if I use them otherwhere.
Information for Rebuild: I've put the ToggleButton into the Backstage as the DataTemplate of a RibbonListBox placed in a BackstageTabItem.

Since this is a style for togglebutton you don't need a findancestor binding - you would use self. And actually since IsChecked is a DP you can just use a trigger - eg <Trigger Property="IsChecked" Value="False">

You absolutely need to remove the Icon property from the control declaration as it will override everything a style tries to do due to precedence.
But those DataTriggers will not work, the binding will look for an ancestor, itself excluded, so you must change them as well as already pointed out by AndrewS. Only if both conditions are met you have a chance of getting this to work (there may be even additional interferences though).

Related

Data Trigger won't trigger background change

I have a TouchListBoxItem and i'm trying to change the background of per "box" when a certain data is "false"
I want to change the background color of certain items in the touchlistbox and these items has a boolean data that should trigger if it should change background
<c:TouchListBox.ItemContainerStyle>
<Style
TargetType="ListBoxItem"
BasedOn="{StaticResource noMouseOverListBoxItemStyle}">
<Setter
Property="FocusVisualStyle"
Value="{StaticResource FocusVisualStyle}" />
<Style.Triggers>
<DataTrigger
Binding="{Binding IsGroupingItem, RelativeSource={RelativeSource FindAncestor, AncestorType=ListBoxItem}}"
Value="False">
<Setter
Property="Background"
Value="{StaticResource ItemBackgroundColorBrush}" />
</DataTrigger>
</Style.Triggers>
</Style>
</c:TouchListBox.ItemContainerStyle>
Added the Style.Triggers and if the IsGroupingItem is False then it should change the Background to ItemBackgroundColorBrush.
The expression
{Binding IsGroupingItem, RelativeSource={RelativeSource AncestorType=ListBoxItem}}
expects an IsGroupingItem property in the ListBoxItem class, which it doesn't have.
Bind to a property of the actual item object by
Binding="{Binding IsGroupingItem}"
Also make sure that IsGroupingItem is actually a public property, not a field, and that it fires the PropertyChanged event of the INotifyPropertyChanged interface.

Get ValidationError in specific xaml element using a template

i have the following code snippet:
<ContentControl Height="16">
<ContentControl.Style>
<Style TargetType="ContentControl">
<Style.Triggers>
<DataTrigger Binding="{Binding ElementName=txtDistanceH, Path=(Validation.HasError)}" Value="True">
<Setter Property="Content" Value="{Binding ElementName=txtDistanceH, Path=(Validation.Errors)[0]}" />
</DataTrigger>
</Style.Triggers>
</Style>
</ContentControl.Style>
</ContentControl>
Now i want to put the style in a separate file instead of inline. However i would like to be able to specify which element it should get the Validation.Errors from, so i can use a single template for several different controls.
Is there any way to tell the template where it should get the Validation.Errors from, OTHER than binding to an element by name?
I tried setting the ContentControls DataContext to the element txtDistanceH, but then i just get a binding error saying that the property cannot be found on the root-element.
thanks for taking the time to answer my question. I've tried it and it works!
However i do have a comment and another related question.
The code i have now is:
<!-- Set content of contentcontrol to the ValidationError of a control stored in Tag, if there is one -->
<Style x:Key="ShowValidationError" TargetType="ContentControl">
<Style.Resources>
<x:Static x:Key="EmptyString" Member="System:String.Empty" />
</Style.Resources>
<Setter Property="Content" Value="{StaticResource EmptyString}" />
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Tag.(Validation.HasError)}" Value="True">
<Setter Property="Content" Value="{Binding RelativeSource={RelativeSource Self}, Path=Tag.(Validation.Errors).CurrentItem}" />
</DataTrigger>
</Style.Triggers>
</Style>
(Validation.Errors).CurrentItem is better than (Validation.Errors)[0], because the latter gives an out of range exception in the debug window when the error is resolved, see This Link for more information. The empty string ensures the control has the same size when its empty as when it has an error.
However even though it compiles and works, i still get some errors during design time. The code responsible is (Validation.HasError) and (Validation.Errors), respectively, in the above snippet.
Property 'Errors' is not attachable to elements of type 'Object'.
The property 'HasError' was not found in type 'Validation'.
Is there any way to fix / suppress these errors?
Bind the Tag property of the ContentControl to the target element using the element name binding and then update the style to use relative source self bindings to the tag to get at the validation errors.
Somewhere in Resources:
<Style x:Key=“ValidationStyle” TargetType="ContentControl">
<Style.Triggers>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Tag.(Validation.HasError)}" Value="True">
<Setter Property="Content" Value="{Binding RelativeSource={RelativeSource Self}, Path=Tag.(Validation.Errors)[0]}" />
</DataTrigger>
</Style.Triggers>
</Style>
And use it thusly:
<ContentControl Style=“{StaticResource ValidationStyle}” Tag=“{Binding ElementName=txtDistanceH}” />

Updating style on runtime in wpf

I have a style for a ItemContainer that is based on the Item being contained (With a StyleSelector). It works fine. However on runtime the property might be changed but the style isn't updated instantly.
Is there anyway for me to get it to update as soon as the changes are saved?
Use a DataTrigger and a Converter which returns the Type of an object
For example,
<Style.Triggers>
<DataTrigger Binding="{Binding Converter=ObjectToTypeConverter}"
Value="{x:Type local:Person}">
<Setter Property="ItemTemplate" Value="{Binding PersonTemplate}" />
</DataTrigger>
<DataTrigger Binding="{Binding Converter=ObjectToTypeConverter}"
Value="{x:Type local:Business}">
<Setter Property="ItemTemplate" Value="{Binding BusinessTemplate}" />
</DataTrigger>
</Style.Triggers>
Use binding. Then you will need to implement INotifyPropertyChanged. The value you are setting should be a property and at the end of the setter, raise the property changed event.
If you give an example of your XAML, I can write it out for you.

Redundant converter calls when using Triggers to determine value

I noticed that my XAML markup is wasting resources by doing conversions which it is not supposed to do. e.g. i have the following Style which acts as a switch:
<Style TargetType="{x:Type Image}">
<Style.Triggers>
<DataTrigger Binding="{Binding IsDownloaded}" Value="True">
<Setter Property="Source"
Value="{Binding Data, Converter={StaticResource ByteArrayToBitmapImageConv}}"/>
</DataTrigger>
<DataTrigger Binding="{Binding IsDownloaded}" Value="False">
<Setter Property="Source"
Value="{Binding Url, Converter={StaticResource UrlToBitmapImageConv}}"/>
</DataTrigger>
</Style.Triggers>
</Style>
Obviously this should either download an image if it has not been cached or turn the raw data into a BitmapImage. The problem is that as soon as both cases have taken place at least once both converters are called when the DataContext changes, irrespectively of the value that IsDownloaded has. So it will either display the converted image but still download it independendly in the background or it will download the image and try to convert null (the data) to a BitmapImage.
Setting the binding mode to OneTime did not help sadly.
I am looking for a clean way to avoid this as it even occurs multiple times in my application. e.g. here:
<Style TargetType="ContentControl">
<Setter Property="Content" Value="{StaticResource ContentNothingSelected}"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Source={x:Static local:App.Settings}, Path=DisplayMode_Current}"
Value="Description">
<Setter Property="Content" Value="{StaticResource DescriptionViewer}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Source={x:Static local:App.Settings}, Path=DisplayMode_Current}"
Value="WebPage">
<Setter Property="Content" Value="{StaticResource WebPageViewer}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Source={x:Static local:App.Settings}, Path=DisplayMode_Current}"
Value="Media">
<Setter Property="Content" Value="{StaticResource MediaViewer}"/>
</DataTrigger>
</Style.Triggers>
</Style>
Even if the display mode is set to Media or Description the application will navigate to the corresponding site in the background, wasting resources and throwing occasional out of place Javascript-error notifications.
I previously did most of this in code but i remodelled it to be more declarative and i would like to keep it that way, any help would be appreciated.
You could reconsider using Triggers and instead use a custom control and the Visual State Manager. Put the logic of downloading and caching and converting images in the custom control and simply set the appropriate states. It's hard to give an example without more xaml I'm afriad.

How to define a default tooltip style for all Controls

I would like to define a style with a template when there are validation errors and would display the first error message as a tooltip.
It works fine when targeting specific control like DatePicker in the following xaml.
<Style TargetType="{x:Type ToolKit:DatePicker}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
I cannot get it to work for Control though, i.e. the following doesn't give any tooltip
<Style TargetType="{x:Type ToolKit:Control}">
<Style.Triggers>
<Trigger Property="Validation.HasError" Value="true">
<Setter Property="ToolTip"
Value="{Binding RelativeSource={RelativeSource Self},
Path=(Validation.Errors)[0].ErrorContent}"/>
</Trigger>
</Style.Triggers>
</Style>
Any idea?
I would recommend you create a Behavior for this one.
Every control is unique in itself and this is not a reliable way to attach a specific behavior to all controls. And in fact, you may end up setting this property on unwanted controls that don't even require validation.
Behaviors are clean and you can assign them to selected controls only. Attached Properties or a Master Behavior can be used to assign Behaviors to child controls.
Here is a CodeProject article from Josh Smith to get you started on Behaviors.

Resources