192

How can I provide multiple conditions for data trigger in WPF?

Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72
Sumeru Suresh
  • 3,121
  • 5
  • 28
  • 31

4 Answers4

307

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>
Gishu
  • 134,492
  • 47
  • 225
  • 308
  • 29
    Is there a way to do an "OR" statement in the multiTrigger. e.g. the name = "portland" OR the state = "OR" – jasonk Jun 28 '10 at 21:28
  • 12
    @jasonk - Not sure if you can do that with a MultiTrigger. You can define two triggers for that.. – Gishu Jun 29 '10 at 05:40
  • 2
    If it's a simple OR condition statement, you can reverse the logic so it turns into an AND. |condition1 |condition2 | result | |true |true | true | |true |false | true | |false |true | true | |false |false | false | instead of checking for either/OR are true, check when both/AND are false, and set the default to true. – WeSam Abdallah Oct 28 '14 at 15:47
  • 3
    @WeSamAbdallah You can't check for conditions being false. – Jim Balter Mar 26 '16 at 03:01
  • 5
    You can check for a condition being false by a simple converter. – Paul Jul 05 '17 at 17:32
  • 2
    @Paul How about ? – r2d2 Jul 11 '19 at 07:55
58

@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.

cod3monk3y
  • 9,508
  • 6
  • 39
  • 54
serine
  • 1,338
  • 14
  • 24
  • 3
    Ah, smart move to negate everything and flip it to an OR condition :) – myermian Sep 11 '13 at 02:15
  • 8
    Could you please expand on this answer a little bit? I'm not sure how to use it. What does the `conditionConverter` do? How are we specifying "Portland" and "OR" as our two `or` options in this example? – DLeh May 30 '14 at 20:42
  • 4
    @cod3monk3y, your link is broken. – Spidermain50 Jul 19 '16 at 16:31
21

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>
Community
  • 1
  • 1
cod3monk3y
  • 9,508
  • 6
  • 39
  • 54
  • 12
    I don't think this is the intent of converters to be used this way. They are really meant for converting values for display. When it gets this complicated, just create a calculated property on the view model that gives you what you need. – Martin Capodici Apr 07 '16 at 01:15
  • 18
    That naming though – Kelly Elton Jan 19 '17 at 23:16
  • 19
    Please take a moment of silence to remember when programming was a craft and code was elegant. – Fuzzy Logic Jun 30 '17 at 00:05
1

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>
Elyasaf755
  • 2,239
  • 18
  • 24