Windows フォーム コントロールのレンダリング

レンダリングとは、ユーザーの画面上に視覚的表現を作成するプロセスを指します。 Windows フォームでは、GDI (新しい Windows グラフィックス ライブラリ) を使用してレンダリングが行われます。 GDI へのアクセスを提供するマネージド クラスは、System.Drawing 名前空間とその副名前空間にあります。

コントロールのレンダリングには、次の要素が関係します。

  • 基底クラス System.Windows.Forms.Control によって提供される描画機能。

  • GDI グラフィックス ライブラリの必須要素。

  • 描画領域のジオメトリ。

  • グラフィックス リソースを解放する手順。

コントロールによって提供される描画機能

基底クラス ControlPaint イベントを通じて描画機能が提供されます。 表示を更新する必要があるたびに、コントロールによって Paint イベントが生成されます。 .NET Framework でのイベントの詳細については、「イベントの処理と発生」を参照してください。

Paint イベントのイベント データ クラスである PaintEventArgs により、コントロールの描画に必要なデータである、グラフィックス オブジェクトへのハンドルと、描画する領域を表す四角形オブジェクトが保持されます。 これらのオブジェクトは、次のコード フラグメントに太字で示されています。

Public Class PaintEventArgs  
   Inherits EventArgs  
   Implements IDisposable  
  
   Public ReadOnly Property ClipRectangle() As System.Drawing.Rectangle  
      ...  
   End Property  
  
   Public ReadOnly Property Graphics() As System.Drawing.Graphics  
      ...  
   End Property  
   ' Other properties and methods.  
   ...  
End Class  
public class PaintEventArgs : EventArgs, IDisposable {  
public System.Drawing.Rectangle ClipRectangle {get;}  
public System.Drawing.Graphics Graphics {get;}  
// Other properties and methods.  
...  
}  

Graphics は、描画機能をカプセル化するマネージド クラスです (このトピックの GDI に関する説明で後述します)。 ClipRectangleRectangle 構造体のインスタンスであり、コントロールで描画できる領域を定義します。 コントロール開発者は、コントロールの ClipRectangle プロパティを使用して、ClipRectangle を計算できます (このトピックのジオメトリに関する説明で後述します)。

コントロールは Control から継承する OnPaint メソッドをオーバーライドすることにより、レンダリング ロジックを提供する必要があります。 OnPaint は、渡された PaintEventArgs インスタンスの Graphics および ClipRectangle プロパティを通じて、グラフィックス オブジェクトと描画対象の四角形にアクセスします。

Protected Overridable Sub OnPaint(pe As PaintEventArgs)  
protected virtual void OnPaint(PaintEventArgs pe);  

基底クラス ControlOnPaint メソッドによって描画機能は実装されず、Paint イベントに登録されているイベント デリゲートを単に呼び出すだけです。 OnPaint をオーバーライドするときは、登録されているデリゲートで Paint イベントを受け取ることができるように、通常、基底クラスの OnPaint メソッドを呼び出す必要があります。 ただし、画面全体を描画するコントロールの場合は、基底クラスの OnPaint を呼び出すことはできません。これによって、ちらつきが生じるためです。 OnPaint イベントのオーバーライドの例については、「方法: 進行状況を示す Windows フォーム コントロールを作成する」を参照してください。

注意

コントロールから直接 OnPaint を呼び出さないでください。代わりに、Invalidate メソッド (Control から継承)、または Invalidate を呼び出す他のメソッドを呼び出します。 Invalidate メソッドによって、OnPaint が呼び出されます。 Invalidate メソッドはオーバーロードされ、Invalidatee に指定された引数に応じて、その画面領域の一部または全体がコントロールによって再描画されます。

基底クラス Control により、描画に便利なもう 1 つのメソッド OnPaintBackground が定義されています。

Protected Overridable Sub OnPaintBackground(pevent As PaintEventArgs)  
protected virtual void OnPaintBackground(PaintEventArgs pevent);  

OnPaintBackground を使用すると、ウィンドウの背景 (およびそのシェイプ) が描画され、高速な処理が保証されます。一方、OnPaint を使用すると、細部が描画され、個々の描画要求が、再描画の必要なすべての領域をカバーする 1 つの Paint イベントに結合されるため、処理が遅くなる場合があります。 たとえば、コントロールのグラデーション カラーの背景を描画する必要がある場合は、OnPaintBackground を呼び出す必要がある場合があります。

