2つの画面のいずれかでのDataGridViewのひどい再描画パフォーマンス


81

私は実際にこれを解決しましたが、後世のために投稿しています。

デュアルモニターシステムのDataGridViewで非常に奇妙な問題が発生しました。この問題は、コントロールの再描画が非常に遅い(完全な再描画の場合は30秒など)として現れますが、それが私の画面の1つにある場合に限ります。一方、再描画速度は問題ありません。

私は最新の非ベータドライバー(175.何か)を搭載したNvidia 8800GTを持っています。ドライバーのバグですか?私はこの特定の構成で生きなければならないので、それを空中に残しておきます。(ただし、ATIカードでは発生しません...)

ペイント速度はセルの内容とは関係ありません。カスタム描画では、塗りつぶされた長方形をペイントするだけでも、パフォーマンスはまったく向上しません。

後で、(System.Windows.Forms.Integration名前空間からの)ElementHostをフォームに配置すると問題が修正されることがわかりました。それを台無しにする必要はありません。DataGridViewもオンになっているフォームの子である必要があります。Visibleプロパティがtrueである限り、サイズを(0、0)に変更できます。

.NET 3 /3.5の依存関係をアプリケーションに明示的に追加したくありません。リフレクションを使用して、実行時に(可能であれば)このコントロールを作成するメソッドを作成します。それは機能し、少なくとも必要なライブラリがないマシンでは正常に失敗します-ただ遅い状態に戻ります。

この方法では、アプリの実行中に修正を適用することもでき、フォームでWPFライブラリがどのように変更されているかを簡単に確認できます(Spy ++を使用)。

多くの試行錯誤の末、(フォームだけでなく)コントロール自体でダブルバッファリングを有効にすると問題が修正されることに気付きました。


したがって、DataGridViewに基づいてカスタムクラスを作成するだけで、DoubleBufferingを有効にできます。それでおしまい!

class CustomDataGridView: DataGridView
{
    public CustomDataGridView()
    {
        DoubleBuffered = true;
    }
}

グリッドのすべてのインスタンスがこのカスタムバージョンを使用している限り、すべて問題ありません。これが原因でサブクラスソリューションを使用できない状況に遭遇した場合(コードがない場合)、そのコントロールをフォームに挿入しようと試みることができると思います:)(私はリフレクションを使用して、DoubleBufferedプロパティを外部から強制的にオンにして、依存関係をもう一度回避しようとする可能性が高くなります)。

こんなに単純なことが私の時間の多くを食い尽くしたのは悲しいことです...


1
Multimonがインストールされているクライアントでも同様の問題が発生しました。何らかの理由で、Multimonをオフにすると、問題は解決します。
BlueRaja-Danny Pflughoeft 2012

これが発生する理由と、DoubleBufferedをデフォルトでオンにできない理由を知っている人は誰でも説明できますか?
VojtěchDohnal

回答:


64

DoubleBufferingを有効にできるように、DataGridViewに基づいてカスタムクラスを作成する必要があります。それでおしまい!


class CustomDataGridView: DataGridView
{
    public CustomDataGridView()
    {
        DoubleBuffered = true;
    } 
}

グリッドのすべてのインスタンスがこのカスタムバージョンを使用している限り、すべて問題ありません。これが原因でサブクラスソリューションを使用できない状況に遭遇した場合(コードがない場合)、そのコントロールをフォームに挿入しようと試みることができると思います:)(ただし、リフレクションを使用して、DoubleBufferedプロパティを外部から強制的にオンにして、依存関係をもう一度回避しようとする可能性が高くなります)。

こんなに単純なことが私の時間の多くを食い尽くしたのは悲しいことです...

注:質問を回答済みとしてマークできるように、回答を回答にする


1
WPF用のWindowsフォーム統合でこれをどのように行うことができますか?
部分的

答えてくれてありがとう。サブクラスソリューションを使用できない場合がありますか?(「コードがない場合」のビットがわかりませんでした)。
ダンW

