ウィンドウコントロールでスクロールインした領域を描画する - C#

ウィンドウコントロールでスクロールインした領域を描画するコードを紹介します。

概要

こちらの記事ではウィンドウコントロールで描画された内容をスクロールするコードを紹介しましたが、 すでに描画された内容はスクロールしますが、画面の外側からスクロールインしたコンテンツは描画されません。
この記事では、画面の外側からスクロールインしたコンテンツを描画するコードを紹介します。

プログラム

Windows Formアプリケーションを作成します。

UI

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

コード

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

namespace ScrollWindowDemo
{
  public partial class FormSimpleScrollDraw : Form
  {
    public const uint SW_SCROLLCHILDREN = 0x0001;
    public const uint SW_INVALIDATE = 0x0002;
    public const uint SW_ERASE = 0x0004;
    public const uint SW_SMOOTHSCROLL = 0x0010;

    [StructLayout(LayoutKind.Sequential)]
    public struct RECT
    {
      public int _Left;
      public int _Top;
      public int _Right;
      public int _Bottom;
    }

    //[DllImport("user32.dll")]
    //static extern int ScrollWindowEx(IntPtr hWnd, int dx, int dy, IntPtr prcScroll, IntPtr prcClip, IntPtr hrgnUpdate, IntPtr prcUpdate, uint flags);

    [DllImport("user32.dll")]
    static extern int ScrollWindowEx(IntPtr hWnd, int dx, int dy, ref RECT prcScroll, ref RECT prcClip, IntPtr hrgnUpdate, out RECT prcUpdate, uint flags);

    public Point sc = new Point(0,0);

    public FormSimpleScrollDraw()
    {
      InitializeComponent();
    }

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

      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));

      int idx = 0;
      for (int y = 0; y < 12; y++) {
        for (int x = 0; x < 12; x++) {
          Rectangle drawRect = new Rectangle(x * rWidth + sc.X, y * rHeiht + sc.Y, rWidth, rHeiht);
          if (e.ClipRectangle.IntersectsWith(drawRect) == true) {

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

            e.Graphics.DrawString(Convert.ToString(idx), f, bs, new Point(x * rWidth + sc.X + 2, y * rHeiht + sc.Y + 2));
          }
          idx++;
        }
      }

    }

    private void button1_Click(object sender, EventArgs e)
    {
      PanelScroll(0, -16);
    }

    private void button2_Click(object sender, EventArgs e)
    {
      PanelScroll(-16, 0);
    }

    private void button3_Click(object sender, EventArgs e)
    {
      PanelScroll(16, 0);
    }

    private void button4_Click(object sender, EventArgs e)
    {
      PanelScroll(0, 16);
    }

    private void PanelScroll(int x,int y)
    {
      RECT rect = new RECT();
      rect._Left = 0;
      rect._Top = 0;
      rect._Right = panel1.Width;
      rect._Bottom = panel1.Height;
      RECT clip = new RECT();
      clip._Left = 0;
      clip._Top = 0;
      clip._Right = panel1.Width;
      clip._Bottom = panel1.Height;
      RECT udaterect = new RECT();
      ScrollWindowEx(panel1.Handle, x, y, ref rect, ref clip, (IntPtr)null, out udaterect, SW_SCROLLCHILDREN);

      sc.X += x;
      sc.Y += y;

      panel1.Invalidate(new Rectangle(udaterect._Left, udaterect._Top, udaterect._Right - udaterect._Left, udaterect._Bottom - udaterect._Top));
    }
  }
}

解説

スクロール量を保持する変数を用意します。初期値は(0,0)とします。
    public Point sc = new Point(0,0);

描画部分

描画はPanelのPaintイベントに実装します。64pxの正方形のマスを描画します。マスは縦12個、横12個描画します。 スクロールに対応するため、描画する位置にsc変数の値を加算し、スクロールした量だけスライドして描画します。
また、描画時には、PaintEventArgs オブジェクトの ClipRectangleプロパティの値と描画する領域が重なるかをIntersectsWith()メソッドで判定し、 描画が必要な領域に含まれる場合にのみ描画処理を実行します。
  int rWidth = 64;
  int rHeiht = 64;

  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));

  int idx = 0;
  for (int y = 0; y < 12; y++) {
    for (int x = 0; x < 12; x++) {
      Rectangle drawRect = new Rectangle(x * rWidth + sc.X, y * rHeiht + sc.Y, rWidth, rHeiht);
      if (e.ClipRectangle.IntersectsWith(drawRect) == true) {

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

        e.Graphics.DrawString(Convert.ToString(idx), f, bs, new Point(x * rWidth + sc.X + 2, y * rHeiht + sc.Y + 2));
      }
      idx++;
    }
  }

スクロール部分

以下は描画内容をスクロールするコードです。スクロールの動作についてはこちらの記事を参照してください。
スクロールしたのち、スクロール量を sc変数に設定します。
private void button1_Click(object sender, EventArgs e)
{
  PanelScroll(0, -16);
}

private void button2_Click(object sender, EventArgs e)
{
  PanelScroll(-16, 0);
}

private void button3_Click(object sender, EventArgs e)
{
  PanelScroll(16, 0);
}

private void button4_Click(object sender, EventArgs e)
{
  PanelScroll(0, 16);
}

private void PanelScroll(int x,int y)
{
  RECT rect = new RECT();
  rect._Left = 0;
  rect._Top = 0;
  rect._Right = panel1.Width;
  rect._Bottom = panel1.Height;
  RECT clip = new RECT();
  clip._Left = 0;
  clip._Top = 0;
  clip._Right = panel1.Width;
  clip._Bottom = panel1.Height;
  RECT udaterect = new RECT();
  ScrollWindowEx(panel1.Handle, x, y, ref rect, ref clip, (IntPtr)null, out udaterect, SW_SCROLLCHILDREN);

  sc.X += x;
  sc.Y += y;

  panel1.Invalidate(new Rectangle(udaterect._Left, udaterect._Top, udaterect._Right - udaterect._Left, udaterect._Bottom - udaterect._Top));
}

実行結果

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


[button1]をクリックします。描画内容が上方向にスクロールします。画面外からスクロールインする部分も描画されます。


[button2]をクリックします。描画内容が左方向にスクロールします。


[button3]をクリックすると描画内容が右方向にスクロールします。


[button4]をクリックすると、下方向にスクロールします。

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