Trigger when condition is not equal to - wpf

I need a Style under WPF that sets several properties when multiple conditions are fullfilled. However, one of my conditions is of type Not Equal To. How should I change the below Style so that the condition would become Not Equal To? Can it be even achieved without IValueConverter?
<Style>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<!--<Condition 1 here.../>-->
<!--<Condition 2 here.../>-->
<Condition Binding="{Binding Path=id}" Value="3"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="Red"/>
<Setter Property="Foreground" Value="Black"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
I would need the below but this of course don't work since Triggers only support Equal operator.
<Style>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<!--<Condition 1 here.../>-->
<!--<Condition 2 here.../>-->
<Condition Binding="{Binding Path=id}" Value<>"3"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="Red"/>
<Setter Property="Foreground" Value="Black"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>

You need an IValueConverter and some extra markup for this:
<Style>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<!--<Condition 1 here.../>-->
<!--<Condition 2 here.../>-->
<Condition>
<Condition.Binding>
<Binding Path="id" Converter="{StaticResource ValueToEqualsParameterConverter}">
<Binding.ConverterParameter>
<System:Int32>3</System:Int32>
</Binding.ConverterParameter>
</Binding>
</Condition.Binding>
<Condition.Value>
<System:Boolean>False</System:Boolean>
</Condition.Value>
</Condition>
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="Red" />
<Setter Property="Foreground" Value="Black" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
And the converter:
public class ValueToEqualsParameterConverter : IValueConverter {
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
return value == parameter;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
return null;
}
}

Another option is to define default value as setter in style and then implement data trigger. In following code, the background value is always red except when value is 3
<Style>
<Setter Property="Background" Value="Red"/>
<Setter Property="Foreground" Value="Black"/>
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<!--<Condition 1 here.../>-->
<!--<Condition 2 here.../>-->
<Condition Binding="{Binding Path=id}" Value="3"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="DefaultColor"/>
<Setter Property="Foreground" Value="DefaultColor2"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>

Related

Data trigger check value of dynamic resource

