1

現在、仮想モードでの WinForms DataGridView のパフォーマンスに苦労しています。DataGridView 内に 2000x2000 またはそれ以上のサイズのマトリックスがあるシナリオがあります。カスタムペイント、スクロールなどに関する全体的なパフォーマンスを自分のニーズに合わせて向上させることができました。残っている唯一のポイントは、すべてのセルの選択です。私が言及したサイズの行列の場合、約 10 秒かかりますが、これは絶対に受け入れられません。

事実: 行と列の AutoSizeMode は none に設定されています。RowHeadersWidthSize は表示行のみに設定され、columnHeaderHeight サイズは無効に設定されています。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 に答える