快速选择虚拟DataGridView(WinForms)中的所有单元格

时间:2013-03-06 08:02:55

标签: c# winforms datagridview virtual

我目前正在努力应对虚拟模式下WinForms DataGridView的性能。在某些情况下,我在DataGridView中有大小为2000x2000或甚至更大的矩阵。我已经设法提高自定义绘画,滚动等的整体性能,以满足我的需求。剩下的唯一一点就是选择所有细胞。对于我提到的大小的矩阵,大约需要10秒,这是绝对不可接受的。

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

必须将单元格的选择模式设置为CellSelect。我知道,这不是最好的虚拟数据网格,但这就是我们所需要的。我已经尝试将CTRL + A和鼠标向下连接到单元格(-1,-1)并将其中的选择模式设置为fullRowSelect,然后将事件重新路由到基类。但这也不会带来性能提升。

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

您是否有人可以选择所有单元格而不取消共享所有行或具有更好的性能?

[编辑] 我看到了与ListView相关的here描述的类似问题。现在我想知道DataGridView是否也有类似的解决方案?

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

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

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

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

1 个答案:

答案 0 :(得分:1)

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

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