【问题标题】:Fast way to select all cells in virtual DataGridView (WinForms)在虚拟 DataGridView (WinForms) 中选择所有单元格的快速方法
【发布时间】:2013-03-06 08:02:54
【问题描述】:

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

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

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

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

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

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

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

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

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

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

【问题讨论】:

  • 您在使用BeginUpdate/EndUpdate 吗?由于某些原因,我决定在我的项目中使用ListView 的虚拟模式而不是DataGridView(已经忘记了原因,但我的内存中有一些与ctrl-a 相关的问题)。另一个提示:尽量减少您处理的项目数量,如果您仍然希望对所有项目进行操作,虚拟化将无济于事。尝试将事物组织成页面,使用过滤(数据库查询)等。
  • 嗨,Sinatr。感谢您的评论。但是WinForms的DataGridView没有BeginUpdate/EndUpdate功能。

标签: c# winforms datagridview virtual


【解决方案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();
    }
}

【讨论】:

  • 嗨洛朗。我还不知道这个 P/Invoke 方法。不幸的是,它不能解决我的场景中的性能问题。但在其他情况下它仍然可能对我有所帮助。谢谢。
猜你喜欢
  • 2013-06-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-01-02
  • 1970-01-01
  • 2018-10-12
相关资源
最近更新 更多