1

我目前正在为虚拟模式下 WinForms DataGridView 的性能而苦苦挣扎。在某些情况下,我在 DataGridView 中有大小为 2000x2000 甚至更大的矩阵。我已经设法根据我的需要提高了关于自定义绘画、滚动等的整体性能。剩下的唯一一点是所有单元格的选择。对于我提到的大小的矩阵,大约需要 10 秒,这是绝对不能接受的。

事实:行和列的 AutoSizeMode 设置为无。RowHeadersWidthSize 设置为仅可见行,columnsHeaderHeight 大小设置为禁用。我附加到 CellPainting 事件和 CellFormatting 事件。为了确保我在那里执行的操作不是性能问题的罪魁祸首,我暂时从那些没有成功的事件中分离出来。

必须将单元格的选择模式设置为 CellSelect。我知道,这对于虚拟数据网格来说并不是最好的,但这正是我们所需要的。在将事件重新路由到基类之前,我已经尝试附加到 CTRL + A 并将鼠标放在单元格(-1,-1)上并将其中的选择模式设置为 fullRowSelect。但这也不会带来性能提升。

我知道,如果一个单元格的绘画与相邻单元格不同,则整个行将不共享。这导致在选择所有单元格时所有行都变得不共享,我认为这是性能问题的罪魁祸首。

你们中是否有人可能有另一种方法来选择所有单元格而不取消共享所有行或具有更好的性能?

[编辑]我在这里看到了一个与ListView 相关的类似问题。现在我想知道 DataGridView 是否也可以使用类似的解决方案?

[编辑2]我刚刚意识到,选择所有单元格时的内存使用情况也大大增加(我认为这样做的原因是所有行分享)。例如,我有一个包含 1 列和 200 万行的大数据集。当 DataGrid 使用它的所有值建立时,应用程序使用大约300 MB的内存。现在选择所有单元格时,内存使用量增加到1.3 GB内存。

[当前解决方法]由于我还没有为给定问题找到合适的解决方案,因此我实施了一种解决方法来支持大矩阵的完整选择。目前,我覆盖了 Ctrl + A 的行为并单击左上角的单元格(-1,-1)。在那里我不对网格本身进行选择,而只是将单元格的背景颜色设置为用于选择的颜色。用户现在可以看到所有单元格都突出显示,就好像它们被选中一样。当我在虚拟模式下使用 DataGridView 并且选择在网格和基础数据结构之间同步时,我在基础数据中设置了一个特殊标志,即应选择所有内容。当用户现在单击任何其他单元格时,选择行为将重置为默认值,并且单元格的背景颜色也将重置。

在完全选择模式下,我还处理“GetClipboardContent”方法以确保将所有单元格值复制到剪贴板(目前这会导致另一个性能问题,但这是另一回事)。

尽管目前这是一个可行的解决方案,但我当然仍然会对基于 DataGridView 本身提供的功能提供解决方案的其他想法感兴趣。

4

1 回答 1

1

如果@Sinatr 建议的 BeginUpdate 和 EndUpdate 不起作用,我建议尝试使用此挂起/恢复 API 在单元格选择操作期间锁定显示:

try
{
    dataGridView1.SuspendDrawing()
    // Your cell selection operation
    ...
}
finally
{
    dataGridView1.ResumeDrawing()
}

我没有在你的场景中尝试过。但这对于需要频繁刷新的几个大 DataGridView 帮助很大。这就是为什么我认为它值得一试,因为它有可能在你的情况下让事情变得更快一些。

[DllImport("user32.dll", EntryPoint = "SendMessageA", ExactSpelling = true, 
                         CharSet = CharSet.Ansi, SetLastError = true)]
private static extern int SendMessage(IntPtr hwnd, int wMsg, int wParam, int lParam);
private const int WM_SETREDRAW = 0xB;

public static void SuspendDrawing(this Control target)
{
    SendMessage(target.Handle, WM_SETREDRAW, 0, 0);
}

public static void ResumeDrawing(this Control target) { ResumeDrawing(target, true); }
public static void ResumeDrawing(this Control target, bool redraw)
{
    SendMessage(target.Handle, WM_SETREDRAW, 1, 0);

    if (redraw)
    {
        target.Refresh();
    }
}
于 2013-03-06T08:50:10.593 回答