Search code examples
c#wpfdatagridforeground

Change datagrid row cell foreground colour dynamically wpf


In my WPF project I have a datagrid which has cells bound to different things. Here is the xaml:

<DataGrid x:Name="Tasks" CanUserDeleteRows="True">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Column1" Binding="{Binding C1}"/>
        <DataGridTextColumn Header="Column2" Binding="{Binding C2}"/>
        <DataGridTextColumn Header="Column3" Binding="{Binding C3}"/>
        <DataGridTextColumn Header="Column4" Binding="{Binding C4}"/>
        <DataGridTextColumn Header="Column5" Binding="{Binding C5}"/>
        <DataGridTextColumn Header="Column6" Binding="{Binding C6}"/>
        <DataGridTextColumn Header="Column7" Binding="{Binding C7}"/>
        <DataGridTextColumn Header="Column8" Binding="{Binding C8}"/>
    </DataGrid.Columns>
    <DataGrid.ContextMenu>
        <ContextMenu>
            <MenuItem Header="Add task" Click="ADDtask_Click" FontSize="11"></MenuItem>
        </ContextMenu>
    </DataGrid.ContextMenu>
</DataGrid>

This is my C# code:

public Guid 8 { get; }
public string 7 { get; set; }
public string 6 { get; set; }
public string 5 { get; set; }
public string 4 { get; set; }
public string 3 { get; set; }
public string 2 { get; set; }
public string 1 { get; set; }

public Result(string c1, string c2, string c3, string c4, string c5, string c6, string c7, string c8)
{
    this.Identifier = Guid.NewGuid();
    this.8 = c1;
    this.7 = c2;
    this.6 = c3;
    this.5 = c4;
    this.4 = c5;
    this.3 = c6;
    this.2 = c7;
    this.1 = c8;
}

private void Start_Task_Click(object sender, RoutedEventArgs e)
{
    System.Windows.Controls.Button button = (System.Windows.Controls.Button)sender;

    Result task = (Result)button.DataContext;

    if(...)
    {
        ????
    }
    else
    {
        return;
    }
}

public string idlestatus = "idle";

private void Button_Click1(object sender, RoutedEventArgs e) //this adds the columns to the datagrid
{
    Tasks.Items.Add(new Result(textbox1.Text, textbox2.Text, textbox3.Text, textbox4.Text, textbox5.Text, textbox6.Text, textbox7.Text, idlestatus));
}

I want to make an if statement like this:

if(task.c1 == "...") {
    cell.Style.Foreground.Color = ...
}

How would I be able to do this? I have tried doing something like this:

Tasks.CellStyle.Style.Foreground.Color

But it doesn't work, any help would be appreciated.


