无法遍历C#DataGrid中的单元格来更改其背景颜色

时间:2012-07-18 07:49:59

标签: c# .net wpf data-binding datagrid

我想遍历DataGrid中的单元格,以便使用this code突出显示一些单元格:

//Loop through all rows.
for (int x = 0; x < yourGridName.Rows.Count; x++) {
  //Loop through all cells in that row and change its color.
  for (int y = 0; y < yourGridName.Rows[x].Cells.Count; y++) {
    yourGridName.Rows[x].Cells[y].Style.BackColor = System.Drawing.Color.Red;
  }
}

我在WPF中定义了我的DataGrid,如下所示:

<DataGrid AutoGenerateColumns="False"
  EnableRowVirtualization="True" ItemsSource="{Binding}"
  Margin="12,236,12,0" Name="conflictedDevicesDataGrid"
  RowDetailsVisibilityMode="VisibleWhenSelected" Grid.ColumnSpan="2"
  AlternatingRowBackground="#2FFF0000" IsManipulationEnabled="False">
  <DataGrid.Columns>
        ....
  </DataGrid.Columns>
</DataGrid>

然后我用这段代码填充它:

var conflictedDevicesDataTable = new DataTable();
conflictedDevicesDataTable.Rows.Add(new object[] {
  "CSV",roc.scadaNode, roc.deviceName, roc.deviceDescription, roc.rocChannel,
  roc.rocAddress, roc.rocGroup, roc.configuration, roc.revision
});
...
conflictedDevicesDataGrid.ItemsSource = conflictedDevicesDataTable.DefaultView;

然而,当我尝试通过:

遍历列和行时
conflictedDevicesDataGrid.Rows[x]

行不是项目,我可以遍历列而不是行。我在Google上找到的每个例子都说通过.Rows [x]迭代数据网格,但我似乎无法做到这一点。我怎样才能迭代我的DataGrid中的每个单元格并以编程方式更改背景颜色?

1 个答案:

答案 0 :(得分:1)

好的,所以你想要实现的目标在WPF中实际上非常困难,无论出于何种原因。

事实是,在WPF中,正如您所发现的那样,您无法迭代给定行的单元格。所以你有两个选择:

  • 迭代VisualTree中的单元格。我强烈反对这个选项,因为首先,如果你使用虚拟化,它会很糟糕,其次,它非常难看。

  • 在viewModel上设置一个特殊属性,并通过DataGrid.CellStyle将dataGridCell的background属性绑定到此属性。这里的问题是你无法直接绑定,因为DataGridCell的datacontext是与该单元所属的行对应的ViewModel(如图...),而不是该单元所代表的item属性。所以它变得相当复杂,因为你必须做一种“传递性”的招标。

我建议你看看这里:

Binding a cell object's property to a DataGridCell in WPF DataGrid

在这里:

How to style a WPF DataGridCell dynamically

这第二个链接解释了我如何做你想要达到的完全相同的事情,以及这种方法的缺点:缓慢......(阅读问题,而不是答案)

这样做,然后您可以迭代项目并在ViewModel中设置属性值。

编辑:这里有一些代码(基于您的问题)

    <local:CellViewModelToTagConverter x:Key="CellViewModelToTagConverter" />

    <DataGrid AutoGenerateColumns="False"
              EnableRowVirtualization="True"
              ItemsSource="{Binding}"
              Margin="12,236,12,0"
              Name="conflictedDevicesDataGrid"
              RowDetailsVisibilityMode="VisibleWhenSelected"
              Grid.ColumnSpan="2"
              AlternatingRowBackground="#2FFF0000"
              IsManipulationEnabled="False">
        <DataGrid.Columns>
            <!--binding to the Text property of the CellViewModel for the column n°0-->
            <DataGridTextColumn Binding="{Binding [0].Text}">
                <DataGridTextColumn.CellStyle>
                    <Style TargetType="DataGridCell">
                        <!--this part is the most important, this is where you transfer the right dataContext to the cell-->
                        <Setter Property="Tag">
                            <Setter.Value>
                                <MultiBinding Converter="{StaticResource CellViewModelToTagConverter}" Mode="OneWay" UpdateSourceTrigger="PropertyChanged">
                                    <Binding />
                                    <Binding RelativeSource="{x:Static RelativeSource.Self}"/>
                                </MultiBinding>
                            </Setter.Value>
                        </Setter>
                        <!--and here, you bind the Background property-->
                        <Setter Property="Background" Value="{Binding Tag.Background, RelativeSource={RelativeSource Self}, Mode=OneWay}" />
                    </Style>
                </DataGridTextColumn.CellStyle>
            </DataGridTextColumn>
        </DataGrid.Columns>
    </DataGrid>

这里是转换器的代码:

public class CellViewModelToTagConverter : MarkupExtension, IMultiValueConverter
{
    public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
    {
        var row = values[0] as MyRowViewModel;
        var cell = values[1] as DataGridCell;

        if (row != null && cell != null)
        {
            var column = cell.Column as DataGridColumn;

            if (column != null)
                cell.SetBinding(FrameworkElement.TagProperty, new Binding {
                    Source = row[column.DataGridOwner.Columns.IndexOf(column)],
                    BindsDirectlyToSource = true
                });
        }

        return DependencyProperty.UnsetValue;
    }

    public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }

    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        return new CellViewModelToTagConverter();
    }
}

当然,这意味着你有一个 MyRowViewModel

internal class MyRowViewModel : Collection<MyCellViewModel>, INotifyPropertyChanged
{
}

MyCellViewModel ,带有背景 dependencyProperty:

internal class MyCellViewModel : DependencyObject
{
private static readonly DependencyProperty BackgroundProperty = DependencyProperty.Register("Background", typeof(Brush), typeof(MyCellViewModel));
internal Brush Background
{
    get { return (Brush)(GetValue(BackgroundProperty)); }
    set { SetValue(BackgroundProperty, value); }
}
}

这应该可以解决问题(我希望我没有忘记任何事情,如果我这样做,你可以随时添加评论)

注意:这是我的代码的略微修改版本,因为我的应用程序要复杂得多,而且我没有像这样测试它,所以你可能需要做一些调整。另外,在我的情况下,我必须为每种类型的单元格都有一个CellViewModel,因为我还动态设置了Foreground,Font,FontSyle,fontWeight等...属性(顺便说一下,我的性能问题),但你可能没问题结构更简单。你只需要根据你的情况调整这个想法。