Change Trigger order in BasedOn / inherited Style - wpf

I have a base Style - DataGridRowSelectionStyle. On some DataGrids I need to extend this Style to ink the background.
DataGridRowSelectionStyle
<Style TargetType="DataGridRow" x:Key="DataGridRowSelectionStyle">
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{extensions:Theme Key=DataGrid_Row_IsMouseOver}"/>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="{extensions:Theme Key=DataGrid_Row_IsSelected}"/>
<Setter Property="FontWeight" Value="Bold"/>
</Trigger>
</Style.Triggers>
</Style>
RowStyle
<DataGrid.RowStyle>
<Style TargetType="DataGridRow" BasedOn="{StaticResource DataGridRowSelectionStyle}">
<Style.Triggers>
<DataTrigger Binding="{Binding CurrentStatus}" Value="{x:Static production1:ProcessDataEval.OK}">
<Setter Property="Background" Value="{extensions:Theme Key=DGLB_Green}"/>
</DataTrigger>
<DataTrigger Binding="{Binding CurrentStatus}" Value="{x:Static production1:ProcessDataEval.NG}">
<Setter Property="Background" Value="{extensions:Theme Key=DGLB_Red}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
Because of the Trigger order, the two base Triggers are overwritten and IsMouseOver or IsSelected is not triggered anymore.
Solution 1: Extend the RowStyle. Very bad solution, because I would not need my base Style anymore..
<DataGrid.RowStyle>
<Style TargetType="DataGridRow" BasedOn="{StaticResource DataGridRowSelectionStyle}">
<Style.Triggers>
<DataTrigger Binding="{Binding CurrentStatus}" Value="{x:Static production1:ProcessDataEval.OK}">
<Setter Property="Background" Value="{extensions:Theme Key=DGLB_Green}"/>
</DataTrigger>
<DataTrigger Binding="{Binding CurrentStatus}" Value="{x:Static production1:ProcessDataEval.NG}">
<Setter Property="Background" Value="{extensions:Theme Key=DGLB_Red}"/>
</DataTrigger>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Background" Value="{extensions:Theme Key=DataGrid_Row_IsMouseOver}"/>
</Trigger>
<Trigger Property="IsSelected" Value="True">
<Setter Property="Background" Value="{extensions:Theme Key=DataGrid_Row_IsSelected}"/>
<Setter Property="FontWeight" Value="Bold"/>
</Trigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>
Solution 2: Create a behavior and add it to the base Style which would reorder the final Style.
Problem: Behavior<TriggerCollection> or Behavior<Style> is not working!
The type 'System.Windows.Style' must be convertible to 'System.Windows.DependencyObject' in order to use it as paramter 'T' in the generic class 'System.Windows.Interactivity.Behavior'
Someone got a solution how to use a behavior in a Style or how to change the trigger order in the inherited Style?

I got a solution by using an AttachedProperty.
I cache every Trigger with an index for the final TriggerCollection. After the DataGridRow is rendered, else if (d is FrameworkElement frameworkElement) is true and the Style gets cloned with a new order of the Triggers.
public static class TriggerAttachedBehavior
{
private static readonly Dictionary<Trigger, int> _Triggers = new Dictionary<Trigger, int>();
/// <summary>
/// Gets property value.
/// </summary>
/// <param name="attachedObj"></param>
/// <returns></returns>
public static int GetOderIndex(Trigger attachedObj)
{
return (int)attachedObj.GetValue(OderIndexProperty);
}
/// <summary>
/// Sets property value.
/// </summary>
/// <param name="attachedObj"></param>
/// <param name="value"></param>
public static void SetOderIndex(Trigger attachedObj, int value)
{
attachedObj.SetValue(OderIndexProperty, value);
}
/// <summary>
/// The <see cref="OderIndexProperty"/> DependencyProperty.
/// </summary>
public static readonly DependencyProperty OderIndexProperty = DependencyProperty.RegisterAttached("OderIndex", typeof(int), typeof(TriggerAttachedBehavior), new UIPropertyMetadata(-1, OderIndexChangedCallback));
/// <summary>
/// Occurs when OderIndexProperty has changed.
/// </summary>
/// <param name="d">Dependency object.</param>
/// <param name="args">Event arguments.</param>
private static void OderIndexChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs args)
{
if (d is Trigger attachedObj)
{
_Triggers.Add(attachedObj, (int)args.NewValue);
}
else if (d is FrameworkElement frameworkElement)
{
// clone style with trigger lock
var newStyle = new Style(frameworkElement.Style.TargetType, frameworkElement.Style);
newStyle.Triggers.Clear();
// add all triggers except the base
foreach (TriggerBase triggerBase in frameworkElement.Style.Triggers)
{
if(_Triggers.Any(t => _Equals(t.Key, triggerBase)))
continue;
newStyle.Triggers.Add(triggerBase);
}
// add the base class triggers
foreach (int i in _Triggers.Values.OrderBy(t => t))
{
newStyle.Triggers.Add(_Triggers.First(t => t.Value == i).Key);
}
// apply new style
frameworkElement.Style = newStyle;
}
}
private static bool _Equals(TriggerBase x, TriggerBase y)
{
if (x.GetType() != y.GetType())
return false;
switch (x)
{
case DataTrigger dataTrigger:
return false;
case EventTrigger eventTrigger:
return false;
case MultiDataTrigger multiDataTrigger:
return false;
case MultiTrigger multiTrigger:
return false;
case Trigger trigger:
return trigger.Property.Name.Equals((y as Trigger).Property.Name);
}
return false;
}
}
<Style TargetType="DataGridRow" x:Key="DataGridRowSelectionStyle" BasedOn="{StaticResource DataGridRowDefaultStyle}">
<Setter Property="behaviors:TriggerAttachedBehavior.OderIndex" Value="0"></Setter>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True" behaviors:TriggerAttachedBehavior.OderIndex="998">
<Setter Property="Background" Value="{extensions:Theme Key=DataGrid_Row_IsMouseOver}"/>
</Trigger>
<Trigger Property="IsSelected" Value="True" behaviors:TriggerAttachedBehavior.OderIndex="999">
<Setter Property="Background" Value="{extensions:Theme Key=DataGrid_Row_IsSelected}"/>
<Setter Property="FontWeight" Value="Bold"/>
</Trigger>
</Style.Triggers>
</Style>

