Click here to Skip to main content
15,868,141 members
Articles / Desktop Programming / WPF

TrafficLight Column in WPF DataGrid using MultiTrigger

Rate me:
Please Sign up or sign in to vote.
5.00/5 (4 votes)
20 Jan 2020CPL2 min read 6K   205   6  
Using a self defined TrafficLight Column to virtualize a status bool and the readonly value at the same time instead of a simple CheckBox

Introduction

Sometimes, a simple checkbox is usual for a data grid. I wanted to have a clear showing of an active status as green and red. For this, I found some code but as I wanted to visualize also the disable/readonly state, things were going to be complicated.

Step 1 - Simple Red and Green

As first, I wanted just a simple green cycle for true and red cycle for false. So I defined the column like:

XML
<DataGridTemplateColumn Header="Active" >
	<DataGridTemplateColumn.CellTemplate>
		<DataTemplate>
			<ToggleButton Style="{StaticResource ToggleStyleLight}" 
             IsChecked="{Binding IsActive, UpdateSourceTrigger=PropertyChanged}" />
		</DataTemplate>
	</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
...

And a style using simple Triggers:

XML
<Window.Resources>
        <Style x:Key="ToggleStyleLight" TargetType="ToggleButton">
            <Setter Property="Template">
                <Setter.Value>
                    <ControlTemplate>
                        <Border x:Name="innerBorder">
                            <Ellipse Fill="#60606000" Stroke="#FF000000" Stretch="Fill"
                            x:Name="statusLight" Width="15" Height="15" Margin="2"
                            VerticalAlignment="Center" HorizontalAlignment="Center"/>
                        </Border>
                        <ControlTemplate.Triggers>
                           <Trigger Property="IsChecked" Value="True">
                                <Setter TargetName="statusLight" 
                                        Property="Fill" Value="#FF00FF00" />
                            </Trigger>
                            <Trigger Property="IsChecked" Value="False">
                                <Setter TargetName="statusLight" 
                                        Property="Fill" Value="#FFFF0000" />
                            </Trigger>
                        </ControlTemplate.Triggers>
                    </ControlTemplate>
                </Setter.Value>
            </Setter>
        </Style>
    </Window.Resources>

Learning

This works fine but there was two aspects I wanted to resolve:

  • Updated values were not reliably updated in the view model. I found out UpdateSourceTrigger=PropertyChanged solved the problem.
  • As long as the column is always readable, but when I started to disable the column it was not that simple. I found out MultiTrigger was required.

Using MultiTrigger

Using MultiTrigger enabled me to define four different views depending on the combination of active and readonly. MultiTrigger should be used as soon as you start using more than one condition.

When I start to get this done, I simple add a new trigger checking for IsEnabled but that does not work. Because WPF/XAML is evaluating all triggers and if more than one sets the same property, you may not know which trigger wins.

The updated code is as given below:

XML
<DataGridTemplateColumn Header="Active" IsReadOnly="{Binding IsActivatable}" >
	<DataGridTemplateColumn.CellTemplate>
		<DataTemplate>
			<ToggleButton Style="{StaticResource ToggleStyleLight}"
			IsChecked="{Binding IsActive, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
			IsEnabled="{Binding IsActivatable}" />
		</DataTemplate>
	</DataGridTemplateColumn.CellTemplate>
</DataGridTemplateColumn>
...

Style using simple MultiTrigger:

XML
<Window.Resources>
<Style x:Key="ToggleStyleLight" TargetType="ToggleButton">
	<Setter Property="Template">
		<Setter.Value>
			<ControlTemplate>
				<Border x:Name="innerBorder">
					<Ellipse Fill="#60606000" Stroke="#FF000000" Stretch="Fill"
					x:Name="statusLight" Width="15" Height="15" Margin="2"
					VerticalAlignment="Center" HorizontalAlignment="Center"/>
				</Border>
				<ControlTemplate.Triggers>
					<MultiTrigger>
						<MultiTrigger.Conditions>
							<Condition Property="ToggleButton.IsEnabled" Value="True" />
							<Condition Property="ToggleButton.IsChecked" Value="True" />
						</MultiTrigger.Conditions>
						<MultiTrigger.Setters>
							<Setter TargetName="statusLight" 
                                    Property="Fill" Value="#FF00FF00" />
							<Setter TargetName="statusLight" 
                                    Property="Stroke" Value="#FF000000" />
						</MultiTrigger.Setters>
					</MultiTrigger>
					<MultiTrigger>
						<MultiTrigger.Conditions>
							<Condition Property="ToggleButton.IsEnabled" Value="False" />
							<Condition Property="ToggleButton.IsChecked" Value="True" />
						</MultiTrigger.Conditions>
						<MultiTrigger.Setters>
							<Setter TargetName="statusLight" 
                                    Property="Fill" Value="#60606000" />
							<Setter TargetName="statusLight" 
                                    Property="Stroke" Value="#FF00FF66" />
						</MultiTrigger.Setters>
					</MultiTrigger>
					<MultiTrigger>
						<MultiTrigger.Conditions>
							<Condition Property="ToggleButton.IsEnabled" Value="True" />
							<Condition Property="ToggleButton.IsChecked" Value="False" />
						</MultiTrigger.Conditions>
						<MultiTrigger.Setters>
							<Setter TargetName="statusLight" 
                                    Property="Fill" Value="#FFFF0000" />
							<Setter TargetName="statusLight" 
                                    Property="Stroke" Value="#FF000000" />
						</MultiTrigger.Setters>
					</MultiTrigger>
					<MultiTrigger>
						<MultiTrigger.Conditions>
							<Condition Property="ToggleButton.IsEnabled" Value="False" />
							<Condition Property="ToggleButton.IsChecked" Value="False" />
						</MultiTrigger.Conditions>
						<MultiTrigger.Setters>
							<Setter TargetName="statusLight" 
                                    Property="Fill" Value="#60606000" />
							<Setter TargetName="statusLight" 
                                    Property="Stroke" Value="#FFFF0066" />
						</MultiTrigger.Setters>
					</MultiTrigger>
				</ControlTemplate.Triggers>
			</ControlTemplate>
		</Setter.Value>
	</Setter>
</Style>

Points of Interest

MultiTrigger seams to be difficult if you see them for the first time. But effectively, they are very easy. There are not many samples available, especially in combination with DataGrids, so I decided to put this one online. As you see, it is simple to combine different Properties to activate a trigger or not. Also, it is important to know that simple Triggers may override each other.

History

  • 17th January, 2020: Initial version

License

This article, along with any associated source code and files, is licensed under The Common Public License Version 1.0 (CPL)


Written By
Software Developer (Senior) Haufe Lexware
Germany Germany
Senior Development Engineer @Haufe Group
Developing C#, C++, Java Script

Comments and Discussions

 
-- There are no messages in this forum --