指定した範囲のみを再描画する - C#

コントロールの指定した範囲のみを再描画するコードを紹介します。

概要

コントロールで指定した範囲のみを明示的に再描画したい場合があります。 再描画したい領域を指定して更新する場合は、Invalidateメソッドを利用します。

書式

Invalidate([更新が必要な範囲のRectangleオブジェクト]);

プログラム例

UI

下図のフォームを作成します。ButtonとPanelを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 InvalidateDemo
{
  public partial class FormInvalidateArea : Form
  {
    private int[,] DrawCount;

    public FormInvalidateArea()
    {
      InitializeComponent();
    }

    private void panel1_Paint(object sender, PaintEventArgs e)
    {
      int rWidth = 32;
      int rHeiht = 32;

      Pen p = new Pen(Color.FromArgb(0x5d,0x86,0xba),1);
      SolidBrush b = new SolidBrush(Color.FromArgb(0xcd,0xdc,0xef));
      
      Font f = new Font("MS UI Gothic",10);
      SolidBrush bs = new SolidBrush(Color.FromArgb(0x00,0x44,0x99));

      for (int x = 0; x < 12; x++) {
        for (int y = 0; y < 12; y++) {
          Rectangle drawRect = new Rectangle(x*rWidth, y*rHeiht, rWidth, rHeiht);
          e.Graphics.FillRectangle(b, drawRect);
          e.Graphics.DrawRectangle(p, drawRect);

          DrawCount[x, y]++;
          e.Graphics.DrawString(Convert.ToString(DrawCount[x,y]),f,bs, new Point(x * rWidth+2, y * rHeiht+2));
        }
      }
    }

    private void FormInvalidateArea_Load(object sender, EventArgs e)
    {
      DrawCount = new int[12,12];
      for (int i = 0; i < 12; i++) {
        for (int j = 0; j < 12; j++) {
          DrawCount[i,j] = 0;
        }
      }
    }

    private void button1_Click(object sender, EventArgs e)
    {
      Rectangle r = new Rectangle(32 * 4, 32 * 4, 32*2, 32 * 2);
      panel1.Invalidate(r);
    }
  }
}

解説

2次元配列のDrawCount変数を用意し、Loadイベントで12x12の配列を作成し、0に初期化します。
    private int[,] DrawCount;
    private void FormInvalidateArea_Load(object sender, EventArgs e)
    {
      DrawCount = new int[12,12];
      for (int i = 0; i < 12; i++) {
        for (int j = 0; j < 12; j++) {
          DrawCount[i,j] = 0;
        }
      }
    }

32x32ピクセルの正方形を縦横に12個並べます。
内部にDrawCount の値を描画して表示します。Paintイベントが実行されるごとに、DrawCount 変数の値を増加させます。 初回の描画時には"1"が表示され、以後、再描画ごとに数値が増える動作になります。
    private void panel1_Paint(object sender, PaintEventArgs e)
    {
      int rWidth = 32;
      int rHeiht = 32;

      Pen p = new Pen(Color.FromArgb(0x5d,0x86,0xba),1);
      SolidBrush b = new SolidBrush(Color.FromArgb(0xcd,0xdc,0xef));
      
      Font f = new Font("MS UI Gothic",10);
      SolidBrush bs = new SolidBrush(Color.FromArgb(0x00,0x44,0x99));

      for (int x = 0; x < 12; x++) {
        for (int y = 0; y < 12; y++) {
          Rectangle drawRect = new Rectangle(x*rWidth, y*rHeiht, rWidth, rHeiht);
          e.Graphics.FillRectangle(b, drawRect);
          e.Graphics.DrawRectangle(p, drawRect);

          DrawCount[x, y]++;
          e.Graphics.DrawString(Convert.ToString(DrawCount[x,y]),f,bs, new Point(x * rWidth+2, y * rHeiht+2));
        }
      }
    }