MultiDataTrigger can be used to paint rows based on their status value only when IsMouseOver is false and IsSelected is false. Additional conditions (when they are met) prevent MultiDataTriggers from overriding base Triggers:
<DataGrid.RowStyle>
<Style TargetType="DataGridRow" BasedOn="{StaticResource DataGridRowSelectionStyle}">
<Style.Triggers>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding CurrentStatus}" Value="{x:Static production1:ProcessDataEval.OK}"/>
<Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="False"/>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="False"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="{extensions:Theme Key=DGLB_Green}"/>
</MultiDataTrigger>
<MultiDataTrigger >
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding CurrentStatus}" Value="{x:Static production1:ProcessDataEval.NG}"/>
<Condition Binding="{Binding IsMouseOver, RelativeSource={RelativeSource Self}}" Value="False"/>
<Condition Binding="{Binding IsSelected, RelativeSource={RelativeSource Self}}" Value="False"/>
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="{extensions:Theme Key=DGLB_Red}"/>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</DataGrid.RowStyle>

Related

How do you get ListBoxItem.IsMouseCaptured to trigger style changes?

I'm using the following style:
<Style x:Key="ListBoxItemStyle" TargetType="{x:Type ListBoxItem}">
<Setter Property="Foreground" Value="{DynamicResource Button-Foreground}"/>
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ListBoxItem}">
<TextBlock Padding="10"
Background="{TemplateBinding Background}"
Foreground="{TemplateBinding Foreground}">
<ContentPresenter/>
</TextBlock>
</ControlTemplate>
</Setter.Value>
</Setter>
<Style.Triggers>
<Trigger Property="IsEnabled" Value="False">
<Setter Property="Foreground" Value="{DynamicResource Button-Foreground-Disabled}"/>
</Trigger>
<Trigger Property="IsMouseCaptured" Value="True">
<Setter Property="Background" Value="{DynamicResource Button-Background-Hover}"/>
<Setter Property="Foreground" Value="{DynamicResource Button-Foreground}"/>
</Trigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsMouseOver" Value="True"/>
<Condition Property="IsMouseCaptured" Value="False"/>
</MultiTrigger.Conditions>
<Setter Property="Background" Value="{DynamicResource Button-Background-Hover}"/>
<Setter Property="Foreground" Value="{DynamicResource Button-Foreground-Hover}"/>
</MultiTrigger>
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="IsSelected" Value="True"/>
<Condition Property="IsMouseOver" Value="False"/>
</MultiTrigger.Conditions>
<Setter Property="Background" Value="{DynamicResource Button-Foreground}"/>
<Setter Property="Foreground" Value="{DynamicResource Button-Foreground-Hover}"/>
</MultiTrigger>
</Style.Triggers>
</Style>
The issue I'm having is that the IsMouseCaptured trigger never occurs. It keeps the IsMouseOver style even when I clicked it (the background is the same color intentionally, but I've tried changing just to experiment and it doesn't change at all and the foreground certainly doesn't change).
Is there a way to have triggers occur for a specific list box item when it is clicked? All the other triggers are working as expected.
Is there a way to have triggers occur for a specific list box item when it is clicked?
Not using any built-in property.
You could create your own custom ListBoxItem that adds an IsPressed property that you can bind to:
public class CustomListBoxItem : ListBoxItem
{
public static readonly DependencyProperty IsPressedProperty = DependencyProperty.Register(
nameof(IsPressed), typeof(bool), typeof(CustomListBoxItem), new PropertyMetadata(false));
public bool IsPressed
{
get => (bool)GetValue(IsPressedProperty);
set => SetValue(IsPressedProperty, value);
}
protected override void OnMouseDown(MouseButtonEventArgs e)
{
base.OnMouseDown(e);
IsPressed = true;
}
protected override void OnMouseUp(MouseButtonEventArgs e)
{
base.OnMouseUp(e);
IsPressed = false;
}
}
Unless you create the ListBoxItem elements explicitly, you will also need a custom ListBox that generates containers of your custom type:
public class CustomListBox : ListBox
{
protected override DependencyObject GetContainerForItemOverride() =>
new CustomListBoxItem();
}

