ボタンクリックなどのアクションのタイミングで画面に描画する (OnPaint イベントを利用) - ウィンドウのリサイズなどで描画内容が消えないようにする - C#

ボタンクリックなどのアクションのタイミングでOnPaintを利用して画面に描画するコードを紹介します。

概要

こちらの記事では、 CreateGraphicsを利用してボタンがクリックされたタイミングで、画面を描画するコードを紹介しました。 紹介したコードで画面の描画はできますが、ウィンドウをリサイズするなどして、描画されたエリアの一部が欠けた場合、再描画されないため、 描画した内容が失われてしまいます。
この記事では、OnPaintを利用して、描画した内容を保持するコードを紹介します。

位置やサイズが固定された図形を描画する場合

位置やサイズが固定された図形を描画する場合であれば、直接OnPaintイベントに図形を描画する処理を実装します。

プログラム例

こちらの記事で紹介した図形描画をOnPaintに実装します。

UI

下図のフォームを作成します。Panelコントロールを一つ配置します。

コード

以下のコードを記述します。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace OnPaintGraphicsDraw
{
  public partial class FormDrawFix : Form
  {
    public FormDrawFix()
    {
      InitializeComponent();
    }

    private void panel1_Paint(object sender, PaintEventArgs e)
    {
      Pen p1 = new Pen(Color.Red, 2);
      e.Graphics.DrawRectangle(p1, new Rectangle(64, 64, 256, 64));

      Pen p2 = new Pen(Color.Green, 2);
      e.Graphics.DrawRectangle(p2, new Rectangle(360, 24, 32, 128));

      Pen p3 = new Pen(Color.Blue, 1);
      e.Graphics.DrawLine(p3, new Point(24, 200), new Point(560, 32));
    }
  }
}

解説

OnPaintイベントに描画処理を記述します。GraphicsオブジェクトはPaintEventArgs オブジェクトのGraphicsオブジェクトを利用します。

実行結果

プロジェクトを実行します。下図のウィンドウが表示され、描画結果が表示されます。


OnPaintに処理を実装した場合は、リサイズしてウィンドウの描画結果の一部が欠けたのち、ウィンドウサイズを元の大きさに戻すと、 OnPaintが再実行され、欠けた部分が再描画されます。


ボタンクリックのタイミングで値を計算して描画する場合

固定された図形を描画する場合は上記の実装で問題ありませんが、 ボタン等のクリックのタイミングで描画する図形の座標や大きさを決定する場合はボタンのクリックのタイミングで描画する図形の情報を計算し、 描画する図形の情報を変数等に格納し、OnPaintイベントで描画する図形の情報を取り出して描画する必要があります。

プログラム例

ボタンをクリックしたタイミングで、乱数を使用して、矩形の座標を10個生成します。生成した座標の矩形をOnPatintイベントで画面に描画します。

UI

下図のフォームを作成します。PanelとButtonを1つずつ配置します。

コード

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace OnPaintGraphicsDraw
{
  public partial class FormDraw : Form
  {
    List<Rectangle> DrawList = new List<Rectangle>();

    public FormDraw()
    {
      InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
      for (int i = 0; i < 10; i++) {
        int x = new Random().Next(600);
        int y = new Random().Next(200);
        int rwidth = new Random().Next(128);
        int rheight = new Random().Next(48);
        DrawList.Add(new Rectangle(x, y, rwidth, rheight));
      }
      panel1.Invalidate();
    }

    private void panel1_Paint(object sender, PaintEventArgs e)
    {
      Pen p = new Pen(Color.Blue, 1);
      foreach (Rectangle r in DrawList) {
        e.Graphics.DrawRectangle(p, r);
      }
    }
  }
}

解説

button1をクリックすると下記のコードが実行されます。 forループで、Randomオブジェクトを利用して乱数を生成し、ランダムなRectangleの座標を10個作成します。 (乱数の生成の詳細はこちらの記事を参照してください。)
作成したRectangleオブジェクトをList<Rectangle>型のDrawListに挿入します。
  for (int i = 0; i < 10; i++) {
    int x = new Random().Next(600);
    int y = new Random().Next(200);
    int rwidth = new Random().Next(128);
    int rheight = new Random().Next(48);
    DrawList.Add(new Rectangle(x, y, rwidth, rheight));
  }

Invalidate メソッドを呼び出しPanelオブジェクトを更新します。 Invalidateメソッド自体はコントロールの領域を無効化するメソッドで、領域が無効化されることで、OnPaintが呼び出される動作になります。
  panel1.Invalidate();

PanelコントロールのOnPaintイベントのコードが下記です。
DrawListオブジェクトをforeachループでアクセスし、Rectangleオブジェクトを取り出し、長方形を描画します。 OnPaintメソッドに記述することで、リサイズなどでウィンドウの描画内容が消えない状態になります。
  private void panel1_Paint(object sender, PaintEventArgs e)
  {
    Pen p = new Pen(Color.Blue, 1);
    foreach (Rectangle r in DrawList) {
      e.Graphics.DrawRectangle(p, r);
    }
  }

実行結果

プロジェクトを実行します。下図のウィンドウが表示されます。


[button1]をクリックします。Panelに長方形が10個描画されます。


リサイズした場合の動作を確認します。


ウィンドウをリサイズし、小さくします。


再度ウィンドウを元のサイズにリサイズします。欠けた部分が再描画される動作が確認できます。

著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
掲載日: 2023-01-16
iPentec all rights reserverd.