Month: December 2017

WPF MultiDataTrigger and IMultiValueConverter

It’s been a very long since I posted my last article. I am back now with new enthusiasm and with more energy.

So, today I am going to write about how to use MultiDataTrigger and IMultiValueConverter.
Recently I came across a situation where in I had to make a choice between these two.

I won’t say that both serve a same purpose. But there could be some situations where in one has to choose between these two choices.

Before proceeding let us try to understand the concept of MultiDataTrigger and IMultiValueConverter.
Definition of MultiDataTrigger given on docs says that,

“Represents a trigger that applies property values or performs actions when the bound data meet a set of conditions.”

While using MultiDataTrigger in XAML, we need to specify the conditions on which trigger would be activated. Once these conditions are met, a corresponding value in the setter tag would be set against the required property.

Let us have a look at hypothetical example. Application screen is having two check boxes and one text block.
Background color of the textblock needs to be set on click of these check boxes. Conditions for the same are:

  1. Background should be red when none of the check box is checked
  2. Background should be yellow when only first check box is checked
  3. Background should be blue when only second check box is checked
  4. Background should be green when both check boxes are checked

To achieve above condition we will use MultiDataTrigger in following mentioned way.

<StackPanel>
	<CheckBox Content="Weld" Name="cbWeld"/>
	<CheckBox Content="Assembly" Name="cbAssembly"/>               

	<TextBlock Text="Material">
		<TextBlock.Style>
			
<Style TargetType="{x:Type TextBlock}">
				<Style.Triggers>
					<MultiDataTrigger>
						<MultiDataTrigger.Conditions>
							<Condition Binding="{Binding ElementName=cbWeld, Path=IsChecked}" Value="True"/>
							<Condition Binding="{Binding ElementName=cbAssembly, Path=IsChecked}" Value="True"/>
						</MultiDataTrigger.Conditions>
						<Setter Property="Background" Value="Green"></Setter>
					</MultiDataTrigger>
					<MultiDataTrigger>
						<MultiDataTrigger.Conditions>
							<Condition Binding="{Binding ElementName=cbWeld, Path=IsChecked}" Value="False"/>
							<Condition Binding="{Binding ElementName=cbAssembly, Path=IsChecked}" Value="True"/>
						</MultiDataTrigger.Conditions>
						<Setter Property="Background" Value="Blue"></Setter>
					</MultiDataTrigger>
					<MultiDataTrigger>
						<MultiDataTrigger.Conditions>
							<Condition Binding="{Binding ElementName=cbWeld, Path=IsChecked}" Value="True"/>
							<Condition Binding="{Binding ElementName=cbAssembly, Path=IsChecked}" Value="False"/>
						</MultiDataTrigger.Conditions>
						<Setter Property="Background" Value="Yellow"></Setter>
					</MultiDataTrigger>
					<MultiDataTrigger>
						<MultiDataTrigger.Conditions>
							<Condition Binding="{Binding ElementName=cbWeld, Path=IsChecked}" Value="False"/>
							<Condition Binding="{Binding ElementName=cbAssembly, Path=IsChecked}" Value="False"/>
						</MultiDataTrigger.Conditions>
						<Setter Property="Background" Value="Red"></Setter>
					</MultiDataTrigger>
				</Style.Triggers>
			</Style>


		</TextBlock.Style>
	</TextBlock>
</StackPane>

Here I have used more than one MultiDataTrigger to achieve the objective. In every condition of MultiDataTrigger, different values of check boxes are monitored.
Background property of the textblock is set accordingly.
But the problem with this approach is that, MultiDataTrigger needs to be repeated in XAML to cater different requirements.

This problem can be solved using IMultiValueConverter. Documentation for IMultiValueConverter can be found here. IMultiValueConverter is used with Multibinding. In Multibinding, more that one binding can be specified and IMultiValueConverter can be used to convert the bound values to a single desired value.

Here we will bind IsChecked property and will implement IMultiValueConverter to convert the boolean values of check boxes to Brushes. This converted value would be assigned to Background property of the Textblock.

Let us implement IMultiValueConverter.

public class BooleanToColorConverter : IMultiValueConverter
{
	public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
	{
		bool cb1 = (bool)values[0];
		bool cb2 = (bool)values[1];

		if (cb1 && cb2)
			return Brushes.Green;
		else if (cb1 && !cb2)
			return Brushes.Yellow;
		else if (!cb1 && cb2)
			return Brushes.Blue;
		else
			return Brushes.Red;
	}

	public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
	{
		return null;
	}
}

To use this BooleanToColorConverter, it’s namespace needs to be included in XAML and a resource needs to be defined for it.

<Window x:Class="WpfBasics.MainWindow"
        xmlns:custom="clr-namespace:WpfBasics">
<Window.Resources>        
	<custom:BooleanToColorConverter x:Key="ColorConverter"/>
</Window.Resources>
<StackPanel>
	<CheckBox Content="Weld" Name="cbWeld"/>
	<CheckBox Content="Assembly" Name="cbAssembly"/>               

	<TextBlock Text="Material">
		<TextBlock.Style>
			
<Style TargetType="{x:Type TextBlock}">
				<Setter Property="Background">
					<Setter.Value>
						<MultiBinding Converter="{StaticResource ColorConverter}">
							<Binding ElementName="cbWeld" Path="IsChecked"></Binding>
							<Binding ElementName="cbAssembly" Path="IsChecked"></Binding>
						</MultiBinding>
					</Setter.Value>
				</Setter>
			</Style>

		</TextBlock.Style>
	</TextBlock>
</StackPanel>
</Window>

Above code looks more elegant than the previous one. In Setter’s value, MultiBinding is set with the Converter.
BooleanToColorConverter accepts the values from various bindings and returns a desired color. This color is used as a value in setter for Background property of TextBlock.

So we have achieved the desired result using two different solutions.
Enjoy coding!!