I have a WPF application and the content of should be displayed in German and English. Therefore, I created two seperate resource dictionaries which contain string snippets in each language. It is possible to switch between the languages while the application is running.
At this point I got stuck at a problem. There are some settings the user can make. If the setting was completed successfully a message shows up. The text of the message is taken from the resource dictionary. Based on a success or error message the text is displayed green or red.
<TextBlock Text="{Binding UpdateTaxPercentageSettingsMessage}" FontWeight="Bold">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding UpdateTaxPercentageSettingsMessage}" Value="{DynamicResource tax_percentage_update_went_wrong}">
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
<DataTrigger Binding="{Binding UpdateTaxPercentageSettingsMessage}" Value="{StaticResource active_tax_law_update_went_wrong}">
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
<DataTrigger Binding="{Binding UpdateTaxPercentageSettingsMessage}" Value="{StaticResource tax_percentage_was_updated_successfully}">
<Setter Property="Foreground" Value="Green"/>
</DataTrigger>
<DataTrigger Binding="{Binding UpdateTaxPercentageSettingsMessage}" Value="{StaticResource active_tax_law_was_updated_successfully}">
<Setter Property="Foreground" Value="Green"/>
</DataTrigger>
<DataTrigger Binding="{Binding UpdateTaxPercentageSettingsMessage}" Value="{StaticResource differential_taxation_info_update_went_wrong}">
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
<DataTrigger Binding="{Binding UpdateTaxPercentageSettingsMessage}" Value="{StaticResource differential_taxation_info_was_updated_successfully}">
<Setter Property="Foreground" Value="Green"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
To make switching languages possible while the application is running, the string snippets taken from the resource dictionary has to be a DynamicResource. Unfortunately, I can not use DynamicResources as condition in data triggers. Has anyone faced a similar problem yet? I am grateful for your suggestions!
Do not use triggers on localized text, neither does it work with DynamicResouce, because Value is not a dependency property, nor is it readable. Instead, create an enum that describes your errors.
public enum ErrorType
{
WhoCares, // No comment on this.
ThisIsSuspicious, // Suspicous value.
ItsATrap, // Admiral Ackbar warned us.
ItIsNotWhatYouThinkItIs, // It is exactly what you think.
ItCannotBeThatSerious, // Serious Sam approves.
WhatDoesTheFlashingRedLightMean // If it is still flashing, how bad can it be, really?
}
Expose anothe property for the error and implement INotifyPropertyChanged if necessary.
public ErrorType ErrorType { get; }
Use the property instead of your resources as Value of the triggers.
<TextBlock Text="{Binding UpdateTaxPercentageSettingsMessage}" FontWeight="Bold">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Binding="{Binding ErrorType}" Value="{x:Static local:ErrorType.WhoCares}">
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
<DataTrigger Binding="{Binding ErrorType}" Value="{x:Static local:ErrorType.ThisIsSuspicious}">
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
<DataTrigger Binding="{Binding ErrorType}" Value="{x:Static local:ErrorType.ItsATrap}">
<Setter Property="Foreground" Value="Green"/>
</DataTrigger>
<DataTrigger Binding="{Binding ErrorType}" Value="{x:Static local:ErrorType.ItIsNotWhatYouThinkItIs}">
<Setter Property="Foreground" Value="Green"/>
</DataTrigger>
<DataTrigger Binding="{Binding ErrorType}" Value="{x:Static local:ErrorType.ItCannotBeThatSerious}">
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
<DataTrigger Binding="{Binding ErrorType}" Value="{x:Static local:ErrorType.WhatDoesTheFlashingRedLightMean}">
<Setter Property="Foreground" Value="Green"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
Bonus round. As your data triggers are repetitive (they set the same values often), consider using a Binding with a custom converter that simply says, if the bound value matches any of the given values, apply this setter.
public class IsMatchingConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (!(parameter is IEnumerable enumerable))
return false;
return enumerable.Cast<object>().Contains(value);
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new InvalidOperationException();
}
}
Create an instance of the converter in any resource dictionary in scope.
<Window.Resources>
<local:IsMatchingConverter x:Key="IsMatchingConverter"/>
</Window.Resources>
Change the data triggers and pass the target values in an array as converter parameters.
<TextBlock Text="{Binding UpdateTaxPercentageSettingsMessage}" FontWeight="Bold">
<TextBlock.Style>
<Style TargetType="{x:Type TextBlock}">
<Style.Triggers>
<DataTrigger Value="True">
<DataTrigger.Binding>
<Binding Path="ErrorType" Converter="{StaticResource IsMatchingConverter}">
<Binding.ConverterParameter>
<x:Array Type="local:ErrorType">
<local:ErrorType>WhoCares</local:ErrorType>
<local:ErrorType>ThisIsSuspicious</local:ErrorType>
<local:ErrorType>ItCannotBeThatSerious</local:ErrorType>
</x:Array>
</Binding.ConverterParameter>
</Binding>
</DataTrigger.Binding>
<Setter Property="Foreground" Value="Red"/>
</DataTrigger>
<DataTrigger Value="True">
<DataTrigger.Binding>
<Binding Path="ErrorType" Converter="{StaticResource IsMatchingConverter}">
<Binding.ConverterParameter>
<x:Array Type="local:ErrorType">
<local:ErrorType>ItsATrap</local:ErrorType>
<local:ErrorType>ItIsNotWhatYouThinkItIs</local:ErrorType>
<local:ErrorType>WhatDoesTheFlashingRedLightMean</local:ErrorType>
</x:Array>
</Binding.ConverterParameter>
</Binding>
</DataTrigger.Binding>
<Setter Property="Foreground" Value="Green"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>

How Shall i set the Border of a DataGrid Cell based on a checkbox IsChecked Property

