Windows Formsダブルバッファリングの実装の詳細

二重バッファリングについては、 ここここでたくさん書かれています



ここで、 Java DBの実装方法を読むことができます。



ダブルバッファリングがC#でどのように実装されているかを説明します。 ここで書いたことの多くはMSDNで読むことができますが、実装の詳細はありません。



ダブルバッファリングの手動制御(以下DBと呼びます)



ダブルバッファリングを手動で制御するために、.NET Frameworkは次の3つのクラスを提供します。



BufferedGraphicsManager


BufferedGraphicsManagerクラスは、現在のアプリケーションドメイン( AppDomain )に関連付けられたBufferedGraphicsContextクラスのオブジェクトに( Current静的プロパティを介して)アクセスするために使用されます。 実際、 Currentは、静的コンストラクターで作成されたBufferedGraphicsContextクラスのオブジェクトを返します。 BufferedGraphicsManagerクラスのソースコードは次のとおりです。

public sealed class BufferedGraphicsManager { private static BufferedGraphicsContext bufferedGraphicsContext; public static BufferedGraphicsContext Current { get { return BufferedGraphicsManager.bufferedGraphicsContext; } } static BufferedGraphicsManager() { BufferedGraphicsManager.bufferedGraphicsContext = new BufferedGraphicsContext(); AppDomain.CurrentDomain.ProcessExit += new EventHandler(BufferedGraphicsManager.OnShutdown); AppDomain.CurrentDomain.DomainUnload += new EventHandler(BufferedGraphicsManager.OnShutdown); } private static void OnShutdown(object sender, EventArgs e) { BufferedGraphicsManager.Current.Invalidate(); } }
      
      





このコードは、現在の現在のアプリケーションドメインがアンロードされると、 BufferedGraphicsManager内に格納されているBufferedGraphicsContextクラスオブジェクトが破棄されることを示しています。



BufferedGraphicsContext


BufferedGraphicsContextは、 Graphicsオブジェクトに基づいたBufferedGraphicsの新しいインスタンスの作成(および破棄)を提供し、このための唯一のAllocateメソッドを提供します。

 public BufferedGraphics Allocate(Graphics targetGraphics, Rectangle targetRectangle) { if (targetRectangle.Width * targetRectangle.Height > this.MaximumBuffer.Width * this.MaximumBuffer.Height) return this.AllocBufferInTempManager(targetGraphics, IntPtr.Zero, targetRectangle); else return this.AllocBuffer(targetGraphics, IntPtr.Zero, targetRectangle); }
      
      



このメソッドは、パラメーターとしてGraphicsオブジェクトと、バッファーを作成する対象の領域を取ります。

この領域の領域がMaximumBufferプロパティで指定された領域を超えない場合、 AllocBufferメソッドが呼び出され 、そこから受信したBufferedGraphicsオブジェクトが返されます。 AllocBufferメソッドは、それ自体の内部で(以下で説明するCreateBufferメソッドを使用して)新しいオフスクリーングラフを作成し、それをBufferedGraphicsでラップし、 バッファーオブジェクト変数に保存して返します。 この変数は、 BufferedGraphicsContextのインスタンスを破棄するときに( Disposeメソッドを使用して)関連付けられたBufferedGraphicsインスタンスを後で破棄するために使用されます。

CreateBufferメソッドは、 Graphicsのオフスクリーン(つまり、画面に表示せずにメモリにのみ保存)インスタンスを作成します。 ネイティブのCreateDIBSection関数を使用して、「デバイスに依存しないビットマップ」(DIB)を作成し、それに基づいて新しいGraphicsオブジェクトを作成し、結果として返します。



