Search code examples
c#wpfdatagrid

Conditional Formatting of a WPF DataGrid Cell Or Row Based On The Values Of Multiple Cells


I have a DataGrid in WPF. I would like to perform some calculations in C# using the value of one or more cells in a row and format cells in that row based on the results. I've searched but can't find anything about using multiple values to format cells.

For the purposes of this exercise assume the DataGrid contains three fields called FolderStatus, FolderCount and FolderName.

(code edited as per egeer's suggested answer)

Here's the relevant XAML -

<Window x:Class="WorkflowManager.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:WorkflowManager"
    mc:Ignorable="d"
    Title="MainWindow" Height="450" Width="800">

               ....


 <DataGrid d:ItemsSource="{d:SampleData ItemCount=1}" Margin="0,0,0,0" Name ="WorkflowDatabase" Grid.Column="0" Grid.Row="3" VerticalScrollBarVisibility="Visible" IsReadOnly="True" MinRowHeight="20">
                        <DataGrid.Resources>
                            <local:SampleConverter x:Key="bgConverter" />
                            <Style TargetType="DataGridCell">
                                <Setter Property="Background"
  Value="{Binding Converter={StaticResource bgConverter}}" />
                            </Style>
                        </DataGrid.Resources>
                    </DataGrid>

When I use egeer's suggested code Visual Studio tells me that SampleData in

if (value is  SampleData data) 

Has an error "The type or namespace name could not be found (are you missing a using directive or an assembly reference?)"

So I've simplified the code here to always return a red brush but there is no apparent change to my DataGrid.

public class SampleConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        var defaultBrush = Brushes.Transparent;
        defaultBrush = Brushes.Red;
        return defaultBrush;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

Visual Studio is telling me that


Solution

  • There are really two ways you could go here, either use a MultiDataTrigger or an IValueConverter.

    Option 1 - IValueConverter

    Since you are formatting the whole row, this is should be pretty straightforward. The DataContext of each row is what gets passed to the IValueConverter, so you will have your whole object at your disposal to calculate any conditional formatting as you see fit.

    In the example below, I do some convoluted comparisons across all the properties you mentioned to calculate the background of the row.

    Value Converter

    public class SampleConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var defaultBrush = Brushes.Transparent;
            if (value is SampleData data)
            {
                return 
                    data.FolderName == "Name 4" ? Brushes.Violet
                    : data.FolderCount > 1 && data.FolderName != "Name 3" ? Brushes.Red
                    : data.FolderStatus.Contains("3") ? Brushes.LightBlue
                    : defaultBrush;
            }
    
            return defaultBrush;
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
    

    xaml

    <DataGrid ItemsSource="{Binding Data}">
        <DataGrid.Resources>
            <local:SampleConverter x:Key="bgConverter" />
            <Style TargetType="DataGridCell">
                <Setter Property="Background"
                        Value="{Binding Converter={StaticResource bgConverter}}" />
            </Style>
        </DataGrid.Resources>
    </DataGrid>
    
    

    Code Behind

    public partial class MultiConditionFormatting : Window
    {
        public MultiConditionFormatting()
        {
            InitializeComponent();
            DataContext = this;
        }
    
        public List<SampleData> Data { get; set; } = new List<SampleData>
        {
            new SampleData { FolderCount = 1, FolderName = "Name 1", FolderStatus = "Status 1"},
            new SampleData { FolderCount = 3, FolderName = "Name 2", FolderStatus = "Status 2"},
            new SampleData { FolderCount = 3, FolderName = "Name 3", FolderStatus = "Status 3"},
            new SampleData { FolderCount = 3, FolderName = "Name 3", FolderStatus = "Status 4"},
            new SampleData { FolderCount = 4, FolderName = "Name 4", FolderStatus = "Status 4"},
        };
    }
    
    public class SampleData
    {
        public int FolderCount { get; set; }
        public string FolderName { get; set; }
        public string FolderStatus { get; set; }
    }
    

    enter image description here

    Option 2 - MultiDataTrigger

    If your use case only needs to check equality, you can use a MultiDataTrigger on your DataGridRow.

    This route may be easier and quicker for simple logic, but if things get complex it can get messy. Also, you should be aware that the conditions are prioritized bottom up, so the last condition of multiple conflicting conditions will win as shown below.

    <DataGrid ItemsSource="{Binding Data}">
        <DataGrid.RowStyle>
            <Style TargetType="DataGridRow">
                <Setter Property="Background" Value="Transparent" />
                <Style.Triggers>
                    <MultiDataTrigger>
                        <MultiDataTrigger.Conditions>
                            <Condition Binding="{Binding FolderCount}" Value="3" />
                        </MultiDataTrigger.Conditions>
                        <Setter Property="Background" Value="LightBlue" />
                    </MultiDataTrigger>
                    <MultiDataTrigger>
                        <MultiDataTrigger.Conditions>
                            <Condition Binding="{Binding FolderCount}" Value="3" />
                            <Condition Binding="{Binding FolderName}" Value="Name 3" />
                            <Condition Binding="{Binding FolderStatus}" Value="Status 3" />
                        </MultiDataTrigger.Conditions>
                        <Setter Property="Background" Value="Red" />
                    </MultiDataTrigger>
                </Style.Triggers>
            </Style>
        </DataGrid.RowStyle>
    </DataGrid>
    

    enter image description here