Please find the below style which i use to set the border of DataGridCell on Keyboard focus.
<Style x:Key="DynamciEditableDataGridCellStyle" TargetType="{x:Type DataGridCell}" BasedOn="{StaticResource DefaultDataGridCellStyle}">
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsKeyboardFocusWithin" Value="True"/>
<!--How to add another condition which checks on the check box IsChecked property-->
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter Property="Border.BorderThickness" Value="3"/>
<Setter Property="Border.BorderBrush" Value="{DynamicResource ActiveInputBorderBrush}"/>
</MultiTrigger.Setters>
</MultiTrigger>
<Trigger Property="IsSelected" Value="False">
<Setter Property="Background" Value="{DynamicResource EditableBackgroundBrush}"/>
</Trigger>
</Style.Triggers>
</Style>
I need to have two conditions for setting border thickness and Border Brush.......
1) On KeyboardFocus inside the cell (this is working as per the above code)
2) On checkbox IsChecked property is True (checkbox is not a part of the datagrid but a control inside the same view)
please help me on achieving this..
Because you do not include XAML of your CheckBox in your question, I assume it has a Name specified, then you can use MultiDataTrigger instead of MultiTrigger so that you can use Binding to listen to any property change of some other element:
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsKeyboardFocusWithin, RelativeSource={RelativeSource Self}}" Value="True"/>
<Condition Binding="{Binding IsChecked, ElementName=checkBox}" Value="True"/>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<Setter Property="Border.BorderThickness" Value="3"/>
<Setter Property="Border.BorderBrush" Value="{DynamicResource ActiveInputBorderBrush}"/>
</MultiDataTrigger.Setters>
</MultiDataTrigger>
<Trigger Property="IsSelected" Value="False">
<Setter Property="Background" Value="{DynamicResource EditableBackgroundBrush}"/>
</Trigger>
</Style.Triggers>
I assume your CheckBox has Name of checkBox.

How to multi-trigger on property and binding in wpf style

I have datagrid in my view and I am trying to trigger a style for the DataGridRowHeader so that it has a particular background when both of the following are true:
IsDirty=True (Property on the DataContext of the row)
IsRowSelected=True (Property on the DataGridRowHeader)
How do I write a multi-trigger that triggers for the above paired conditions as my following style code throws InvalidOperationException/{"Must have non-null value for 'Property'."}:
<Style x:Key="DataGridStandardRowHeaderStyle" TargetType="DataGridRowHeader">
<Style.Triggers>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Binding="{Binding IsDirty}" Value="True" />
<Condition Property="IsRowSelected" Value="True" />
</MultiTrigger.Conditions>
<Setter Property="Background" Value="LightYellow" />
</MultiTrigger>
</Style.Triggers>
</Style>
Kindly help me out.
The mistake in my style code finally got across to me and the correct one that now works for me is given below:
<Style x:Key="DataGridStandardRowHeaderStyle" TargetType="DataGridRowHeader">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsDirty}" Value="True" />
<Condition Binding="{Binding IsRowSelected, RelativeSource={RelativeSource Self}}" Value="True" />
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="LightYellow" />
</MultiDataTrigger>
</Style.Triggers>
</Style>

Property trigger bound to flag enum

