OnPaintBackgroundとWM_ERASEBKGNDの動作の違い - C#

OnPaintBackgroundとWM_ERASEBKGNDの動作の違いを紹介します。

概要

コントロールの背景を初期化、描画するイベントとして、OnPaintBackgroundが用意されています。 また、ウィンドウの背景を消去する必要があるときに送信される、WM_ERASEBKGND ウィンドウメッセージがあります。 この2つの動作の違いを確認します。

プログラム1: OnPaintBackgroundでの背景の描画

UI

下図のフォームを作成します。フォームに"GraphicsControl"のウィンドウコントロールを配置します。

コード

コンポーネントを作成し、以下のコードを記述します。フォームのコードには追記しません。
GraphicsControl.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PaintBackgroundDemo
{
  public partial class GraphicsControl : Control
  {
    public GraphicsControl()
    {
      InitializeComponent();
    }

    public GraphicsControl(IContainer container)
    {
      container.Add(this);

      InitializeComponent();
    }

    protected override void OnPaint(PaintEventArgs e)
    {
      base.OnPaint(e);
    }

    protected override void OnPaintBackground(PaintEventArgs e)
    {
      base.OnPaintBackground(e);
      
      int r = new Random().Next(0, 256);
      SolidBrush b = new SolidBrush(Color.FromArgb(r, 0xC0, 0XC0));
      e.Graphics.FillRectangle(b, new Rectangle(0, 0, this.Width+2, this.Height+2));
    }
  }
}
GraphicsControl.Designer.cs
namespace PaintBackgroundDemo
{
  partial class GraphicsControl
  {
    /// <summary>
    /// 必要なデザイナー変数です。
    /// </summary>
    private System.ComponentModel.IContainer components = null;

    /// <summary> 
    /// 使用中のリソースをすべてクリーンアップします。
    /// </summary>
    /// <param name="disposing">マネージド リソースを破棄する場合は true を指定し、その他の場合は false を指定します。</param>
    protected override void Dispose(bool disposing)
    {
      if (disposing && (components != null)) {
        components.Dispose();
      }
      base.Dispose(disposing);
    }

    #region コンポーネント デザイナーで生成されたコード

    /// <summary>
    /// デザイナー サポートに必要なメソッドです。このメソッドの内容を
    /// コード エディターで変更しないでください。
    /// </summary>
    private void InitializeComponent()
    {
      components = new System.ComponentModel.Container();
    }

    #endregion
  }
}

解説

OnPaintBackgroundメソッドをオーバーライドし、以下のコードを記述します。
基底クラスのOnPaintBackgroundメソッドを呼び出したのち、コントロール前面を塗りつぶします。 このとき、塗りつぶす色をランダムで変化させて塗りつぶします。この処理により、描画するたびに色が変化します。
コントロールの全面が塗りつぶされるのであれば、全体の色が変わりますが、クリップされている状態であれば、 クリップされている内側のみが別の色で塗りつぶされる動作になります。
    protected override void OnPaintBackground(PaintEventArgs e)
    {
      base.OnPaintBackground(e);
      
      int r = new Random().Next(0, 256);
      SolidBrush b = new SolidBrush(Color.FromArgb(r, 0xC0, 0XC0));
      e.Graphics.FillRectangle(b, new Rectangle(0, 0, this.Width+2, this.Height+2));
    }

実行結果

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


ウィンドウをリサイズします。ウィンドウコントロールがリサイズされた部分の背景が別の色で描画されます。


背景の塗りつぶしはコントロール全体で描画していますが、リサイズにより拡張された領域のみ背景が別の色で描画されることから、 リサイズで拡張された部分のみが描画対象となるようクリップされている動作が確認できます。

プログラム2: WM_ERASEBKGND ウィンドウメッセージでの背景の描画

UI

下図のフォームを作成します。フォームに"GraphicsControl"のウィンドウコントロールを配置します。

コード

コンポーネントを作成し、以下のコードを記述します。フォームのコードには追記しません。
GraphicsControlWndProc.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace PaintBackgroundDemo
{
  public partial class GraphicsControlWndProc : Control
  {
    public const int WM_ERASEBKGND = 0x0014;

    public GraphicsControlWndProc()
    {
      InitializeComponent();
    }

    public GraphicsControlWndProc(IContainer container)
    {
      container.Add(this);

      InitializeComponent();
    }

    protected override void WndProc(ref Message message)
    {
      switch (message.Msg) {
        case WM_ERASEBKGND:
          WMEraseBkgnd(message.HWnd, message.LParam, message.WParam);
          break;
      }
      base.WndProc(ref message);
    }

    private void WMEraseBkgnd(nint HWnd, nint lparam, nint wparam)
    {
      Graphics g = this.CreateGraphics();

      int r = new Random().Next(0, 256);
      SolidBrush b = new SolidBrush(Color.FromArgb(r, 0xC0, 0XC0));
      g.FillRectangle(b, new Rectangle(0, 0, this.Width, this.Height));

      g.Dispose();
    }
  }
}
GraphicsControlWndProc.Designer.cs
namespace PaintBackgroundDemo
{
  partial class GraphicsControlWndProc
  {
    /// <summary>
    /// 必要なデザイナー変数です。
    /// </summary>
    private System.ComponentModel.IContainer components = null;

    /// <summary> 
    /// 使用中のリソースをすべてクリーンアップします。
    /// </summary>
    /// <param name="disposing">マネージド リソースを破棄する場合は true を指定し、その他の場合は false を指定します。</param>
    protected override void Dispose(bool disposing)
    {
      if (disposing && (components != null)) {
        components.Dispose();
      }
      base.Dispose(disposing);
    }

    #region コンポーネント デザイナーで生成されたコード

    /// <summary>
    /// デザイナー サポートに必要なメソッドです。このメソッドの内容を
    /// コード エディターで変更しないでください。
    /// </summary>
    private void InitializeComponent()
    {
      components = new System.ComponentModel.Container();
    }

    #endregion
  }
}

解説

WndProcメソッドをオーバーライドして、ウィンドウメッセージを受け取るメソッドを実装します。
今回は、WM_ERASEBKGND ウィンドウメッセージを受け取った場合に、WMEraseBkgnd メソッドを呼び出すロジックを実装します。
    protected override void WndProc(ref Message message)
    {
      switch (message.Msg) {
        case WM_ERASEBKGND:
          WMEraseBkgnd(message.HWnd, message.LParam, message.WParam);
          break;
      }
      base.WndProc(ref message);
    }

WMEraseBkgnd メソッドでは、コントロールの全面を塗りつぶします。塗りつぶす際の色を乱数で変化させます。 コントロールの全面が塗りつぶされるのであれば、全体の色が変わりますが、クリップされている状態であれば、 クリップされている内側のみが別の色で塗りつぶされる動作になります。
    private void WMEraseBkgnd(nint HWnd, nint lparam, nint wparam)
    {
      Graphics g = this.CreateGraphics();

      int r = new Random().Next(0, 256);
      SolidBrush b = new SolidBrush(Color.FromArgb(r, 0xC0, 0XC0));
      g.FillRectangle(b, new Rectangle(0, 0, this.Width, this.Height));

      g.Dispose();
    }

実行結果

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


ウィンドウをリサイズします。コントロールの背景色が変化します。


先のOnPaintBackgroundメソッドをオーバーライドした場合と動作が異なり、コントロールの全面がランダムのカラーで塗りつぶされる動作が確認できます。



OnPaintBackgroundとWM_ERASEBKGNDの動作の描画時の動作の違いを確認できました。

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