简体   繁体   English

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

[英]Can't iterate through cells in a C# DataGrid to change their background colour

I want to iterate through the cells in my DataGrid so I can highlight some of the cells using this code : 我想遍历DataGrid中的单元格,以便可以使用以下代码突出显示某些单元格:

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

I have defined my DataGrid in WPF as follows: 我已经在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>

Then I populated it with this code: 然后用以下代码填充它:

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;

However when I try and iterate through the columns and rows via: 但是,当我尝试通过以下方式遍历列和行时:

conflictedDevicesDataGrid.Rows[x]

Rows is not an item, I can iterate through columns but not rows. 行不是项目,我可以遍历列,但不能遍历行。 Every example I find on Google says to iterate through a datagrid via .Rows[x] but I cannot seem to do this. 我在Google上找到的每个示例都说要通过.Rows [x]遍历数据网格,但我似乎无法做到这一点。 How else can I iterate through each cell in my DataGrid and programmatically change the background colour? 我还能如何遍历DataGrid中的每个单元并以编程方式更改背景色?

ok, so what you are trying to achieve is actually pretty difficult in WPF, for whatever reason. 好的,无论出于何种原因,在WPF中要实现的目标实际上都非常困难。

the thing is that in WPF, as you discovered, you can't iterate on the cells of a given Row. 事实是,正如您所发现的那样,在WPF中,您无法在给定Row的单元格上进行迭代。 So you have 2 options: 因此,您有2个选择:

  • iterate on the cells in the VisualTree. 迭代VisualTree中的单元格。 I strongly discourage this option because first, it will mess things up pretty badly if you're using virtualization, and second, it's very ugly. 我强烈不鼓励使用此选项,因为首先,如果使用虚拟化,它将使事情变得非常糟糕,其次,它非常丑陋。

  • set a special property on your viewModel, and bind the dataGridCell's background property to this one through the DataGrid.CellStyle. 在viewModel上设置一个特殊属性,然后通过DataGrid.CellStyle将dataGridCell的background属性绑定到该属性。 The problem here is that you can't bind directly, since a DataGridCell's datacontext is the ViewModel corresponding to the row to which the cell belongs (go figure...), and not the item's property represented by the cell. 这里的问题是您不能直接绑定,因为DataGridCell的数据上下文是与单元格所属行相对应的ViewModel(请参见图...),而不是单元格所代表的项目属性。 So it gets quite complicated, because you have to do kind of a "transitive" biding. 因此,它变得相当复杂,因为您必须进行“传递式”出价。

I suggest you have a look here: 我建议您在这里看看:

Binding a cell object's property to a DataGridCell in WPF DataGrid 将单元格对象的属性绑定到WPF DataGrid中的DataGridCell

and here: 和这里:

How to style a WPF DataGridCell dynamically 如何动态设置WPF DataGridCell的样式

this second link explains how I am doing the exact same thing you want to achieve, along with the drawback of such a method: slowness... (read the question, not the answer) 第二个链接说明了我如何做您想要实现的完全相同的事情,以及这种方法的缺点:缓慢...(阅读问题,而不是答案)

doing like this, you then can iterate on the items and set the property value in your ViewModel. 这样,您就可以迭代这些项目并在ViewModel中设置属性值。

edit: here's some code for you (based on your question) 编辑:这是一些适合您的代码(根据您的问题)

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

here is the converter's Code behind: 这是后面的转换器代码:

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

of course, this means you have a MyRowViewModel somewhere: 当然,这意味着您在某个地方有MyRowViewModel

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

and a MyCellViewModel with a Background dependencyProperty: 和具有背景依赖项属性的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); }
}
}

and this should do the trick (I hope I haven't forgotten anything, if I did you can always add a comment) 这应该可以解决问题(我希望我没有忘记任何事情,如果我愿意的话,您可以随时添加评论)

NB: this is a slightly modified version of my code of course, since my app is much more complex, and I did not test it like this so you might have to do some adjustments. 注意:这当然是我的代码的稍微修改后的版本,因为我的应用程序要复杂得多,并且我没有像这样测试它,因此您可能需要做一些调整。 Also, I have to have a CellViewModel for each type of cell in my case, since I also set the Foreground, Font, FontSyle, fontWeight etc... properties dynamically (hence my performance problems, by the way) but you might be ok with a simpler structure. 另外,在我的情况下,我必须为每种类型的单元格都具有一个CellViewModel,因为我还动态设置了Foreground,Font,FontSyle,fontWeight等属性(因此,我的性能出现问题),但是您可能还可以具有更简单的结构。 You just have to adapt the idea to your case. 您只需要使想法适应您的情况即可。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM