【问题标题】:Update a DataGridView using LINQ使用 LINQ 更新 DataGridView
【发布时间】:2018-01-24 00:59:06
【问题描述】:

在回答this question的过程中,我写了一些代码来保存和恢复DataGridView中单元格的选择状态。这个想法是保存状态,然后在用户单击网格后重新应用它,以模仿 DataGridView 通常不提供的“粘性选择”功能。我写的解决方案很好用。

作为我自己的练习,我决定重写一些代码以使用 LINQ 和流畅的语法。另外,我认为流畅的语法看起来很漂亮:) 你可以让 DataGridView 与 LINQ with the Cast<> method 一起工作。我的第一份工作是保存选择状态:

之前:

static private bool[][] GetSelectionState(DataGridView input)
{
    int rowCount = input.Rows.Count;
    int columnCount = input.Columns.Count;
    var result = new bool[rowCount][];
    for (var r = 0; r < rowCount; r++)
    {
        result[r] = new bool[columnCount];
        for (var c = 0; c < columnCount; c++)
        {
            var cell = input.Rows[r].Cells[c];
            result[r][c] = cell.Selected;
        }
    }
    return result;
}

之后(使用 LINQ):

static private bool[][] GetSelectionState(DataGridView grid)
{
    return grid
        .Rows
        .Cast<DataGridViewRow>()
        .Select
        (
            r => r.Cells
                .Cast<DataGridViewCell>()
                .Select(c => c.Selected)
                .ToArray()
        )
        .ToArray();
}

那部分也很好用。

但我无法将状态应用回 DataGridView。我知道这并不完全符合 LINQ 的精神(我认为它更适用于函数式/不可变类型的编程方法)。但我想我可以通过使用Zip 将单元格与存储的选择状态对齐并通过简单的赋值操作来应用它来欺骗它。这是我想出的。

之前:

static private void SetSelectionState(DataGridView input, bool[][] selectionState)
{
    for (int r = 0; r <= selectionState.GetUpperBound(0); r++)
    {
        for (int c = 0; c <= selectionState[r].GetUpperBound(0); c++)
        {
            input.Rows[r].Cells[c].Selected = selectionState[r][c];
        }
    }
}

之后(使用 LINQ——这就是问题):

static private void SetSelectionState(DataGridView grid, IEnumerable<IEnumerable<bool>> selectionState)
{
    grid.Rows
        .Cast<DataGridViewRow>()
        .Zip
        (
            selectionState, 
            (row, rowState) => row
                .Cells
                .Cast<DataGridViewCell>()
                .Zip
                (
                    rowState, 
                    (cell, cellState) => cell.Selected = cellState 
                )
        )
        .ToList();  //Purge enumerator
}

^这会编译并运行,但完成后, Selected 每个单元格的属性都设置为 false。 em>

我也试过替换

.Zip
(
    rowState, 
    (cell, cellState) => cell.Selected = cellState
)

...与...

.Zip
(
    rowState, 
    (cell, cellState) => 
    {
        cell.Selected = cellState; 
        return cellState; 
    }
)

...还有...

.Zip
(
    rowState, 
    (cell, cellState) => 
    {
        grid.Rows[cell.RowIndex].Cells[cell.ColumnIndex].Selected = cellState;
        return cellState; 
    }
)

..但结果相同。

是否可以以这种方式使用 LINQ 从压缩的锯齿状数组中更新控件的属性?还是因为 LINQ 将所有内容都视为不可变,并复制所有内容,所以这是不可能的?是否有其他“LINQ-y”方法可以做到这一点?

【问题讨论】:

    标签: c# .net winforms linq


    【解决方案1】:

    问题在于,由于延迟执行,驱动内部 Zip 调用的枚举器没有被执行。我通过在每个结果集的末尾添加Last 解决了这个问题,确保枚举器将运行到它的结论,并解决了问题。

    static private void SetSelectionState(DataGridView grid, IEnumerable<IEnumerable<bool>> selectionState)
    {
        grid.Rows
            .Cast<DataGridViewRow>()
            .Zip
            (
                selectionState,
                (row, rowState) => row.Cells
                    .Cast<DataGridViewCell>()
                    .Zip
                    (
                        rowState,
                        (cell, cellState) => cell.Selected = cellState
                    )
                    .Last()
            )
            .Last();
    }
    

    【讨论】:

      【解决方案2】:

      我个人会留下 SetSelectionState 原样:两个嵌套的 for 循环。

      更清晰、更便携(Linq 是 .net 特有的,for 是通用的)

      如果我需要可枚举的版本,我会遍历数组,使用带有索引的 Select 和 SelectMany 重载,并使用这些索引来访问 DataGridView 单元格:

      private void SetSelectionState(DataGridView grid, bool[][] selectionState)
      {
          selectionState.SelectMany((bool[] array, int row) => 
                                    array.Select((bool state, int column) =>
                                                 grid[column, row].Selected = state))
                        .Last(); // forces projection
      }
      

      或使用 DataGridViewCell 的 RowIndex 和 ColumnIndex 属性

      private void SetSelectionState(DataGridView grid, bool[][] selectionState)
      {
          grid.Rows.OfType<DataGridViewRow>()
              .SelectMany(r => r.Cells.OfType<DataGridViewCell>())
              .Select(c => c.Selected = selectionState[c.RowIndex][c.ColumnIndex])
              .Last();
      }
      

      【讨论】:

        猜你喜欢
        • 2014-08-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-09-16
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多