転送された領域がMaximumBuffer領域を超える場合、 AllocBufferInTempManagerメソッドが呼び出されます 。そのソースコードは以下のとおりです。

 private BufferedGraphics AllocBufferInTempManager(Graphics targetGraphics, IntPtr targetDC, Rectangle targetRectangle) { //   ""  var bufferedGraphicsContext= new BufferedGraphicsContext(); //      (graphics),    ( context)  //     var bufferedGraphics = bufferedGraphicsContext.AllocBuffer(targetGraphics, targetDC, targetRectangle); // ,   ,   bufferedGraphics //        // bufferedGraphicsContext: bufferedGraphics.DisposeContext = true; return bufferedGraphics; }
      
      





このコードは、 AllocBufferInTempManagerメソッド内で、 AllocBufferメソッドを呼び出す新しいBufferedGraphicsContextインスタンスが作成され、そこから受信したBufferedGraphicsが結果として返されることを示しています。 さらに、作成された一時オブジェクトBufferedGraphicsContextはすぐには破棄されませんが、作成されたBufferedGraphicsが破棄されたときにのみ破棄されます。 これを行うために、 BufferedGraphicsは作成者へのバックリンクを保存し、破棄時にDisposeContextプロパティがtrueの場合 、それを自身で取得します。



バッファリングされたグラフィック


BufferedGraphicsクラスは非常に小さいです。 そのソースコードは100行強を占めています。 これは、 Graphicsオブジェクトの単純なラッパーであり、別のGraphicsにコピーするためのRenderメソッドを提供します。

 public void Render(Graphics target)
      
      





コピーは、ネイティブのBitBlt関数によって実行されます。



自動DB



DBをレンダリングコントロールに使用する最も簡単な方法は、目的のコントロールの自動DBを有効にすることです。

 control.DoubleBuffered = true;
      
      



または

 control.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
      
      





自動DBを有効にすると、コントロールがどうなるかを考えてください。 SetStyleメソッドと同様に、 DoubleBufferedプロパティはControlクラスにあります。 このクラスのソースコードを見てください。 DoubleBufferedプロパティコードは次のようになります。

  protected virtual bool DoubleBuffered { get { return this.GetStyle(ControlStyles.OptimizedDoubleBuffer); } set { if (value != this.DoubleBuffered) { if (value) this.SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.OptimizedDoubleBuffer, value); else this.SetStyle(ControlStyles.OptimizedDoubleBuffer, value); } } }
      
      





このコードスニペットからわかるように、DBを有効にする上記の2つの方法は、 ControlStyles.AllPaintingInWmPaintフラグもDoubleBufferedセッターで設定されていることを除いて、互いに違いはありません。 ただし、このフラグはコントロールコンストラクター1でも設定されるため、手動でリセットしなかった場合、これらのメソッドは両方とも同じ効果があります。

Controlクラスのソースコードから、 ControlStyles.AllPaintingInWmPaintフラグがプライベートWmEraseBkgndメソッド内でのみチェックされていること(およびDoubleBufferedプロパティのコンストラクターとセッターでのみ設定されていること)を確認できます 。 その実装は次のとおりです。

  private void WmEraseBkgnd(ref Message m) { if (this.GetStyle(ControlStyles.UserPaint) && !this.GetStyle(ControlStyles.AllPaintingInWmPaint)) { ... using (PaintEventArgs e = new PaintEventArgs(wparam, ClientRect)) this.PaintWithErrorHandling(e, (short) 1); } ... }
      
      





これは、 AllPaintingInWmPaintフラグ設定されていない場合、ウィンドウがWM_ERASEBKGNDメッセージを受信すると、 レイヤーパラメーター1 3PaintWithErrorHandlingメソッドが呼び出され 、コントロール4の背景が再描画れることを示しています。



ControlStyles.UserPaintフラグも検討する価値があります。 このフラグは、コントロールのコンテンツがシステムツールではなくFramework.NETツールを使用してレンダリングされることを示します。 たとえば、フォームに背景画像を設定し、 UserPaintフラグをクリアすると、画像は描画されません。