MultiDataTrigger Not respecting all conditions

I have an "On" "Off" switch that I've created and I cannot get the MultiDataTrigger to respect all Conditions.
<UserControl.Resources>
<Style TargetType="Button" BasedOn="{StaticResource NoDevExpressStyle}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate>
<Border Height="30" Width="81" CornerRadius="15" BorderThickness="1" Background="White">
<Border.Style>
<Style TargetType="Border">
<Style.Triggers>
<!-- On Not Selected -->
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{ Binding Path=State}" Value="{x:Static models:ExperimentState.On}" />
<Condition Binding="{Binding SwitchClicked, RelativeSource={RelativeSource AncestorType=UserControl, Mode=FindAncestor}}" Value="False"></Condition>
</MultiDataTrigger.Conditions>
<Setter Property="BorderBrush" Value="{StaticResource OnInactiveColorBrush}"></Setter>
</MultiDataTrigger>
<!-- On Selected -->
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{ Binding Path=State}" Value="{x:Static models:ExperimentState.On}" />
<Condition Binding="{Binding SwitchClicked, RelativeSource={RelativeSource AncestorType=UserControl, Mode=FindAncestor}}" Value="True"></Condition>
</MultiDataTrigger.Conditions>
<Setter Property="BorderBrush" Value="{StaticResource OnColorBrush}"></Setter>
</MultiDataTrigger>
<!-- OFF Not Selected -->
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{ Binding Path=State}" Value="{x:Static models:ExperimentState.Off}" />
<Condition Binding="{Binding Path=SwitchClicked}" Value="False"></Condition>
</MultiDataTrigger.Conditions>
<Setter Property="BorderBrush" Value="{StaticResource OffInactiveColorBrush}"></Setter>
</MultiDataTrigger>
<!-- Off and selected -->
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{ Binding Path=State}" Value="{x:Static models:ExperimentState.Off}" />
<Condition Binding="{Binding Path=SwitchClicked}" Value="True"></Condition>
</MultiDataTrigger.Conditions>
<Setter Property="BorderBrush" Value="{StaticResource OffColorBrush}"></Setter>
</MultiDataTrigger>
</Style.Triggers>
</Style>
</Border.Style>
</Border>
<ControlTemplate.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Cursor" Value="Hand"></Setter>
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</UserControl.Resources>
<Button x:Name="Button" Click="ButtonBase_OnClick" />
It seems to respecting the "State" Property which is a dependency property that is set once and never changed. However the SwitchClicked is not respecting which is a property inside the control.
private bool _switchClicked;
public bool SwitchClicked
{
get => _switchClicked;
set => _switchClicked = value;
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
if (Button.Command != null)
{
if (Button.Command.CanExecute(Button.CommandParameter))
{
SwitchClicked = !SwitchClicked;
SetValue(IsActiveProperty, !IsActive);
Button.Command.Execute(null);
}
}
}
Can someone please tell me how to insure that all of my conditions are met?
I have tried binding with relitive source to self and user control, however the SwitchClicked property is in the codebehind and there is no ViewModel.
You have to implement INotifyPropertyChanged either in your view model (or I suppose your code-behind), otherwise setting your property will not notify
being changed and the binding in your trigger will not update.
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
public bool SwitchClicked
{
get => _switchClicked;
set
{
if (_switchClicked != value)
{
_switchClicked = value;
OnPropertyChanged(nameof(SwitchClicked));
}
}
}

Using a DataTrigger in my Style