Solution

  • You have some options. Since you don't use auto generated columns you could use the ElementStyle property of the DataGridColumn:

    <DataGrid x:Name="Tasks">
      <DataGrid.Resources>
        <Style x:Key="ElementStyle" TargetType="TextBlock">
          <Style.Triggers>
            <Trigger Property="Text"
                     Value="Error Predicate Text">
              <Setter Property="Foreground"
                      Value="Red" />
            </Trigger>
    
            <Trigger Property="Text"
                     Value="Good Predicate Text">
              <Setter Property="Foreground"
                      Value="Green" />
            </Trigger>
          </Style.Triggers>
        </Style>
      </DataGrid.Resources>
    
      <DataGrid.Columns>
        <DataGridTextColumn Header="Column1"
                            Binding="{Binding C1}"
                            ElementStyle="{StaticResource ElementStyle}" />
        <DataGridTextColumn Header="Column2"
                            Binding="{Binding C2}"
                            ElementStyle="{StaticResource ElementStyle}" />
      </DataGrid.Columns>
    </DataGrid>
    

    Or use a DataTrigger:

    <DataGrid x:Name="Tasks">
      <DataGrid.Columns>
        <DataGridTextColumn Header="Column1"
                            Binding="{Binding C1}" />
        <DataGridTextColumn Header="Column2"
                            Binding="{Binding C2}" />
      </DataGrid.Columns>
      <DataGrid.CellStyle>
        <Style TargetType="DataGridCell">
          <Style.Triggers>
            <DataTrigger Binding="{Binding RelativeSource={RelativeSource Self}, Path=Content.Text}"
                         Value="Error Predicate Text" />
              <Setter Property="Foreground" Value="Red" />
            </DataTrigger>
          </Style.Triggers>
        </Style>
      </DataGrid.CellStyle>
    </DataGrid>
    

    When the trigger should be exclusive to a single cell of a specific column (and not the whole row based on a cell value), you have to use a MultiDataTrigger to check the current column header:

    <DataGrid x:Name="Tasks">
      <DataGrid.Columns>
        <DataGridTextColumn Header="Column1"
                            Binding="{Binding C1}" />
        <DataGridTextColumn Header="Column2"
                            Binding="{Binding C2}" />
      </DataGrid.Columns>
      <DataGrid.CellStyle>
        <Style TargetType="DataGridCell">
          <Style.Triggers>
            <MultiDataTrigger>
              <MultiDataTrigger.Conditions>
                <Condition Binding="{Binding RelativeSource={RelativeSource Self}, Path=Column.Header}"
                           Value="Column1" />
                <Condition Binding="{Binding C1}" Value="Error Predicate Text" />
              </MultiDataTrigger.Conditions>
              <Setter Property="Foreground" Value="Red" />
            </MultiDataTrigger>
          </Style.Triggers>
        </Style>
      </DataGrid.CellStyle>
    </DataGrid>
    

    Another solution is to use a IValueConverter (or a IMultiValueConverter for the same reason as above regarding the MultiDataTrigger):

    <DataGrid x:Name="Tasks">
      <DataGrid.Columns>
        <DataGridTextColumn Header="Column1"
                            Binding="{Binding C1}" />
        <DataGridTextColumn Header="Column2"
                            Binding="{Binding C2}" />
      </DataGrid.Columns>
      <DataGrid.CellStyle>
        <Style TargetType="DataGridCell">
          <Setter Property="Foreground">
            <Setter.Value>
              <MultiBinding>
                <MultiBinding.Converter>
                  <CellForegroundMultiValueConverter />
                </MultiBinding.Converter>
    
                <Binding RelativeSource="{RelativeSource Self}"
                         Path="Column.Header"/>
                <Binding />
                <Binding Path="HasChanges" />
              </MultiBinding>
            </Setter.Value>
          </Setter>
        </Style>
      </DataGrid.CellStyle>
    </DataGrid>
    

    CellForegroundMultiValueConverter.cs

    class CellForegroundMultiValueConverter : IMultiValueConverter
    {
      #region Implementation of IMultiValueConverter
    
      /// <inheritdoc />
      public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
      {
        var columnHeader = values[0] as string;
        var dataItem = values[1] as Result;
        return columnHeader.Equals("Column1", StringComparison.OrdinalIgnoreCase) 
               && dataItem.C1.Equals("....", StringComparison.OrdinalIgnoreCase)
          || columnHeader.Equals("Column2", StringComparison.OrdinalIgnoreCase) 
               && dataItem.C2.Equals("....", StringComparison.OrdinalIgnoreCase)
          ? Brushes.Red
          : Brushes.Black;
      }
    
      public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) => throw new NotSupportedException();
    
      #endregion
    }
    

    Result.cs

    class Result : INotifyPropertyChanged
    {
      private string c1;
      public string C1
      {
        get => this.c1;
        set
        {
          this.c1 = value;
          OnPropertyChanged();
          this.HasChanegs = true;
        }
      }
      private string c2;
      public string C2
      {
        get => this.c2;
        set
        {
          this.c2 = value;
          OnPropertyChanged();
          this.HasChanegs = true;
        }
      }
    
      private bool hasChanges;
      public string HasChanges
      {
        get => this.hasChanges;
        set
        {
          this.hasChanges = value;
          OnPropertyChanged();
         }
       }
    }