素晴らしい!テーブルへの入力とスクロールの両方で奇妙な速度低下に悩まされていた私のプロジェクトの魅力のように機能します(:
knut 2015年

61

Benoitが示唆するようにサブクラス化せずに、リフレクションを使用してプロパティを設定するコードを次に示します。

typeof(DataGridView).InvokeMember(
   "DoubleBuffered", 
   BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.SetProperty,
   null, 
   myDataGridViewObject, 
   new object[] { true });

3
喜んでお手伝いします!この質問はすでに1年前だったので、ほとんど投稿しませんでした。
Brian Ensink 2009

1
いや、それは、Googleからこのスレッドを見つけたばかりの私など、将来誰かを常に助けてくれるでしょう。ありがとう!ところで、これをForm1_Loadセクションに入れるのが望ましいですか?
ダンW

2
これを見つけた他の誰かにアイデアを与えるためだけに:これはControlクラスの便利な拡張メソッドです。public static void ToggleDoubleBuffered(this Control control, bool isDoubleBuffered)
アンソニー

データグリッドビューが配置されるFOrmのロードイベントに配置できますか?
Arie

18

VB.NETでそれを行う方法を検索している人のために、ここにコードがあります:

DataGridView1.GetType.InvokeMember("DoubleBuffered", Reflection.BindingFlags.NonPublic Or Reflection.BindingFlags.Instance Or System.Reflection.BindingFlags.SetProperty, Nothing, DataGridView1, New Object() {True})

10

以前の投稿に加えて、Windowsフォームアプリケーションの場合、これはDataGridViewコンポーネントを高速化するために使用するものです。クラスDrawingControlのコードは以下のとおりです。

DrawingControl.SetDoubleBuffered(control)
DrawingControl.SuspendDrawing(control)
DrawingControl.ResumeDrawing(control)

コンストラクターのInitializeComponent()の後にDrawingControl.SetDoubleBuffered(control)を呼び出します。

ビッグデータの更新を行う前に、DrawingControl.SuspendDrawing(control)を呼び出します。

ビッグデータの更新を行った後、DrawingControl.ResumeDrawing(control)を呼び出します。

これらの最後の2つは、try / finallyブロックを使用して行うのが最適です。(または、クラスを次のように書き直して、コンストラクターとでIDisposable呼び出すことをお勧めします。)SuspendDrawing()ResumeDrawing()Dispose()

using System.Runtime.InteropServices;

public static class DrawingControl
{
    [DllImport("user32.dll")]
    public static extern int SendMessage(IntPtr hWnd, Int32 wMsg, bool wParam, Int32 lParam);

    private const int WM_SETREDRAW = 11;

    /// <summary>
    /// Some controls, such as the DataGridView, do not allow setting the DoubleBuffered property.
    /// It is set as a protected property. This method is a work-around to allow setting it.
    /// Call this in the constructor just after InitializeComponent().
    /// </summary>
    /// <param name="control">The Control on which to set DoubleBuffered to true.</param>
    public static void SetDoubleBuffered(Control control)
    {
        // if not remote desktop session then enable double-buffering optimization
        if (!System.Windows.Forms.SystemInformation.TerminalServerSession)
        {

            // set instance non-public property with name "DoubleBuffered" to true
            typeof(Control).InvokeMember("DoubleBuffered",
                                         System.Reflection.BindingFlags.SetProperty |
                                            System.Reflection.BindingFlags.Instance |
                                            System.Reflection.BindingFlags.NonPublic,
                                         null,
                                         control,
                                         new object[] { true });
        }
    }

    /// <summary>
    /// Suspend drawing updates for the specified control. After the control has been updated
    /// call DrawingControl.ResumeDrawing(Control control).
    /// </summary>
    /// <param name="control">The control to suspend draw updates on.</param>
    public static void SuspendDrawing(Control control)
    {
        SendMessage(control.Handle, WM_SETREDRAW, false, 0);
    }

    /// <summary>
    /// Resume drawing updates for the specified control.
    /// </summary>
    /// <param name="control">The control to resume draw updates on.</param>
    public static void ResumeDrawing(Control control)
    {
        SendMessage(control.Handle, WM_SETREDRAW, true, 0);
        control.Refresh();
    }
}

7

これに対する答えは私にも役立ちました。私は、ソリューションを実装するすべての人にとって標準的な方法であると思う改良を追加すると思いました。

このソリューションは、UIがリモートデスクトップでクライアントセッションとして実行されている場合、特に利用可能なネットワーク帯域幅が低い場合を除いて、うまく機能します。このような場合、ダブルバッファリングを使用するとパフォーマンスが低下する可能性があります。したがって、より完全な答えとして次のことをお勧めします。

class CustomDataGridView: DataGridView
{
    public CustomDataGridView()
    {
        // if not remote desktop session then enable double-buffering optimization
        if (!System.Windows.Forms.SystemInformation.TerminalServerSession)
            DoubleBuffered = true;
    } 
}

詳細については、リモートデスクトップ接続の検出を参照してください。


1

私はその問題の解決策を見つけました。詳細表示プロパティの[トラブルシューティング]タブに移動し、ハードウェアアクセラレーションスライダーを確認します。新しい会社のPCをITから入手したとき、フルから1ティックに設定されていて、データグリッドに問題はありませんでした。ビデオカードドライバを更新してフルに設定すると、データグリッドコントロールの描画が非常に遅くなりました。それで私はそれを元の場所にリセットし、問題は解決しました。

このトリックがあなたにも役立つことを願っています。


1

この問題を修正するために行ったことを追加するだけです。最新のNvidiaドライバーにアップグレードして問題を解決しました。コードを書き直す必要はありませんでした。

完全を期すために、カードは2008年3月付けのドライバーを搭載したNvidia Quadro NVS 290(v。169)でした。最新(2009年2月付けのv。182)にアップグレードすると、すべてのコントロール、特にDataGridViewのペイントイベントが大幅に改善されました。

この問題は、ATIカード(開発が行われている場所)では見られませんでした。


1

ベスト!:

Private Declare Function SendMessage Lib "user32" _
  Alias "SendMessageA" _
  (ByVal hWnd As Integer, ByVal wMsg As Integer, _
  ByVal wParam As Integer, ByRef lParam As Object) _
  As Integer

Const WM_SETREDRAW As Integer = &HB

Public Sub SuspendControl(this As Control)
    SendMessage(this.Handle, WM_SETREDRAW, 0, 0)
End Sub

Public Sub ResumeControl(this As Control)
    RedrawControl(this, True)
End Sub

Public Sub RedrawControl(this As Control, refresh As Boolean)
    SendMessage(this.Handle, WM_SETREDRAW, 1, 0)
    If refresh Then
        this.Refresh()
    End If
End Sub

0

デュアルモニターシステムで.NET3.0とDataGridViewを使用して同様の問題が発生しました。

このアプリケーションでは、背景が灰色のグリッドが表示され、セルを変更できなかったことを示します。「設定の変更」ボタンを選択すると、プログラムはセルの背景色を白に変更して、セルのテキストを変更できることをユーザーに示します。「キャンセル」ボタンは、前述のセルの背景色を灰色に戻します。

背景色が変更されると、ちらつきが発生します。これは、同じ行数と列数のデフォルトサイズのグリッドの短い印象です。この問題は、プライマリモニター(セカンダリではなく)でのみ発生し、単一のモニターシステムでは発生しません。

上記の例を使用して、コントロールをダブルバッファリングすることで、問題が解決しました。どうぞよろしくお願いいたします。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.