二重バッファリングについては、
ここと
ここでたくさん書かれてい
ます 。
ここで、
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
3で
PaintWithErrorHandlingメソッドが
呼び出され 、コントロール
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以外の場所ではチェックされないため、その目的は明確ではありません。