コントロールのペインティングと描画 (.NET 用の Windows フォーム)

コントロールのカスタム ペインティングは、Windows フォームによって簡単に実行できる多数の複雑なタスクの 1 つです。 カスタム コントロールを作成する場合、コントロールのグラフィカルな外観を処理するために使用できる多くのオプションがあります。 カスタム コントロール、つまり Control から継承するコントロールを作成する場合は、そのグラフィカル表現をレンダリングするコードを用意する必要があります。

重要

.NET 7 と .NET 6 用のデスクトップ ガイド ドキュメントは作成中です。

複合コントロール、つまり UserControl または既存の Windows フォーム コントロールのいずれかを継承するコントロールを作成する場合は、標準のグラフィカル表現をオーバーライドし、独自のグラフィックス コードを提供することができます。

新しいコントロールを作成せずに既存のコントロールのカスタム レンダリングを提供する場合は、オプションがいっそう限られたものになります。 ただし、それでもコントロールとアプリケーションに対して、広い範囲のグラフィカルの可能性があります。

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

  • 基底クラス System.Windows.Forms.Control によって提供される描画機能。
  • GDI グラフィックス ライブラリの必須要素。
  • 描画領域のジオメトリ。
  • グラフィックス リソースを解放する手順。

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

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

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

public class PaintEventArgs : EventArgs, IDisposable
{

    public System.Drawing.Rectangle ClipRectangle {get;}
    public System.Drawing.Graphics Graphics {get;}

    // Other properties and methods.
}
Public Class PaintEventArgs
    Inherits EventArgs
    Implements IDisposable

    Public ReadOnly Property ClipRectangle As System.Drawing.Rectangle
    Public ReadOnly Property Graphics As System.Drawing.Graphics

    ' Other properties and methods.
End Class

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

OnPaint

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

次のコードでは、System.Drawing 名前空間を使用しています。

protected override void OnPaint(PaintEventArgs e)
{
    // Call the OnPaint method of the base class.
    base.OnPaint(e);

    // Declare and instantiate a new pen that will be disposed of at the end of the method.
    using var myPen = new Pen(Color.Aqua);

    // Create a rectangle that represents the size of the control, minus 1 pixel.
    var area = new Rectangle(new Point(0, 0), new Size(this.Size.Width - 1, this.Size.Height - 1));

    // Draw an aqua rectangle in the rectangle represented by the control.
    e.Graphics.DrawRectangle(myPen, area);
}
Protected Overrides Sub OnPaint(e As PaintEventArgs)
    MyBase.OnPaint(e)

    ' Declare and instantiate a drawing pen.
    Using myPen = New System.Drawing.Pen(Color.Aqua)

        ' Create a rectangle that represents the size of the control, minus 1 pixel.
        Dim area = New Rectangle(New Point(0, 0), New Size(Me.Size.Width - 1, Me.Size.Height - 1))

        ' Draw an aqua rectangle in the rectangle represented by the control.
        e.Graphics.DrawRectangle(myPen, area)

    End Using
End Sub

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

注意

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

コントロールが最初に描画されるときと、更新されるたびに、コントロールの OnPaint メソッドのコードが実行されます。 サイズが変更されるたびにコントロールが確実に再描画されるようにするには、コントロールのコンストラクターに次の行を追加します。

SetStyle(ControlStyles.ResizeRedraw, true);
SetStyle(ControlStyles.ResizeRedraw, True)

OnPaintBackground

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

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

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

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

GDI+ の基礎

Graphics クラスには、円、三角形、円弧、楕円などのさまざまなシェイプを描画するためのメソッドと、テキストを表示するためのメソッドが用意されています。 System.Drawing 名前空間には、シェイプ (円、四角形、円弧など)、色、フォント、ブラシなどのグラフィックス要素をカプセル化する名前空間とクラスが含まれています。

描画領域のジオメトリ

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

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

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

関連項目