I have DependencyProperty State of Flags enum type. I want to change some element border color due to changing of the State property. For some reasons I can not operate with the element directly but only by setting its Style.
How can I change following code to make it possible not to check exact value of State but check whether it contains desired flag?
<Style.Triggers>
<Trigger Property="State" Value="None">
<Setter Property="StateBorderBrush" Value="Transparent"/>
</Trigger>
<Trigger Property="State" Value="Covered">
<Setter Property="StateBorderBrush" Value="Blue"/>
</Trigger>
<Trigger Property="State" Value="Selected">
<Setter Property="StateBorderBrush" Value="Red"/>
</Trigger>
<Trigger Property="State" Value="contains flag 'Controlled'">
<Setter Property="StateBorderBrush" Value="Orange"/>
</Trigger>
</Style.Triggers>
As far as I'm concerned, the only solution is converter:
public class EnumFlagConverter : ValueConverter
{
public string FlagValue { get; set; }
public override object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
Enum en = value as Enum;
var enumFlag = Enum.Parse((Type)parameter, FlagValue);
return en.HasFlag((Enum)enumFlag);
}
}
And the xaml binding:
<DataTrigger Binding="{Binding State,Converter={cnv:EnumFlagConverter FlagValue='Locked'}, ConverterParameter={x:Type viewModels:SourceControlState}}"
Value="True">
<Setter Property="Source"
TargetName="srcCtrlStatusIndicator"
Value="{StaticResource ImageSourceCheckedOutBySomeoneElse9x9}" />
</DataTrigger>
Check out Lars's generic answer to a similar question here: Using a generic converter
His solution works with enum flags that are not mutually exclusive. That allowed me to use this XAML.
<DataTrigger Binding="{Binding Path=State, Converter={StaticResource EnumBooleanConverter}, ConverterParameter={x:Static enums:MyStatesEnum.MyEnumFlagValue}}" Value="True">
<Setter Property="BorderBrush" Value="Red"/>
</DataTrigger>
Finally I achieved it by using DataTrigger with relative binding to Self:
<DataTrigger Binding="{Binding Path=State, RelativeSource={RelativeSource Self}}" Value="None">
<Setter Property="StateBorderBrush" Value="Transparent"/>
</DataTrigger>
Then it is possible to expand Binding element and apply appropriate converter
This is how you test for an enum in a trigger:
<ControlTemplate.Triggers>
<Trigger Property="ViewState"
Value="{x:Static constants:LicenseViewState.License}">
<Setter Property="Visibility"
Value="Collapsed"
TargetName="ProductComboBoxField">
</Setter>
</Trigger>
</ControlTemplate.Triggers>

WPF ListView & Converter

I have a ListView with a GridViewColumn that has an image in it, it uses a converter to convert a bool to a ImageSource. It was working perfectly for months and now it suddenly just shows a red dot instead of my image.
Converter:
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
{
if ((bool)value)
return new BitmapImage(new Uri(#"/Dionysus.Styling;component/Images/Actions-dialog-ok-apply-icon.png", UriKind.RelativeOrAbsolute));
else return
null;
}
Xaml:
<GridViewColumn Header="">
<GridViewColumn.CellTemplate>
<DataTemplate>
<Image Source="{Binding Path=IsDelayedOrPreferred, Converter={StaticResource DelayConverter}, Mode=TwoWay}" />
</DataTemplate>
</GridViewColumn.CellTemplate>
</GridViewColumn>
Result:
Has anyone seen this behaviour before or know what might have changed. The only recent changes that I made was upgrading to VS 2013 but all other ListViews with Converters
are still working as expected.
Style:
<Style TargetType="{x:Type ListView}">
<Setter Property="BorderBrush" Value="White"/>
<Setter Property="Foreground" Value="Black"/>
<Setter Property="AlternationCount" Value="2"/>
<Setter Property="Background" Value="WhiteSmoke"/>
<EventSetter Event="Loaded" Handler="ListView_Loaded"/>
</Style>
<Style TargetType="{x:Type GridViewColumnHeader}">
<Setter Property="HorizontalContentAlignment" Value="Left"/>
<Setter Property="Margin" Value="1,0"/>
</Style>
<Style TargetType="{x:Type ListViewItem}" x:Key="ListViewStyle">
<Style.Triggers>
<Trigger Property="ItemsControl.AlternationIndex" Value="0">
<Setter Property="Background" Value="WhiteSmoke"></Setter>
</Trigger>
<Trigger Property="ItemsControl.AlternationIndex" Value="1">
<Setter Property="Background" Value="White"></Setter>
</Trigger>
<DataTrigger Binding="{Binding IsFirst}" Value="True">
<Setter Property="Background" Value="LightGreen"></Setter>
</DataTrigger>
<DataTrigger Binding="{Binding IsLastUnpaid}" Value="True">
<Setter Property="Background" Value="LightSalmon"></Setter>
</DataTrigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="#FF41B1E1"></Setter>
</Trigger>
</Style.Triggers>
<Setter Property="Height" Value="20" />
<Setter Property="HorizontalAlignment" Value="Left"/>
</Style>
UPDATE:
I recently changed from a ListView to a DataGrid and then I see a little exclamation mark as if there is data validation errors on the DataGrid.
Any ideas?

Resources