【问题标题】:Can't iterate through cells in a C# DataGrid to change their background colour无法遍历 C# DataGrid 中的单元格以更改其背景颜色
【发布时间】:2012-07-17 05:00:46
【问题描述】:

我想遍历我的 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 中的每个单元格并以编程方式更改背景颜色?

【问题讨论】:

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


    【解决方案1】:

    好的,无论出于何种原因,您想要在 WPF 中实现的目标实际上是相当困难的。

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

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

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

    我建议你看看这里:

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

    和一个带有 Background 依赖属性的 MyCellViewModel

    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 等...属性(因此我的性能问题,顺便说一句)但你可能没问题具有更简单的结构。你只需要根据你的情况调整这个想法。

    【讨论】:

    • 这看起来非常有用,您有完整的示例源吗?
    • 我会的,我会在今天晚些时候发布
    • 我读了很多,但我不太清楚如何实现 MyRowViewModel 的 INotifyPropertyChanged。它到底应该做什么以及我应该如何实施它?我的行由 8 个 DataGridTextColumn 和 1 个 DataGridCheckBoxColumn 组成。我也缺少 XwpfFont 和 XwpfFonBrusht 并且在任何地方都找不到对它们的引用,这些只是用于颜色更改方法的 Brush 吗?
    • 我用 XwpfThings 编辑了错字,抱歉?至于 INotifyPropertyChanged,我真的无能为力,因为我倾向于不使用此方法,而是使用 DependencyObjects
    猜你喜欢
    • 1970-01-01
    • 2018-09-26
    • 1970-01-01
    • 2011-09-12
    • 2013-01-28
    • 2011-04-03
    • 2015-05-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多