メインDBアクションは、 WmPaintメソッド内にデプロイされます。 このメソッドは、コントロールの一部を再描画する必要があるときに到着するWM_PAINTシステムメッセージを処理します。 WmPaintメソッドはプライベートであり、 ControlStyles.UserPaintフラグが設定されている場合、 WndProcメソッドからのみ呼び出すことができます。

 protected virtual void WndProc(ref Message m) { switch (m.Msg) { ... case WM_PAINT: if (this.GetStyle(ControlStyles.UserPaint)) this.WmPaint(ref m); break; ... } }
      
      





DB以外の詳細を省略すると、 WmPaintメソッドの実装は次のようになります。

 private void WmPaint(ref Message m) { if (this.DoubleBuffered || this.GetStyle(ControlStyles.AllPaintingInWmPaint) && this.DoubleBufferingEnabled) { IntPtr num; //   Graphics Rectangle rectangle; //    //  num  rectangle... if (rectangle.Width > 0 && rectangle.Height > 0) { Rectangle clientRectangle = this.ClientRectangle; using (BufferedGraphics bufferedGraphics = BufferedGraphicsManager.Current.Allocate(num, clientRectangle)) { Graphics graphics = bufferedGraphics.Graphics; graphics.SetClip(rectangle); System.Drawing.Drawing2D.GraphicsState gstate = graphics.Save(); using (PaintEventArgs e = new PaintEventArgs(graphics, rectangle)) { this.PaintWithErrorHandling(e, (short) 1, false); graphics.Restore(gstate); this.PaintWithErrorHandling(e, (short) 2, false); bufferedGraphics.Render(); } } } ... } else { //    ... } }
      
      





上記のコードスニペットからわかるように、DBでグラフィックを描画するには、 DoubleBufferedを trueにするか、 ControlStyles.AllPaintingInWmPaintフラグを設定する必要があります( DoubleBufferingEnabledは常にここで5であるため、考慮されません)。



次に、デフォルトのBufferedGrpahicsContextを使用して、グラフィックバッファーが作成されます。

そのため、レンダリング四角形は再描画が必要な領域に等しく設定され、現在の状態が保存されます。

その後、PaintWithErrorHandling 3メソッドを呼び出してOnBackgroundPaintおよびOnPaintメソッドが呼び出され、結果の画像が管理図にコピーされます。



自動バッファリングでわかるように、手動と同じDBメソッドが使用されます。










1. ControlStylesフラグが設定されているControlクラスのコンストラクターのソースコードのフラグメント:

  internal Control(bool autoInstallSyncContext) { ... this.SetStyle(ControlStyles.UserPaint | ControlStyles.StandardClick | ControlStyles.Selectable | ControlStyles.StandardDoubleClick | ControlStyles.AllPaintingInWmPaint | ControlStyles.UseTextForAccessibility, true); ... }
      
      





2.コントロールのサイズが変更されると、 WM_ERASEBKGNDメッセージが届きます。 WM_ERASEBKGNDメッセージが処理されるソースコードのフラグメント:

  protected virtual void WndProc(ref Message m) { switch (m.Msg) { ... case WM_ERASEBKGND: this.WmEraseBkgnd(ref m); break; ... } }
      
      





3. PaintWithErrorHandlingメソッドの実装:

 private void PaintWithErrorHandling(PaintEventArgs e, short layer) { ... switch (layer) { case (short) 1: if (!this.GetStyle(ControlStyles.Opaque)) this.OnPaintBackground(e); break; case (short) 2: this.OnPaint(e); break; } ... }
      
      





4. ControlStyles.Opaqueフラグが設定されている場合、コントロールの背景は再描画されません。



5.プライベートプロパティDoubleBufferingEnabledには次の実装があります。

  bool DoubleBufferingEnabled { private get { return this.GetStyle(ControlStyles.UserPaint | ControlStyles.DoubleBuffer); } }
      
      



WmPaintメソッドはControlStyles.UserPaintフラグが設定されている場合にのみ呼び出されるため、 ここではDoubleBufferingEnabledが常にtrueになります 。 また、閉じられており、 WmPaint以外の場所ではチェックされないため、その目的は明確ではありません。



All Articles