コントロールの表示がちらつく時は、コントロールのダブルバッファリングを有効にすると改善することがあります。ここでは、コントロールのダブルバッファリングを有効にする方法を紹介します。
なお、ダブルバッファリングにすればすべてのちらつきを解消できるという訳ではありません。ちらつきの原因には様々なものがあり、ダブルバッファリングでは解決できないものもあります。ダブルバッファリングへの過剰な期待は禁物と言えるでしょう。
補足:ちらつきを抑える効果のあるよく知られた方法の一つに、再描画を一時的に停止する方法もあります。この方法は、「コントロールの描画を一時的に停止する」で説明しています。
まずはダブルバッファリングについて簡単に説明します。
コントロールが描画される時、通常は、背景が描画されてから、前景が描画されます。この描画を直接画面に行うと、背景が描画された時に一瞬前景が消えるため、ちらついて見えてしまいます。
ダブルバッファリングを有効にすると、コントロールの描画はまずメモリバッファー(オフスクリーンバッファー)に行われます。そしてすべて描画し終えたところで、メモリバッファーの画像が画面にコピーされます。このようにダブルバッファリングでは画面に描画されるのは1度だけなので、ちらつきを抑えることができます。
ただしダブルバッファリングはそれだけメモリを消費しますので、環境や使い方などによってはパフォーマンスを低下させる可能性もあります。
より詳しい説明は、MSDNの「ダブル バッファリングされたグラフィックス」などをご覧ください。
補足:アプリケーションがリモートデスクトップで実行されている時はダブルバッファリングを無効にした方がよいという指摘が、「Horrible redraw performance of the DataGridView on one of my two screens」にあります。アプリケーションがリモートデスクトップで実行されているか調べる方法は、「リモートデスクトップのクライアントセッションで実行されているか調べる」で説明しています。
コントロールのダブルバッファリングを有効にするには、Control.DoubleBufferedプロパティをTrueにするだけでOKです。(ただし、Control.DoubleBufferedプロパティは.NET Framework 2.0以降でしか使用できません。)
しかしDoubleBufferedプロパティはPublicではなくProtectedですので、派生クラスからでなければアクセスできません。よって、DoubleBufferedプロパティをTrueにするために派生クラスを作成します。
以下に、ダブルバッファリングを有効にしたDataGridViewコントロールの例を示します。このコントロールの使い方については、「「○○○クラスの代わりに派生クラスを使用します」の意味は?」をご覧ください。
''' <summary> ''' ダブルバッファリングを有効にしたDataGridViewコントロール ''' </summary> Public Class DoubleBufferingDataGridView Inherits System.Windows.Forms.DataGridView Public Sub New() Me.DoubleBuffered = True End Sub End Class
/// <summary> /// ダブルバッファリングを有効にしたDataGridViewコントロール /// </summary> public class DoubleBufferingDataGridView : System.Windows.Forms.DataGridView { public DoubleBufferingDataGridView() { this.DoubleBuffered = true; } }
「隠蔽されている非パブリックメンバを呼び出す」で紹介している方法を使えば、Protectedプロパティにもアクセスできます。
この方法を使って、コントロールのDoubleBufferedプロパティをTrueにするメソッドの例を示します。
'Imports System.Reflection ''' <summary> ''' コントロールのDoubleBufferedプロパティをTrueにする ''' </summary> ''' <param name="control">対象のコントロール</param> Public Shared Sub EnableDoubleBuffering(control As Control) control.GetType().InvokeMember( _ "DoubleBuffered", _ BindingFlags.NonPublic Or BindingFlags.Instance Or BindingFlags.SetProperty, _ Nothing, _ control, _ New Object() {True}) End Sub
//using System.Reflection; /// <summary> /// コントロールのDoubleBufferedプロパティをTrueにする /// </summary> /// <param name="control">対象のコントロール</param> public static void EnableDoubleBuffering(Control control) { control.GetType().InvokeMember( "DoubleBuffered", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.SetProperty, null, control, new object[] { true }); }
Control.SetStyleメソッドを使ってControlStyles.DoubleBufferフラグをTrueにすることで、ダブルバッファリングを有効にすることもできます。DoubleBufferをTrueにするには、UserPaintとAllPaintingInWmPaintもTrueにする必要があります。
補足:.NET Framework 2.0以降では、OptimizedDoubleBufferとAllPaintingInWmPaintをTrueにすることによってもダブルバッファリングを有効にできます。
Control.SetStyleメソッドもProtectedですので、派生クラスを作って呼び出します。
ダブルバッファリングを有効にしたListViewコントロールの例を以下に示します。
'Imports System.Windows.Forms ''' <summary> ''' ダブルバッファリングを有効にしたListViewコントロール ''' </summary> Public Class DoubleBufferingListView Inherits ListView Public Sub New() 'ダブルバッファリングを有効にする Me.SetStyle( _ ControlStyles.DoubleBuffer Or _ ControlStyles.UserPaint Or _ ControlStyles.AllPaintingInWmPaint, _ True) End Sub End Class
//using System.Windows.Forms; /// <summary> /// ダブルバッファリングを有効にしたListViewコントロール /// </summary> public class DoubleBufferingListView : ListView { public DoubleBufferingListView() { //ダブルバッファリングを有効にする this.SetStyle( ControlStyles.DoubleBuffer | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true); } }
今まで紹介してきたようなことをしなくても、デフォルトでダブルバッファリングが有効になっている(Control.DoubleBufferedプロパティがTrueの)コントロールもあります。私が自分で調べた限り、デフォルトでダブルバッファリングが有効だったコントロールは以下の通りです。(もし他にありましたら、コメントで教えてください。)
「Flicker-free painting」や「How to fix the flickering in User controls」などによると、フォームに大量のコントロールを配置している時のちらつきには別の理由があり、上記の方法では解決できません。
フォームが描画される時、まずコントロールが配置されていない部分が描画されます。この時、コントロールが配置されている部分は白や黒の空白地帯として残ります。その後でコントロールが描画されますが、それまでの間コントロールが消えたようになるため、ちらついて見えます。
これを解決するには、フォームの拡張ウィンドウスタイル(Extended Window Styles)にWS_EX_COMPOSITEDを追加します。WS_EX_COMPOSITEDを追加すると、フォームとその子コントロールすべてがダブルバッファリングで描画されるようになります。WS_EX_COMPOSITEDは、Windows XP以降で有効です。
拡張ウィンドウスタイルにWS_EX_COMPOSITEDを追加したフォームの例を示します。このコードは、フォームクラス内に記述されているものとします。
'Imports System.Windows.Forms Protected Overrides ReadOnly Property CreateParams() As CreateParams Get Dim cp As CreateParams = MyBase.CreateParams '拡張ウィンドウスタイルにWS_EX_COMPOSITEDを追加する cp.ExStyle = cp.ExStyle Or &H2000000 Return cp End Get End Property
//using System.Windows.Forms; protected override CreateParams CreateParams { get { CreateParams cp = base.CreateParams; //拡張ウィンドウスタイルにWS_EX_COMPOSITEDを追加する cp.ExStyle |= 0x02000000; return cp; } }
しかしこのようにすると、一部のコントロールが正常に表示されなくなったり、最小化ボタンなどのアニメーションが無効になったり、Aeroを有効にしていると機能しなかったりなど、様々な問題があるようです。よって、できれば使用しない方が良いでしょう。詳しくは、前述したリンク先をご覧ください。