How can I provide multiple conditions for data trigger in WPF?
THIS ANSWER IS FOR ANIMATIONS ONLY
If you wanna implement the AND logic, you should use MultiTrigger, here is an example:
Suppose we want to do some actions if the property Text="" (empty string) AND IsKeyboardFocused="False", then your code should look like the following:
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Text" Value="" />
<Condition Property="IsKeyboardFocused" Value="False" />
</MultiTrigger.Conditions>
<MultiTrigger.EnterActions>
<!-- Your actions here -->
</MultiTrigger.EnterActions>
</MultiTrigger>
If you wanna implement the OR logic, there are couple of ways, and it depends on what you try to do:
The first option is to use multiple Triggers.
So, suppose you wanna do something if either Text="" OR IsKeyboardFocused="False",
then your code should look something like this:
<Trigger Property="IsEnabled" Value="false">
<Setter Property="Opacity" TargetName="border" Value="0.56"/>
</Trigger>
<Trigger Property="IsMouseOver" Value="true">
<Setter Property="BorderBrush" TargetName="border"
Value="{StaticResource TextBox.MouseOver.Border}"/>
</Trigger>
But the problem in this is what will I do if i wanna do something if either Text ISN'T null OR IsKeyboard="True"? This can be achieved by the second approach:
Recall De Morgan's rule, that says !(!x && !y) = x || y.
So we'll use it to solve the previous problem, by writing a multi trigger that it's triggered when Text="" and IsKeyboard="True", and we'll do our actions in EXIT ACTIONS, like this:
<MultiTrigger>
<MultiTrigger.Conditions>
<Condition Property="Text" Value="" />
<Condition Property="IsKeyboardFocused" Value="False" />
</MultiTrigger.Conditions>
<MultiTrigger.ExitActions>
<!-- Do something here -->
</MultiTrigger.ExitActions>
</MultiTrigger>
Use MultiDataTrigger type
<Style TargetType="ListBoxItem">
<Style.Triggers>
<DataTrigger Binding="{Binding Path=State}" Value="WA">
<Setter Property="Foreground" Value="Red" />
</DataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding Path=Name}" Value="Portland" />
<Condition Binding="{Binding Path=State}" Value="OR" />
</MultiDataTrigger.Conditions>
<Setter Property="Background" Value="Cyan" />
</MultiDataTrigger>
</Style.Triggers>
</Style>
@jasonk - if you want to have "or" then negate all conditions since (A and B) <=> ~(~A or ~B)
but if you have values other than boolean try using type converters:
<MultiDataTrigger.Conditions>
<Condition Value="True">
<Condition.Binding>
<MultiBinding Converter="{StaticResource conditionConverter}">
<Binding Path="Name" />
<Binding Path="State" />
</MultiBinding>
</Condition.Binding>
<Setter Property="Background" Value="Cyan" />
</Condition>
</MultiDataTrigger.Conditions>
you can use the values in Convert method any way you like to produce a condition which suits you.
To elaborate on @serine's answer and illustrate working with non-trivial multi-valued condition: I had a need to show a "dim-out" overlay on an item for the boolean condition NOT a AND (b OR NOT c)
.
For background, this is a "Multiple Choice" question. If the user picks a wrong answer it becomes disabled (dimmed out and cannot be selected again). An automated agent has the ability to focus on any particular choice to give an explanation (border highlighted). When the agent focuses on an item, it should not be dimmed out even if it is disabled. All items that are not in focused are marked de-focused, and should be dimmed out.
The logic for dimming is thus:
NOT IsFocused AND (IsDefocused OR NOT Enabled)
To implement this logic, I made a generic IMultiValueConverter
named (awkwardly) to match my logic
// 'P' represents a parenthesis
// ! a && ( b || ! c )
class NOT_a_AND_P_b_OR_NOT_c_P : IMultiValueConverter
{
// redacted [...] for brevity
public object Convert(object[] values, ...)
{
bool a = System.Convert.ToBoolean(values[0]);
bool b = System.Convert.ToBoolean(values[1]);
bool c = System.Convert.ToBoolean(values[2]);
return !a && (b || !c);
}
...
}
In the XAML I use this in a MultiDataTrigger
in a <Style><Style.Triggers>
resource
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<!-- when the equation is TRUE ... -->
<Condition Value="True">
<Condition.Binding>
<MultiBinding Converter="{StaticResource NOT_a_AND_P_b_OR_NOT_c_P}">
<!-- NOT IsFocus AND ( IsDefocused OR NOT Enabled ) -->
<Binding Path="IsFocus"/>
<Binding Path="IsDefocused" />
<Binding Path="Enabled" />
</MultiBinding>
</Condition.Binding>
</Condition>
</MultiDataTrigger.Conditions>
<MultiDataTrigger.Setters>
<!-- ... show the 'dim-out' overlay -->
<Setter Property="Visibility" Value="Visible" />
</MultiDataTrigger.Setters>
</MultiDataTrigger>
And for completeness sake, my converter is defined in a ResourceDictionary
<ResourceDictionary xmlns:conv="clr-namespace:My.Converters" ...>
<conv:NOT_a_AND_P_b_OR_NOT_c_P x:Key="NOT_a_AND_P_b_OR_NOT_c_P" />
</ResourceDictionary>