ウィンドウコントロールのクライアント領域をスクロールする - C#
ウィンドウコントロールのクライアント領域をスクロールするコードを紹介します。
概要
ウィンドウコントロールで描画された内容をスクロールさせたい場合があります。
クライアント領域の描画内容をスクロールするには、Windows APIのScrollWindowEx()を用います。
ScrollWindowExはRECT構造体を引数にとるため、RECT構造体も合わせて定義します。
Windows APIへ構造体のポインタを渡すコードについては
こちらの記事も参照してください。
書式
DllImport
DllImportは以下のコードを記述します。
[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);
パラメーターの値は次の通りです。
パラメーター名 | 意味 |
hWnd | クライアント領域をスクロールするウィンドウのハンドル |
dx | 水平方向のスクロールの量 |
dy | 垂直方向のスクロール量 |
prcScroll | クライアント領域でスクロールする範囲のRECT構造体 |
prcClip | クリッピング領域のRECT構造体 |
hrgnUpdate | スクロールにより無効になった領域のリージョンのハンドル |
prcUpdate | スクロールにより無効になった領域のRECT構造体 |
flags | スクロールを制御するフラグ |
flagsは以下のフラグを指定できます。
値 | int値 | 意味 |
SW_ERASE | 0x0001 | SW_INVALIDATE フラグと共にこのフラグを指定すると、スクロール後、WM_ERASEBKGND メッセージをウィンドウへ送信し、新たに無効になったリージョンを消去します。 |
SW_INVALIDATE | 0x0002 | スクロール後、hrgnUpdate パラメータが識別しているリージョンを無効にします。 |
SW_SCROLLCHILDREN | 0x0004 | prcScroll パラメータが指す長方形と重なり合う、すべての子ウィンドウをスクロールします。dx と dy の各パラメータで指定したピクセル数だけ、子ウィンドウをスクロールします。システムは、prcScroll パラメータが指す長方形と重なる子ウィンドウへ、WM_MOVE メッセージを送信します。 |
SW_SMOOTHSCROLL | 0x0010 | スムーズスクロールを行います。flags パラメータの HIWORD 部分で、スムーズスクロール操作を行う回数を指定します。 |
RECT構造体
RECT構造体の定義は次の通りです。
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int _Left;
public int _Top;
public int _Right;
public int _Bottom;
}
プログラム例1
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 FormSimpleScroll : Form
{
/// <summary>
/// Scroll children within *lprcScroll.
/// </summary>
public const uint SW_SCROLLCHILDREN = 0x0001;
/// <summary>
/// Invalidate after scrolling.
/// </summary>
public const uint SW_INVALIDATE = 0x0002;
/// <summary>
/// If SW_INVALIDATE, don't send WM_ERASEBACKGROUND.
/// </summary>
public const uint SW_ERASE = 0x0004;
/// <summary>
/// Use smooth scrolling.
/// </summary>
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 FormSimpleScroll()
{
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, y * rHeiht, rWidth, rHeiht);
e.Graphics.FillRectangle(b, drawRect);
e.Graphics.DrawRectangle(p, drawRect);
e.Graphics.DrawString(Convert.ToString(idx), f, bs, new Point(x * rWidth + 2, y * rHeiht + 2));
idx++;
}
}
}
private void button1_Click(object sender, EventArgs e)
{
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, 0, -16, ref rect, ref clip, (IntPtr)null, out udaterect, SW_SCROLLCHILDREN);
}
private void button4_Click(object sender, EventArgs e)
{
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, 0, 16, ref rect, ref clip, (IntPtr)null, out udaterect, SW_SCROLLCHILDREN);
}
private void button2_Click(object sender, EventArgs e)
{
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, -16, 0, ref rect, ref clip, (IntPtr)null, out udaterect, SW_SCROLLCHILDREN);
}
private void button3_Click(object sender, EventArgs e)
{
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, 16, 0, ref rect, ref clip, (IntPtr)null, out udaterect, SW_SCROLLCHILDREN);
}
}
}
解説
Paintイベント
正方形のマスを描画します。幅と高さは64ピクセルで描画します。12x12個のマスを描画します。
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, y * rHeiht, rWidth, rHeiht);
e.Graphics.FillRectangle(b, drawRect);
e.Graphics.DrawRectangle(p, drawRect);
e.Graphics.DrawString(Convert.ToString(idx), f, bs, new Point(x * rWidth + 2, y * rHeiht + 2));
idx++;
}
}
Buttonのクリックイベント
ボタンのクリックにより以下のコードが実行されます。
ScrollWindowEx 関数を呼び出しスクロールをします。下記のコードでは、コントロールの領域の全体をスクロール領域とし、
クリッピングする領域もコントロールの領域の全体とします。
private void button1_Click(object sender, EventArgs e)
{
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, 0, -16, ref rect, ref clip, (IntPtr)null, out udaterect, SW_SCROLLCHILDREN);
}
実行結果
プロジェクトを実行します。ウィンドウが表示され、下図の画面が表示されます。
[button1]をクリックします。描画されたPanel1の領域が上方向にスクロールします。
[button1]をさらにクリックします。クリックするたびに領域が上方向にスクロールします。
[button4]をクリックします。描画されたPanel1の領域が下方向にスクロールします。
[button4]をさらにクリックします。クリックするたびに領域が下方向にスクロールします。
[button2]をクリックします。描画されたPanel1の内容が左方向にスクロールします。
[button3]をクリックします。描画された内容が右方向にスクロールします。
補足
このプログラムでは、描画内容をスクロールしますが、画面外からスクロールインする領域は描画されません。
画面外からスクロールインする領域を描画する方法は
こちらの記事を参照してください。
プログラム例2
UI
下図のフォームを作成します。
コード
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Security.Permissions;
using System.Drawing;
using System.Runtime.InteropServices;
namespace WinformVisualComponent
{
public partial class VisualComponent : Control
{
private static int counter = 0;
[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);
public enum ScrollBarsMode { None, Vertical, Horizontal, Both }
private ScrollBarsMode scrollBarsMode;
private TextBox debugTextBox;
public ScrollBarsMode ScrollBars
{
set
{
scrollBarsMode = value;
this.UpdateStyles();
}
get
{
return scrollBarsMode;
}
}
public TextBox DebugTextBox
{
set
{
debugTextBox = value;
}
get
{
return debugTextBox;
}
}
public VisualComponent()
{
InitializeComponent();
}
public VisualComponent(IContainer container)
{
container.Add(this);
InitializeComponent();
}
protected override void WndProc(ref Message message)
{
base.WndProc(ref message);
RECT rect = new RECT();
rect._Left = 0;
rect._Top = 0;
rect._Right = this.Width;
rect._Bottom = this.Height;
IntPtr prect = Marshal.AllocCoTaskMem(Marshal.SizeOf(rect));
Marshal.StructureToPtr(rect, prect, true);
RECT cliprect = new RECT();
cliprect._Left = 0;
cliprect._Top = 0;
cliprect._Right = this.Width;
cliprect._Bottom = this.Height;
IntPtr pcliprect = Marshal.AllocCoTaskMem(Marshal.SizeOf(cliprect));
Marshal.StructureToPtr(cliprect, pcliprect, true);
RECT updateRect = new RECT();
IntPtr pupdateRect = Marshal.AllocCoTaskMem(Marshal.SizeOf(updateRect));
if (message.Msg == WindowsConst.WM_VSCROLL){
short lo = LoWord((long)message.WParam);
if (lo == WindowsConst.SB_LINEDOWN) {
ScrollWindowEx(this.Handle, 0, 16, prect, IntPtr.Zero,
(IntPtr)null, pupdateRect, WindowsConst.SW_SCROLLCHILDREN);
}
else if (lo == WindowsConst.SB_LINEUP) {
ScrollWindowEx(this.Handle, 0,-16, prect, IntPtr.Zero,
(IntPtr)null, pupdateRect, WindowsConst.SW_SCROLLCHILDREN);
}
}
else if (message.Msg == WindowsConst.WM_HSCROLL) {
short lo = LoWord((long)message.WParam);
if (lo == WindowsConst.SB_LINELEFT) {
ScrollWindowEx(this.Handle, -16, 0, prect, IntPtr.Zero,
(IntPtr)null, pupdateRect, WindowsConst.SW_SCROLLCHILDREN);
}
else if (lo == WindowsConst.SB_LINERIGHT) {
ScrollWindowEx(this.Handle, 16, 0, prect, IntPtr.Zero,
(IntPtr)null, pupdateRect, WindowsConst.SW_SCROLLCHILDREN);
}
}
}
protected override CreateParams CreateParams
{
get
{
new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand();
CreateParams cp = base.CreateParams;
int ScrollBarFlag = 0;
switch (scrollBarsMode) {
case ScrollBarsMode.None:
ScrollBarFlag = 0;
break;
case ScrollBarsMode.Horizontal:
ScrollBarFlag = WindowsConst.WS_HSCROLL;
break;
case ScrollBarsMode.Vertical:
ScrollBarFlag = WindowsConst.WS_VSCROLL;
break;
case ScrollBarsMode.Both:
ScrollBarFlag = WindowsConst.WS_HSCROLL + WindowsConst.WS_VSCROLL;
break;
}
cp.Style |= ScrollBarFlag;
return cp;
}
}
protected override void OnPaint(PaintEventArgs e)
{
base.OnPaint(e);
}
public void Drawinit()
{
Graphics g = this.CreateGraphics();
g.DrawRectangle(new Pen(Color.Red, 1.0f), new Rectangle(100, 100, 20, 20));
}
protected short LoWord(long input)
{
return (short)((int)input & 0xFFFF);
}
protected short HiWord(long input)
{
return (short)((int)input >> 16);
}
protected long MakeWParam(short l, short h){
return MakeLong(l, h);
}
protected long MakeLong(short a, short b)
{
return a & 0xFFFF | b & 0XFFF << 16;
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace WinformVisualComponent
{
public partial class SubFormScrollEvent : Form
{
public SubFormScrollEvent()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
SubFormLogView sflv = new SubFormLogView();
visualComponent1.DebugTextBox = sflv.textBox1;
sflv.Show();
}
private void button2_Click(object sender, EventArgs e)
{
visualComponent1.Drawinit();
}
}
}
解説
- スクロールバーを表示するコードはこちらの記事を参照してください。
- スクロールバーの操作を検出するコードはこちらの記事を参照してください。
以下のコードではRECT構造体の定義とScrollWindowExのインポートをします。
[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);
RECT構造体とそのポインタを準備します。ポインタはMarshal.AllocCoTaskMemメソッドを用いてメモリを確保し、Marshal.StructureToPtrメソッドで構造体をポインタにします。
RECT rect = new RECT();
rect._Left = 0;
rect._Top = 0;
rect._Right = this.Width;
rect._Bottom = this.Height;
IntPtr prect = Marshal.AllocCoTaskMem(Marshal.SizeOf(rect));
Marshal.StructureToPtr(rect, prect, true);
RECT cliprect = new RECT();
cliprect._Left = 0;
cliprect._Top = 0;
cliprect._Right = this.Width;
cliprect._Bottom = this.Height;
IntPtr pcliprect = Marshal.AllocCoTaskMem(Marshal.SizeOf(cliprect));
Marshal.StructureToPtr(cliprect, pcliprect, true);
RECT updateRect = new RECT();
IntPtr pupdateRect = Marshal.AllocCoTaskMem(Marshal.SizeOf(updateRect));
ウィンドウコントロールをスクロールします。
ScrollWindowEx(this.Handle, 16, 0, prect, IntPtr.Zero,
(IntPtr)null, pupdateRect, WindowsConst.SW_SCROLLCHILDREN);
実行結果
アプリケーションを実行すると下図のウィンドウが表示されます。
Button2をクリックします。赤い四角形が表示されます。(下図参照)
スクロールバーの矢印ボタンをクリックするとコントロールのクライアント領域がスクロールします。赤い四角の位置が変わることでスクロールの動作を確認できます。
著者
iPentecのメインプログラマー
C#, ASP.NET の開発がメイン、少し前まではDelphiを愛用