103

In my XAML code, I want to set the Background color of each row, based on a value of the object in one specific row. I have an ObservableCollection of z, and each of the z has a property called State. I started out with something like this in my DataGrid:

<DataGrid.RowStyle>
    <Style TargetType="DataGridRow">
        <Setter Property="Background" 
                Value="{Binding z.StateId, Converter={StaticResource StateIdToColorConverter}}"/>
     </Style>
</DataGrid.RowStyle>

This is a wrong approach because x is not a property in my ViewModel class.

In my ViewModel class I have an ObservableCollection<z> which is the ItemsSource of this DataGrid, and a SelectedItem of type z.

I could bind the color to SelectedItem, but this will only change one row in the DataGrid.

How can I, based on one property change this rows backgroundcolor?

wonea
  • 4,783
  • 17
  • 86
  • 139
Tobias Moe Thorstensen
  • 8,861
  • 16
  • 75
  • 143

4 Answers4

197

Use a DataTrigger:

<DataGrid ItemsSource="{Binding YourItemsSource}">
    <DataGrid.RowStyle>
        <Style TargetType="DataGridRow"> 
            <Style.Triggers>
                <DataTrigger Binding="{Binding State}" Value="State1">
                    <Setter Property="Background" Value="Red"></Setter>
                </DataTrigger>
                <DataTrigger Binding="{Binding State}" Value="State2">
                    <Setter Property="Background" Value="Green"></Setter>
                </DataTrigger>
            </Style.Triggers>
        </Style>
    </DataGrid.RowStyle>
</DataGrid>
archer
  • 262
  • 2
  • 15
Nitesh
  • 7,261
  • 3
  • 30
  • 25
  • 2
    I only get: BindingExpression path error: `'State' property not found on 'object' ''z' (HashCode=7162954)'. BindingExpression:Path=State; DataItem='z' (HashCode=7162954); target element is 'DataGridRow' (Name=''); target property is 'NoTarget' (type 'Object')` Howcome that it does not find the Property State when my entity holds this, and my database shows the State as a column? – Tobias Moe Thorstensen Aug 05 '13 at 08:22
  • 2
    I hope you are not doing it as `z.State`. – Nitesh Aug 05 '13 at 08:25
  • 4
    Just came across this again after time off from wpf, wish i could upvote again! – Ric Jul 25 '14 at 09:41
  • 7
    This is great. In the solution where I used it, I needed the state to change based on an `enum` value. [This answer on StackOverflow](http://stackoverflow.com/questions/13917033/datatrigger-on-enum-to-change-image) helped me with that. – kaspermoerch Jan 13 '15 at 07:48
  • Don't forget the property you bind to has to be `public` – CAD bloke Oct 19 '17 at 04:22
  • This is sweet, I had a hard time using Visual selection and then found this way of using XAML to format based on data. Thanks! – Oscar Ortiz Jan 15 '19 at 02:50
22

The same can be done without DataTrigger too:

 <DataGrid.RowStyle>
     <Style TargetType="DataGridRow">
         <Setter Property="Background" >
             <Setter.Value>
                 <Binding Path="State" Converter="{StaticResource BooleanToBrushConverter}">
                     <Binding.ConverterParameter>
                         <x:Array Type="SolidColorBrush">
                             <SolidColorBrush Color="{StaticResource RedColor}"/>
                             <SolidColorBrush Color="{StaticResource TransparentColor}"/>
                         </x:Array>
                     </Binding.ConverterParameter>
                 </Binding>
             </Setter.Value>
         </Setter>
     </Style>
 </DataGrid.RowStyle>

Where BooleanToBrushConverter is the following class:

public class BooleanToBrushConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value == null)
            return Brushes.Transparent;

        Brush[] brushes = parameter as Brush[];
        if (brushes == null)
            return Brushes.Transparent;

        bool isTrue;
        bool.TryParse(value.ToString(), out isTrue);

        if (isTrue)
        {
            var brush =  (SolidColorBrush)brushes[0];
            return brush ?? Brushes.Transparent;
        }
        else
        {
            var brush = (SolidColorBrush)brushes[1];
            return brush ?? Brushes.Transparent;
        }
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}
archer
  • 262
  • 2
  • 15
Vahagn Nahapetyan
  • 1,337
  • 13
  • 22
  • Even better is the application of a IMultiValueConverter (https://learn.microsoft.com/en-us/dotnet/api/system.windows.data.imultivalueconverter) to simply bind more than one property and have the converter return the correct color for the state of those multiple properties (I ommit the sample since its realy similar to the normal converter case but I can post it should anyone need it) – user8276908 Mar 18 '19 at 09:42
  • I use multivalue converter in that cases. One reason because I didn't know how to define an array as parameter of a converter, but also because if there is a multivalue converter, i guess it is for some reason. But why it is better a multivalue converter instead of this solution? – Álvaro García Mar 01 '22 at 09:13
14

In XAML, add and define a RowStyle Property for the DataGrid with a goal to set the Background of the Row, to the Color defined in my Employee Object.

<DataGrid AutoGenerateColumns="False" ItemsSource="EmployeeList">
   <DataGrid.RowStyle>
        <Style TargetType="DataGridRow">
             <Setter Property="Background" Value="{Binding ColorSet}"/>
        </Style>
   </DataGrid.RowStyle>

And in my Employee Class

public class Employee {

    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }

    public string ColorSet { get; set; }

    public Employee() { }

    public Employee(int id, string name, int age)
    {
        Id = id;
        Name = name;
        Age = age;
        if (Age > 50)
        {
            ColorSet = "Green";
        }
        else if (Age > 100)
        {
            ColorSet = "Red";
        }
        else
        {
            ColorSet = "White";
        }
    }
}

This way every Row of the DataGrid has the BackGround Color of the ColorSet Property of my Object.

ΩmegaMan
  • 29,542
  • 12
  • 100
  • 122
NickTheDev
  • 209
  • 4
  • 10
0

My solution:
XAML

<DataGrid.RowStyle>
  <Style TargetType="DataGridRow">
    <Setter Property="Background" >
      <Setter.Value>
        <Binding Path="State" Converter="{StaticResource BoolToSolidColorBrushConverter}">
          <Binding.ConverterParameter>
            <x:Array Type="SolidColorBrush">
              <SolidColorBrush Color="Salmon"/>
              <SolidColorBrush Color="Transparent"/>
            </x:Array>
          </Binding.ConverterParameter>
        </Binding>
      </Setter.Value>
    </Setter>
  </Style>
</DataGrid.RowStyle>

c# (BoolToSolidColorBrushConverter)

public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
  SolidColorBrush[] solidColorBrushes;

  if (parameter is SolidColorBrush[] && ((SolidColorBrush[])parameter).Length > 1)
  {
    solidColorBrushes = (SolidColorBrush[])parameter;
  }
  else
  {
    solidColorBrushes = new SolidColorBrush[] { new SolidColorBrush(Colors.Transparent), new SolidColorBrush(Colors.LightBlue) };
  }

  return (null == value || false == (bool)value) ? solidColorBrushes[1] : solidColorBrushes[0];
}