ボタンをクリックすると、5行目、5列目のマスから縦横2マス分を再描画します。
再描画が実行されると、DrawCountの値が増加するため、再描画されたかが確認できます。
    private void button1_Click(object sender, EventArgs e)
    {
      Rectangle r = new Rectangle(32 * 4, 32 * 4, 32*2, 32 * 2);
      panel1.Invalidate(r);
    }

実行結果

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


[button1]をクリックします。5行目、5列目のマスから縦横2マス分が再描画され、マスの数値が2に変化することが確認できます。


続けてボタンをクリックすると数値が増加します。


指定した領域の明示的な再描画ができました。

プログラム例:クリップ状態を確認する

コードを変更してOnPaint時のクリップの状態を確認します。

UI

下図のフォームを作成します。ButtonとPanelを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 InvalidateDemo
{
  public partial class FormInvalidateAreaClip : Form
  {
    private int[,] DrawCount;

    public FormInvalidateAreaClip()
    {
      InitializeComponent();
    }

    private void panel1_Paint(object sender, PaintEventArgs e)
    {
      int rWidth = 32;
      int rHeiht = 32;

      Pen p = new Pen(Color.FromArgb(0x5d, 0x86, 0xba), 1);

      Font f = new Font("MS UI Gothic", 10);
      SolidBrush bs = new SolidBrush(Color.FromArgb(0x00, 0x44, 0x99));

      for (int x = 0; x < 12; x++) {
        for (int y = 0; y < 12; y++) {
          Rectangle drawRect = new Rectangle(x * rWidth, y * rHeiht, rWidth, rHeiht);

          DrawCount[x, y]++;
          int red = Math.Max(0xcd - DrawCount[x, y] * 16, 0);
          int green = Math.Max(0xdc - DrawCount[x, y] * 16, 0);
          int blue = 0xef;
          SolidBrush b = new SolidBrush(Color.FromArgb(red, green, blue));

          e.Graphics.FillRectangle(b, drawRect);
          e.Graphics.DrawRectangle(p, drawRect);

          e.Graphics.DrawString(Convert.ToString(DrawCount[x, y]), f, bs, new Point(x * rWidth + 2, y * rHeiht + 2));
        }
      }

    }

    private void FormInvalidateAreaClip_Load(object sender, EventArgs e)
    {
      DrawCount = new int[12, 12];
      for (int i = 0; i < 12; i++) {
        for (int j = 0; j < 12; j++) {
          DrawCount[i, j] = 0;
        }
      }
    }

    private void button1_Click(object sender, EventArgs e)
    {
      Rectangle r = new Rectangle(80,48,280,92);
      panel1.Invalidate(r);
    }
  }
}

解説

先のコードと同様ですが、FillRectangleメソッドで利用するブラシのカラーもDrawCountの値に応じて変化させます。
領域を指定して描画した場合に、どの部分が再描画されたかを確認できます。
    DrawCount[x, y]++;
    int red = Math.Max(0xcd - DrawCount[x, y] * 16, 0);
    int green = Math.Max(0xdc - DrawCount[x, y] * 16, 0);
    int blue = 0xef;
    SolidBrush b = new SolidBrush(Color.FromArgb(red, green, blue));

    e.Graphics.FillRectangle(b, drawRect);

[button1]をクリックすると、指定したRecangleの領域を更新します。
    private void button1_Click(object sender, EventArgs e)
    {
      Rectangle r = new Rectangle(80,48,280,92);
      panel1.Invalidate(r);
    }

実行結果

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


[button1]をクリックします。下図の表示に変化します。Invalidateメソッドに与えた領域の部分が更新されます。
OnPaintイベントではすべてのマスを描画するロジックですが、Invalidateメソッドに与えた領域のみが再描画されていることが確認できます。


ボタンを複数回クリックすると、指定した範囲の数値と背景色のみが変化します。Invalidateで範囲を指定した場合、OnPaintの描画時にクリップされる動作であることが確認できます。


著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用
最終更新日: 2023-01-25
作成日: 2023-01-22
iPentec all rights reserverd.