OnPaintBackground にはイベントのような命名法があり、OnPaint メソッドと同じ引数を受け取りますが、OnPaintBackground は本当のイベント メソッドではありません。 PaintBackground イベントは存在せず、OnPaintBackground でイベント デリゲートは呼び出されません。 OnPaintBackground メソッドをオーバーライドするとき、派生クラスで基底クラスの OnPaintBackground メソッドを呼び出す必要はありません。

GDI+ の基礎

Graphics クラスには、円、三角形、円弧、楕円などのさまざまなシェイプを描画するためのメソッドと、テキストを表示するためのメソッドが用意されています。 System.Drawing 名前空間とその副名前空間には、シェイプ (円、四角形、円弧など)、色、フォント、ブラシなどのグラフィックス要素をカプセル化する名前空間とクラスが含まれています。 GDI の詳細については、「マネージド グラフィックス クラスの使用」を参照してください。 GDI の基本事項については、「方法: 進行状況を示す Windows フォーム コントロールを作成する」でも説明しています。

描画領域のジオメトリ

コントロールの ClientRectangle プロパティにより、ユーザーの画面上でコントロールが使用できる四角形の領域が指定されています。一方、PaintEventArgsClipRectangle プロパティにより、実際に描画される領域が指定されます (描画は、PaintEventArgs インスタンスを引数として受け取る Paint イベント メソッドで行われることに注意してください)。 コントロールの表示の小さいセクションが変更された場合のように、コントロールで使用可能な領域の一部だけを描画することが必要になる場合があります。 そのような状況では、コントロール開発者は、実際に描画する四角形を計算し、それを Invalidate に渡す必要があります。 引数として Rectangle または Region を受け取る Invalidate のオーバーロードされたバージョンにより、その引数を使用して、PaintEventArgsClipRectangle プロパティが生成されます。

次のコード フラグメントは、FlashTrackBar カスタム コントロールで、描画する四角形の領域がどのように計算されるかを示しています。 client 変数は、ClipRectangle プロパティを表しています。 完全なサンプルについては、「方法: 進行状況を示す Windows フォーム コントロールを作成する」を参照してください。

Rectangle invalid = new Rectangle(
    client.X + min,
    client.Y,
    max - min,
    client.Height);

Invalidate(invalid);
Dim invalid As Rectangle = New Rectangle( _
    client.X + lmin, _
    client.Y, _
    lmax - lmin, _
    client.Height)

Invalidate(invalid)

グラフィックス リソースの解放

グラフィックス オブジェクトは、システム リソースを使用するため、コストが高くなります。 そのようなオブジェクトには、System.Drawing.Graphics クラスのインスタンスと、System.Drawing.BrushSystem.Drawing.Pen、およびその他のグラフィックス クラスのインスタンスが含まれます。 必要なときにだけグラフィックス リソースを作成し、使い終わったらすぐに解放することが重要です。 IDisposable インターフェイスが実装されている型を作成する場合は、終了したらその Dispose メソッドを呼び出してリソースを解放します。

次のコード フラグメントは、FlashTrackBar カスタム コントロールで、Brush リソースがどのように作成され、解放されるかを示しています。 完全なソース コードについては、「方法: 進行状況を示す Windows フォーム コントロールを作成する」を参照してください。

private Brush baseBackground = null;
Private baseBackground As Brush
base.OnPaint(e);
if (baseBackground == null) {
    if (showGradient) {
        baseBackground = new LinearGradientBrush(new Point(0, 0),
                                                 new Point(ClientSize.Width, 0),
                                                 StartColor,
                                                 EndColor);
    }
    else if (BackgroundImage != null) {
        baseBackground = new TextureBrush(BackgroundImage);
    }
    else {
        baseBackground = new SolidBrush(BackColor);
    }
}
MyBase.OnPaint(e)

If (baseBackground Is Nothing) Then

    If (myShowGradient) Then
        baseBackground = New LinearGradientBrush(New Point(0, 0), _
                                                 New Point(ClientSize.Width, 0), _
                                                 StartColor, _
                                                 EndColor)
    ElseIf (BackgroundImage IsNot Nothing) Then
        baseBackground = New TextureBrush(BackgroundImage)
    Else
        baseBackground = New SolidBrush(BackColor)
    End If

End If
protected override void OnResize(EventArgs e) {
    base.OnResize(e);
    if (baseBackground != null) {
        baseBackground.Dispose();
        baseBackground = null;
    }
}
Protected Overrides Sub OnResize(ByVal e As EventArgs)
    MyBase.OnResize(e)
    If (baseBackground IsNot Nothing) Then
        baseBackground.Dispose()
        baseBackground = Nothing
    End If
End Sub

関連項目