DOBON.NET

コントロールのダブルバッファリングを有効にして、ちらつきを防止する

コントロールの表示がちらつく時は、コントロールのダブルバッファリングを有効にすると改善することがあります。ここでは、コントロールのダブルバッファリングを有効にする方法を紹介します。

なお、ダブルバッファリングにすればすべてのちらつきを解消できるという訳ではありません。ちらつきの原因には様々なものがあり、ダブルバッファリングでは解決できないものもあります。ダブルバッファリングへの過剰な期待は禁物と言えるでしょう。

補足:ちらつきを抑える効果のあるよく知られた方法の一つに、再描画を一時的に停止する方法もあります。この方法は、「コントロールの描画を一時的に停止する」で説明しています。

ダブルバッファリングとは?

まずはダブルバッファリングについて簡単に説明します。

コントロールが描画される時、通常は、背景が描画されてから、前景が描画されます。この描画を直接画面に行うと、背景が描画された時に一瞬前景が消えるため、ちらついて見えてしまいます。

ダブルバッファリングを有効にすると、コントロールの描画はまずメモリバッファー(オフスクリーンバッファー)に行われます。そしてすべて描画し終えたところで、メモリバッファーの画像が画面にコピーされます。このようにダブルバッファリングでは画面に描画されるのは1度だけなので、ちらつきを抑えることができます。

ただしダブルバッファリングはそれだけメモリを消費しますので、環境や使い方などによってはパフォーマンスを低下させる可能性もあります。

より詳しい説明は、MSDNの「ダブル バッファリングされたグラフィックス」などをご覧ください。

補足:アプリケーションがリモートデスクトップで実行されている時はダブルバッファリングを無効にした方がよいという指摘が、「Horrible redraw performance of the DataGridView on one of my two screens」にあります。アプリケーションがリモートデスクトップで実行されているか調べる方法は、「リモートデスクトップのクライアントセッションで実行されているか調べる」で説明しています。

Control.DoubleBufferedプロパティを使用する方法

コントロールのダブルバッファリングを有効にするには、Control.DoubleBufferedプロパティをTrueにするだけでOKです。(ただし、Control.DoubleBufferedプロパティは.NET Framework 2.0以降でしか使用できません。)

しかしDoubleBufferedプロパティはPublicではなくProtectedですので、派生クラスからでなければアクセスできません。よって、DoubleBufferedプロパティをTrueにするために派生クラスを作成します。

以下に、ダブルバッファリングを有効にしたDataGridViewコントロールの例を示します。このコントロールの使い方については、「「○○○クラスの代わりに派生クラスを使用します」の意味は?」をご覧ください。

VB.NET
コードを隠すコードを選択
''' <summary>
''' ダブルバッファリングを有効にしたDataGridViewコントロール
''' </summary>
Public Class DoubleBufferingDataGridView
    Inherits System.Windows.Forms.DataGridView
    Public Sub New()
        Me.DoubleBuffered = True
    End Sub
End Class
C#
コードを隠すコードを選択
/// <summary>
/// ダブルバッファリングを有効にしたDataGridViewコントロール
/// </summary>
public class DoubleBufferingDataGridView : System.Windows.Forms.DataGridView
{
    public DoubleBufferingDataGridView()
    {
        this.DoubleBuffered = true;
    }
}

リフレクションを使って、DoubleBufferedプロパティにアクセスする

隠蔽されている非パブリックメンバを呼び出す」で紹介している方法を使えば、Protectedプロパティにもアクセスできます。

この方法を使って、コントロールのDoubleBufferedプロパティをTrueにするメソッドの例を示します。

VB.NET
コードを隠すコードを選択
'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
C#
コードを隠すコードを選択
//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 });
}

ControlStyles.DoubleBufferフラグをTrueにする方法

Control.SetStyleメソッドを使ってControlStyles.DoubleBufferフラグをTrueにすることで、ダブルバッファリングを有効にすることもできます。DoubleBufferをTrueにするには、UserPaintとAllPaintingInWmPaintもTrueにする必要があります。

補足:.NET Framework 2.0以降では、OptimizedDoubleBufferとAllPaintingInWmPaintをTrueにすることによってもダブルバッファリングを有効にできます。

Control.SetStyleメソッドもProtectedですので、派生クラスを作って呼び出します。

ダブルバッファリングを有効にしたListViewコントロールの例を以下に示します。

VB.NET
コードを隠すコードを選択
'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
C#
コードを隠すコードを選択
//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の)コントロールもあります。私が自分で調べた限り、デフォルトでダブルバッファリングが有効だったコントロールは以下の通りです。(もし他にありましたら、コメントで教えてください。)

  • PictureBoxコントロール
  • Labelコントロール
  • LinkLabelコントロール
  • SplitContainerコントロール
  • PrintPreviewControlコントロール
  • ToolStripコントロール

WS_EX_COMPOSITEDを使用する方法

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を追加したフォームの例を示します。このコードは、フォームクラス内に記述されているものとします。

VB.NET
コードを隠すコードを選択
'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
C#
コードを隠すコードを選択
//using System.Windows.Forms;

protected override CreateParams CreateParams
{
    get
    {
        CreateParams cp = base.CreateParams;
        //拡張ウィンドウスタイルにWS_EX_COMPOSITEDを追加する
        cp.ExStyle |= 0x02000000;
        return cp;
    }
} 

しかしこのようにすると、一部のコントロールが正常に表示されなくなったり、最小化ボタンなどのアニメーションが無効になったり、Aeroを有効にしていると機能しなかったりなど、様々な問題があるようです。よって、できれば使用しない方が良いでしょう。詳しくは、前述したリンク先をご覧ください。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • コードの先頭に記述されている「Imports ??? がソースファイルの一番上に書かれているものとする」(C#では、「using ???; がソースファイルの一番上に書かれているものとする」)の意味が分からないという方は、こちらをご覧ください。
  • .NET Tipsをご利用いただく際は、注意事項をお守りください。
共有する

この記事への評価

この記事へのコメント

この記事に関するコメントを投稿するには、下のボタンをクリックしてください。投稿フォームへ移動します。通常のご質問、ご意見等は掲示板へご投稿ください。