I am trying to change the fill of a rectangle based upon a value in my ViewModel but despite having tried all the suggestions I have found online, it still isn't working.
The IsMouseOver trigger works fine but the DataTrigger is ignored despite the fact there is always either a 4 or 5 in my ViewModel property.
Could somebody show me where I may be going wrong?
Thanks.
This is my style:
<Style x:Key="FavouriteRectangleStyle" TargetType="{x:Type Rectangle}">
<Setter Property="Opacity" Value="1"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Rectangle.Opacity" Value="0.5" />
</Trigger>
<DataTrigger Binding="{Binding Path=Theme}" Value="4">
<Setter Property="Rectangle.Fill" Value="{DynamicResource content__star__hex646464__shadow}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=Theme}" Value="5">
<Setter Property="Rectangle.Fill" Value="{DynamicResource content__star__favorit__hexebebeb__shadow}"/>
</DataTrigger>
</Style.Triggers>
</Style>
EDIT: So it seems that my Binding was incorrect due to my incorrect assumption that the DataContext of the UserControl is where I should direct the binding.
The Rectangle is in the template for a ListBox and has "Items" as it's DataContext so by changing the "Binding" it now works.
Many thanks for all assistance though:
SOLUTION:
<Style x:Key="FavouriteRectangleStyle" TargetType="{x:Type Rectangle}">
<Setter Property="Opacity" Value="1"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Rectangle.Opacity" Value="0.5" />
</Trigger>
<DataTrigger Binding="{Binding DataContext.Theme,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=UserControl} }" Value="4">
<Setter Property="Rectangle.Fill"
Value="{DynamicResource content__star__hex646464__shadow}"/>
</DataTrigger>
<DataTrigger Binding="{Binding DataContext.Theme,
RelativeSource={RelativeSource Mode=FindAncestor,
AncestorType=UserControl} }" Value="5">
<Setter Property="Rectangle.Fill"
Value="{DynamicResource content__star__favorit__hexebebeb__shadow}"/>
</DataTrigger>
</Style.Triggers>
</Style>
Change your TargetType="Rectangle"
Here is the code I used to test
<Style x:Key="FavouriteRectangleStyle" TargetType="Rectangle">
<Setter Property="Fill" Value="Blue"/>
<Setter Property="Opacity" Value="1"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Rectangle.Opacity" Value="0.5" />
</Trigger>
<DataTrigger Binding="{Binding Theme}" Value="4">
<Setter Property="Fill" Value="Red"/>
</DataTrigger>
<DataTrigger Binding="{Binding Theme}" Value="5">
<Setter Property="Fill" Value="Black"/>
</DataTrigger>
</Style.Triggers>
</Style>
And the implementation
<Rectangle Style="{StaticResource FavouriteRectangleStyle}"/>
Inside the constructor of your window or usercontrol what you haveused, you should set this.DataContext = this; Otherwise binding not properly worked for the corresponding window or usercontrol.
That probably because you miss to implement the INotifyPropertyChanged interface, update you Theme property to this:
private int _theme = 5;
public int Theme
{
get
{
return _theme;
}
set
{
if (_theme == value)
{
return;
}
_theme = value;
OnPropertyChanged();
}
}
and make sure your codebehind or your viewmodel is implementing the interface
public partial class MainWindow : Window,INotifyPropertyChanged
{
here a full working sample:
Title="MainWindow" Height="350" Width="525"
DataContext="{Binding RelativeSource={RelativeSource Self}}">
<Window.Resources>
<SolidColorBrush x:Key="content__star__hex646464__shadow" Color="Red"/>
<SolidColorBrush x:Key="content__star__favorit__hexebebeb__shadow" Color="Green"/>
<Style x:Key="FavouriteRectangleStyle" TargetType="{x:Type Rectangle}">
<Setter Property="Opacity" Value="1"/>
<Style.Triggers>
<Trigger Property="IsMouseOver" Value="True">
<Setter Property="Rectangle.Opacity" Value="0.5" />
</Trigger>
<DataTrigger Binding="{Binding Path=Theme}" Value="4">
<Setter Property="Rectangle.Fill" Value="{DynamicResource content__star__hex646464__shadow}"/>
</DataTrigger>
<DataTrigger Binding="{Binding Path=Theme}" Value="5">
<Setter Property="Rectangle.Fill" Value="{DynamicResource content__star__favorit__hexebebeb__shadow}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Window.Resources>
<StackPanel>
<Rectangle Style="{StaticResource FavouriteRectangleStyle}" VerticalAlignment="Center" HorizontalAlignment="Center" Width="200" Height="100"></Rectangle>
<Button Content="Change Theme" Click="ButtonBase_OnClick"></Button>
</StackPanel>
and the code behind :
public partial class MainWindow : Window,INotifyPropertyChanged
{
private int _theme = 5;
public int Theme
{
get
{
return _theme;
}
set
{
if (_theme == value)
{
return;
}
_theme = value;
OnPropertyChanged();
}
}
public MainWindow()
{
this.InitializeComponent();
}
private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
{
Theme = Theme == 5 ? 4 : 5;
}
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
}
Answer in EDIT part of original question. Thanks for all assistance

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>

Trigger when condition is not equal to

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>

Resources