【问题标题】:Change datagrid row cell foreground colour dynamically wpf动态更改datagrid行单元格前景色wpf
【发布时间】:2020-04-18 01:44:32
【问题描述】:

在我的 WPF 项目中,我有一个数据网格,其中包含绑定到不同事物的单元格。这是 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>

这是我的 C# 代码:

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));
}

我想做这样的 if 语句:

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

我怎么能做到这一点?我试过做这样的事情:

Tasks.CellStyle.Style.Foreground.Color

但它不起作用,任何帮助将不胜感激。

【问题讨论】:

标签: c# wpf datagrid foreground


【解决方案1】:

你有一些选择。由于您不使用自动生成的列,您可以使用DataGridColumnElementStyle 属性:

<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>

或者使用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>

当触发器应该专属于特定列的单个单元格(而不是基于单元格值的整行)时,您必须使用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">
      <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>

另一种解决方案是使用IValueConverter(或IMultiValueConverter,原因与上述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
}

结果.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();
     }
   }
}

【讨论】:

  • 这很好,但我如何能够在我的 c# 中使用 IMultiValueConverter 来更改前景色?
  • 我添加了IMultiValueConverter 的示例实现。我改进了最后一个示例的 XAML 代码(第二个 Binding 元素),它也使用了 Multibinding
  • 谢谢,它适用于当我添加一行包含该单元格文本时,有没有办法做到这一点,以便在文本发生变化时自动更新,可能类似于@ 987654337@?提前致谢!
  • 在不知道您的确切意思的情况下,我仍然确信这是可能的。请提供更多细节或尝试描述您的目标?我不知道你想去哪里。我们还在谈论这个比较dataItem.C1.Equals("....", StringComparison.OrdinalIgnoreCase)吗?您想要什么增强功能?
  • 我已经做到了,所以状态项是NotifyPropertyChanged 并且可以更改值,如果它从“zzz”更改为“xxx”,它不会改变颜色,有什么办法我可以使它在每次字符串更改时更新。谢谢。
猜你喜欢
  • 1970-01-01
  • 2021-03-10
  • 1970-01-01
  • 2018-09-26
  • 2020-03-27
  • 2015-05-14
  • 2013-01-28
  • 2020-06-02
  • 2011-08-17
相关资源
最近更新 更多