WPF数据网格:关于可见区域外选择单元格的虚拟化问题

3

我的DataGrid处理大量数据(平均高达40k行),因此我需要进行虚拟化处理。

有时,我需要选择某一列中的整个单元格(如果不是全部),以便集体更改它们的值。对于任何感兴趣的人,我通过处理对列标题的点击(通常是对列进行排序)并调用以下方法来实现这一点:

private void SelectColumn(object sender, DataGridSortingEventArgs e)
{
    if (MyDataGrid.SelectionUnit != DataGridSelectionUnit.FullRow)
    {
        DataGridColumn column = e.Column;

        if (e.Column != null)
        {
            MyDataGrid.UnselectAllCells();

            for (int i = 0; i < MyDataGrid.Items.Count; i++)
            {
                MyDataGrid.SelectedCells.Add(new DataGridCellInfo(MyDataGrid.Items[i], column));
            }

            // Set the first cell into editing mode
            MyDataGrid.CurrentCell = MyDataGrid.SelectedCells[0];
        }
    }
}

编辑:对不起,我差点忘记添加设置选定单元格的值的代码... :

private void MyDataGrid_CellEditEnding(object sender, DataGridCellEditEndingEventArgs e)
{
    if (MyDataGrid.SelectedCells.Count > 1)
    { // More than 1 cell are selected
        if (e.EditingElement.GetType() == typeof(TextBox))
        { // The cell being edited is of type TextBox
            string value = ((TextBox)e.EditingElement).Text;
            foreach (DataGridCellInfo cellInfo in MyDataGrid.SelectedCells)
            {
                DataGridCell gridCell = TryToFindGridCell(MyDataGrid, cellInfo);
                if (gridCell != null) gridCell.Content = value; // ((TextBox)e.EditingElement).Text returns the Text in the cell sending DataGridCellEditEndingEventArgs e
            }
        }
    }
}

static DataGridCell TryToFindGridCell(DataGrid grid, DataGridCellInfo cellInfo)
{
    DataGridCell result = null;
    DataGridRow row = (DataGridRow)grid.ItemContainerGenerator.ContainerFromItem(cellInfo.Item);
    if (row != null)
    {
        int columnIndex = grid.Columns.IndexOf(cellInfo.Column);
        if (columnIndex > -1)
        {
            DataGridCellsPresenter presenter = GetVisualChild<DataGridCellsPresenter>(row);
            result = presenter.ItemContainerGenerator.ContainerFromIndex(columnIndex) as DataGridCell;
        }
    }
    return result;
}

如果所有选定的单元格都在我的GUI的可见区域内,则此方法非常有效。但由于除了一些带缓冲区的行以外,其余内容都被虚拟化了,所以我遇到了问题。虚拟化的行实际上没有被选择,任何在可见区域之外的单元格都不会随着可见的单元格一起更改它们的值。

有没有人能指导我采取更好的方法?是的,我需要处理这么多的数据,抱歉。 ;)


你又来了。;o) - DHN
抱歉,哈哈。我认为现在关于WPF DataGrid的一半问题都是我的。 - i know nothing
我不了解虚拟化,但我相信如果你知道列,你可以得到一行中的单元格。你不能用这种方式获取所有单元格吗? - nakiya
这可能不是最好的建议,但是如果假设支持网格的数据是可观察的集合,直接获取编辑值并修改可观察的集合可能是有意义的。 - TYY
网格背后的数据是一个 DataTable。 - i know nothing
1个回答

1
我最近在使用WPF的数据表格时遇到了同样的问题,它太慢了。我认为这是因为数据表格行对象对于这么多数据来说太臃肿了。
在我的情况下,我需要处理非常相似数量的结果。我通过完全取消数据表格来解决了这个问题。我希望你的列数是恒定的,因为如果不是这种情况,实现起来可能会很麻烦(读作:我不确定该如何实现...反正...)。
创建一个类,并为数据表中的每一列创建一个属性。
class TableItem
{
    public string Column1 { get; set: }
    public string Column2 { get; set; }
}

无论您从哪里加载数据,都要遍历datatable / etc并将列添加到视图模型中的ObservableCollection对象中。
for (int = 0; i < yourDataTable.Rows.Count; i++)
{
    DataRow row = yourDataTable.Rows[i];
    TableItem ti = new TableItem 
    { 
        Column1 = row["Column1Name"].ToString(), 
        Column2 = row["Column2Name"].ToString()
    }
    yourObservableCollection.Add(ti);
}

ObservableCollectionInViewModel = yourObservableCollection;

创建一个 ListView,并为其设置所需的列数。将 ListView 绑定到刚刚创建的视图模型中的 ObservableCollection,然后使用 DisplayMemberBinding 将每个列绑定到 TableItem 对象的相应属性:

<ListView x:Name="lstGridWOResourceHogging" SelectionMode="Extended" ItemsSource="{Binding ObservableCollecitonInViewModel, IsAsync="True"
              VirtualizingPanel.IsVirtualizing="False">
        <ListView.View>
            <GridView>
                <GridViewColumn Header="Column1Name" DisplayMemberBinding="{Binding Column1}"/>
                <GridViewColumn Header="Column2Name" DisplayMemberBinding="{Binding Column2}"/>
            </GridView>
        </ListView.View>
    </ListView>

这样一来,你关闭了虚拟化,并且因为它不像数据网格的对象那么臃肿,性能会更好。现在,没有虚拟化,你选择的确实被选中了,你可以通过迭代进行操作。

仅供更新,ListView 更快了,但仍然不算“快”。当你需要成千上万个对象/依赖属性(数据行)时,WPF 的对象/依赖属性占用的内存等资源太多了。如果你真的需要速度快的话,Windows Forms Host 将有助于将 Windows Forms DataGridView 放入 WPF 应用程序中。 - Holonet

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接