Remember last time when we solved the puzzle of triggers in WPF? Well, guess what?
That puzzle has a boss level too. Yes, we learned how to use triggers effectively in WPF, but there are real bosses they call it MultiTriggers and MultiDataTriggers.
We learned why we used Triggers. To give you an idea let me brief you a bit.
If you look closely, you’ll find that triggers only satisfies one if condition. But in a programming language, we could have multiple conditions in an (If block)
If both of the conditions are true then the statements below will execute. To bring this characteristic into XAML, WPF has introduced MultiTriggers.
First, let's concentrate on the MultiTrigger.
Syntax
- <MultiTrigger>
- <MultiTrigger.Conditions>
- <Condition Property="IsMouseOver" Value="True"/>
- <Condition Property="IsKeyboardFocused" Value="True"/>
- </MultiTrigger.Conditions>
- <MultiTrigger.Setters>
- <Setter Property="Content" Value="Trigger Applied"/>
- </MultiTrigger.Setters>
- </MultiTrigger>
We are going to apply the trigger on button's properties "IsMouseOver" & "IsKeyboardFocused". So when these 2 properties are true then only we will change the text of a button.
So we not only need to hover a mouse on a button, but also need to have the keyboard's focus.
- Add DataContext through XAML only.
- Changing the text of a button after the trigger has been applied.
- Adding one more button: "name: Cancel". Just to change the keyboard's focus.
- Adding delegate commands for both of the buttons.
- One TextBlock to show where the focus is.
XAML would be designed as in the following with the given attributes.
- <Window x:Class="LearnWPF.MainWindow"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
- xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
- xmlns:local="clr-namespace:LearnWPF.ViewModel"
- mc:Ignorable="d"
- Title="MainWindow" Height="200" Width="400">
- <Window.Resources>
- <local:MainWindowViewModel x:Key="VM" />
- <Style x:Key="SubmitButtonStyle"
- BasedOn="{StaticResource {x:Type Button}}"
- TargetType="Button">
- <Setter Property="Height" Value="25"/>
- <Setter Property="Width" Value="150"/>
- <Setter Property="BorderThickness" Value="2"/>
- <Setter Property="BorderThickness" Value="2"/>
- <Setter Property="HorizontalAlignment" Value="Center"/>
- <Setter Property="VerticalAlignment" Value="Center"/>
- <Setter Property="Content" Value="Trigger Not Applied"/>
- <Setter Property="BorderBrush" Value="Black"/>
- <Setter Property="Background" Value="Gray"/>
- <Setter Property="Foreground" Value="Wheat"/>
- <Style.Triggers>
- <MultiTrigger>
- <MultiTrigger.Conditions>
- <Condition Property="IsMouseOver" Value="True"/>
- <Condition Property="IsKeyboardFocused" Value="True"/>
- </MultiTrigger.Conditions>
- <MultiTrigger.Setters>
- <Setter Property="Content" Value="Trigger Applied"/>
- <Setter Property="BorderBrush" Value="White"/>
- <Setter Property="Background" Value="SkyBlue"/>
- <Setter Property="Foreground" Value="Black"/>
- </MultiTrigger.Setters>
- </MultiTrigger>
- </Style.Triggers>
- </Style>
-
- </Window.Resources>
- <Grid DataContext="{Binding Source={StaticResource VM}}">
- <Grid.RowDefinitions>
- <RowDefinition/>
- <RowDefinition/>
- </Grid.RowDefinitions>
-
- <StackPanel x:Name="MainGrid"
- Orientation="Horizontal"
- HorizontalAlignment="Center">
- <Button x:Name="ButtonStyleMe"
- Style="{StaticResource SubmitButtonStyle}"
- Command="{Binding StyledButtonCliked}"/>
- <Button x:Name="ButtonCancel"
- Content="Cancel"
- Height="25"
- Margin="10 0 0 0"
- Width="150"
- Command="{Binding CancelButtonCliked}"/>
- </StackPanel>
- <TextBlock Text="{Binding FocusText}"
- HorizontalAlignment="Center"
- Grid.Row="1"/>
- </Grid>
- </Window>
Let's have ViewModel as well.
- using System;
- using Prism.Commands;
- using Prism.Mvvm;
-
- namespace LearnWPF.ViewModel
- {
- public class MainWindowViewModel : BindableBase
- {
- private string _focusText;
-
- public string FocusText
- {
- get { return _focusText; }
- set { SetProperty(ref _focusText , value); }
- }
-
- public DelegateCommand StyledButtonCliked { get; set; }
- public DelegateCommand CancelButtonCliked { get; set; }
- public MainWindowViewModel()
- {
- StyledButtonCliked = new DelegateCommand(ShowStyleText);
- CancelButtonCliked = new DelegateCommand(ShowCancelText);
- }
-
- private void ShowCancelText()
- {
- FocusText = "Cancel Button is Focused " + Environment.NewLine + "IsKeyboardFocused in on Cancel Button " +Environment.NewLine + "MultiTigger will not be applicable on IsMouseOver!";
- }
-
- private void ShowStyleText()
- {
- FocusText = "Trigger Button is Focused " + Environment.NewLine + "IsKeyboardFocused in on Trigger Button " + Environment.NewLine + "MultiTigger will be applicable on IsMouseOver!";
- }
- }
- }
Now run the WPF application.
And it is working like a charm.
Before we jump into MultiDataTrigger. Let's first ask ourself why do we need MultiDataTrigger?
- There is one backdrop in MultiTrigger, you can only apply MultiTrigger on multiple properties of the same control. OK, cool. And why this could be a barrier. Say you need to have conditions on two different controls rather than one single control. Oh, now that's a hurdle.
- You can use MultiDataTrigger in this case. It works with the bound value from ViewModel or code-behind rather than dependent on control's properties. Which makes them loosely coupled to control.
- Conditions are bounded with data from View-Model.
Syntax
- <MultiDataTrigger>
- <MultiDataTrigger.Conditions>
- <Condition Binding="{Binding IsAge18}" Value="True"/>
- <Condition Binding="{Binding IsMale}" Value="True"/>
- </MultiDataTrigger.Conditions>
- <MultiDataTrigger.Setters>
- <Setter Property="Text" Value="Trigger applied!"/>
- </MultiDataTrigger.Setters>
- </MultiDataTrigger>
Take the following example: Say there are 2 colleges, a Boy's College & Girl's College.
- Boy's college has a condition that Gender should be Male & student should be at least 18.
- Girl's college has a condition that Gender should be Female & students should be at least 18.
Let's design XAML for these requirements:
- One radio button group: Gender
- RadioButton Male
- RadioButton Female
- CheckBox to check if the student's age is greater than 18
- Textblock at the bottom to show the final result.
- using System;
- using Prism.Commands;
- using Prism.Mvvm;
-
- namespace LearnWPF.ViewModel
- {
- public class MainWindowViewModel : BindableBase
- {
- private bool _isAge18;
-
- public bool IsAge18
- {
- get { return _isAge18; }
- set { SetProperty(ref _isAge18, value); }
- }
- private bool _isMale = false;
-
- public bool IsMale
- {
- get { return _isMale; }
- set
- {
- SetProperty(ref _isMale, value);
- }
- }
- private bool _isFeMale = false;
-
- public bool IsFemale
- {
- get { return _isFeMale; }
- set
- {
- SetProperty(ref _isFeMale, value);
- }
- }
- }
- }
This should work. Let's find out!
As per the gif, we are getting an output as per our design.
We have learned the advantages of both of these, And it would be unfair if I wouldn't tell you it's shortcomings.
If you see in both of these triggers, conditions always have AND operator. Meaning,
Student's age > 18 && IsMale
- <MultiDataTrigger.Conditions>
- <Condition Binding="{Binding IsAge18}" Value="True"/>
- <Condition Binding="{Binding IsMale}" Value="True"/>
- </MultiDataTrigger.Conditions>
What if I want OR operator instead of AND. well, in that case. you will have to use multiple MultiDataTrigger or MultiTrigger tags.
For the above example:
If a student is male: set text to "Male student" OR if the student's age is greater than 18 then set text to "Age is greater than 18"
- <MultiDataTrigger>
- <MultiDataTrigger.Conditions>
- <Condition Binding="{Binding IsAge18}" Value="True"/>
- </MultiDataTrigger.Conditions>
- <MultiDataTrigger.Setters>
- <Setter Property="Text" Value="Age is greater than 18"/>
- </MultiDataTrigger.Setters>
- </MultiDataTrigger>
-
- <MultiDataTrigger>
- <MultiDataTrigger.Conditions>
- <Condition Binding="{Binding IsMale}" Value="True"/>
- </MultiDataTrigger.Conditions>
- <MultiDataTrigger.Setters>
- <Setter Property="Text" Value="Male student"/>
- </MultiDataTrigger.Setters>
- </MultiDataTrigger>
Same way for MultiTrigger, just replace MultiDataTrigger's tag with MultiTrigger.
Conclusion
In this article, we learned:
- What are MultiTriggers & MultiDataTriggers are
- How to use them
- How different are they with respect to normal triggers
- Difference between MultiTrigger & MultiDataTrigger
- Shortcomings & workaround for those shortcomings.
I hope this article has an answer to your question. Thank you for being here. I wish you all the very best.
Find the attached source code for your reference.
If you have any further queries, you can connect with me @