動画の実行結果を見ると、内部のコンテンツのちらつきがあることがわかります。これは、スクロール時にコントロール全体を再描画しているためです。
ちらつきを抑える場合には、スクロールインする部分のみを再描画します。
コードについてはこちらの記事を参照してください。
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace WindowControlScroll
{
public partial class MyComponent : Control
{
// window style constants for scrollbars
public const int WS_VSCROLL = 0x00200000;
public const int WS_HSCROLL = 0x00100000;
public const int WM_LBUTTONDOWN = 0x00000201;
public const int WM_RBUTTONDOWN = 0x00000204;
public const int WM_HSCROLL = 0x00000114;
public const int WM_VSCROLL = 0x00000115;
/*
* Scroll Bar Commands
*/
public const int SB_LINEUP = 0;
public const int SB_LINELEFT = 0;
public const int SB_LINEDOWN = 1;
public const int SB_LINERIGHT = 1;
public const int SB_PAGEUP = 2;
public const int SB_PAGELEFT = 2;
public const int SB_PAGEDOWN = 3;
public const int SB_PAGERIGHT = 3;
public const int SB_THUMBPOSITION = 4;
public const int SB_THUMBTRACK = 5;
public const int SB_TOP = 6;
public const int SB_LEFT = 6;
public const int SB_BOTTOM = 7;
public const int SB_RIGHT = 7;
public const int SB_ENDSCROLL = 8;
[StructLayout(LayoutKind.Sequential)]
struct SCROLLINFO
{
public uint cbSize;
public uint fMask;
public int nMin;
public int nMax;
public uint nPage;
public int nPos;
public int nTrackPos;
}
private enum ScrollBarDirection
{
SB_HORZ = 0,
SB_VERT = 1,
SB_CTL = 2,
SB_BOTH = 3
}
private enum ScrollInfoMask
{
SIF_RANGE = 0x0001,
SIF_PAGE = 0x0002,
SIF_POS = 0x0004,
SIF_DISABLENOSCROLL = 0x0008,
SIF_TRACKPOS = 0x0010,
SIF_ALL = SIF_RANGE | SIF_PAGE | SIF_POS | SIF_TRACKPOS
//SIF_ALL = SIF_RANGE + SIF_PAGE + SIF_POS + SIF_TRACKPOS
}
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetScrollInfo(IntPtr hwnd, int fnBar, ref SCROLLINFO lpsi);
[DllImport("user32.dll")]
private static extern int SetScrollInfo(IntPtr hwnd, int fnBar, [In] ref SCROLLINFO lpsi, bool fRedraw);
const int ScrollOffset = 2;
public Point sc = new Point(0, 0);
public MyComponent()
{
InitializeComponent();
}
public MyComponent(IContainer container)
{
container.Add(this);
InitializeComponent();
}
protected override void InitLayout()
{
base.InitLayout();
int tp;
SetVScrollBar(sc.Y, (uint)ClientSize.Height, 0, (64 * 12) + ScrollOffset, out tp);
SetHScrollBar(sc.X, (uint)ClientSize.Width, 0, (64 * 12) + ScrollOffset, out tp);
}
protected override CreateParams CreateParams {
get
{
CreateParams cp = base.CreateParams;
cp.Style |= WS_HSCROLL + WS_VSCROLL;
return cp;
}
}
protected override void WndProc(ref Message message)
{
base.WndProc(ref message);
short lo;
int tp;
switch (message.Msg) {
case WM_HSCROLL:
lo = LoWord((long)message.WParam);
switch (lo) {
case SB_ENDSCROLL:
break;
case SB_LEFT:
break;
case SB_RIGHT:
break;
case SB_LINELEFT:
sc.X = Math.Max(sc.X -= 8, 0);
break;
case SB_LINERIGHT:
sc.X = Math.Min(sc.X += 8, (64 * 12) - ClientRectangle.Width + ScrollOffset);
break;
case SB_PAGELEFT:
sc.X = Math.Max(sc.X -= 32, 0);
break;
case SB_PAGERIGHT:
sc.X = Math.Min(sc.X += 32, (64 * 12) - ClientRectangle.Width + ScrollOffset);
break;
case SB_THUMBPOSITION:
break;
case SB_THUMBTRACK:
SCROLLINFO HScrInfo = new SCROLLINFO();
HScrInfo.fMask = (uint)ScrollInfoMask.SIF_TRACKPOS;
GetScrollInfo(this.Handle, (int)ScrollBarDirection.SB_HORZ, ref HScrInfo);
sc.X = Math.Min(HScrInfo.nTrackPos, (64 * 12) - ClientRectangle.Width + ScrollOffset);
sc.X = Math.Max(sc.X, 0);
break;
}
SetHScrollBar(sc.X, 32, 0, (64 * 12) - ClientRectangle.Width + ScrollOffset, out tp);
this.Invalidate();
break;
case WM_VSCROLL:
lo = LoWord((long)message.WParam);
switch (lo) {
case SB_BOTTOM:
break;
case SB_ENDSCROLL:
break;
case SB_LINEDOWN:
sc.Y = Math.Min(sc.Y += 8, (64 * 12) - ClientRectangle.Height+ ScrollOffset);
break;
case SB_LINEUP:
sc.Y = Math.Max(sc.Y -= 8, 0);
break;
case SB_PAGEDOWN:
sc.Y = Math.Min(sc.Y += 32, (64 * 12) - ClientRectangle.Height + ScrollOffset);
break;
case SB_PAGEUP:
sc.Y = Math.Max(sc.Y -= 32, 0);
break;
case SB_THUMBPOSITION:
break;
case SB_THUMBTRACK:
SCROLLINFO VScrInfo = new SCROLLINFO();
VScrInfo.fMask = (uint)ScrollInfoMask.SIF_TRACKPOS;
GetScrollInfo(this.Handle, (int)ScrollBarDirection.SB_VERT, ref VScrInfo);
sc.Y = VScrInfo.nTrackPos;
break;
case SB_TOP:
break;
}
SetVScrollBar(sc.Y, (uint)ClientSize.Height, 0, (64 * 12) + ScrollOffset, out tp);
this.Invalidate();
break;
}
}
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
int tp;
SetVScrollBar(sc.Y, (uint)ClientSize.Height, 0, (64 * 12) + ScrollOffset, out tp);
SetHScrollBar(sc.X, (uint)ClientSize.Width, 0, (64 * 12) + ScrollOffset, out tp);
}
protected override void OnPaint(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++;
}
}
}
protected short LoWord(long input)
{
return (short)((int)input & 0xFFFF);
}
protected short HiWord(long input)
{
return (short)((int)input >> 16);
}
public void SetVScrollBar(int Position, uint Page, int nMin, int nMax, out int TrackPosition)
{
SCROLLINFO VScrInfo = new SCROLLINFO();
GetScrollInfo(this.Handle, (int)ScrollBarDirection.SB_VERT, ref VScrInfo);
VScrInfo.cbSize = (uint)Marshal.SizeOf(VScrInfo);
VScrInfo.nPos = Position;
VScrInfo.nPage = Page;
VScrInfo.nMin = nMin;
VScrInfo.nMax = nMax;
TrackPosition = VScrInfo.nTrackPos;
VScrInfo.fMask = (int)ScrollInfoMask.SIF_POS + (int)ScrollInfoMask.SIF_PAGE + (int)ScrollInfoMask.SIF_RANGE + (int)ScrollInfoMask.SIF_DISABLENOSCROLL;
SetScrollInfo(this.Handle, (int)ScrollBarDirection.SB_VERT, ref VScrInfo, true);
}
public void SetHScrollBar(int Position, uint Page, int nMin, int nMax, out int TrackPosition)
{
SCROLLINFO HScrInfo = new SCROLLINFO();
GetScrollInfo(this.Handle, (int)ScrollBarDirection.SB_VERT, ref HScrInfo);
HScrInfo.cbSize = (uint)Marshal.SizeOf(HScrInfo);
HScrInfo.nPos = Position;
HScrInfo.nPage = Page;
HScrInfo.nMin = nMin;
HScrInfo.nMax = nMax;
TrackPosition = HScrInfo.nTrackPos;
HScrInfo.fMask = (int)ScrollInfoMask.SIF_POS + (int)ScrollInfoMask.SIF_PAGE + (int)ScrollInfoMask.SIF_RANGE + (int)ScrollInfoMask.SIF_DISABLENOSCROLL;
SetScrollInfo(this.Handle, (int)ScrollBarDirection.SB_HORZ, ref HScrInfo, true);
}
}
}
Top, Bottom, Left, Right
に設定し、フォームのサイズに合わせてコントロールの大きさが変化する状態に設定します。 public Point sc = new Point(0, 0);
WS_HSCROLL + WS_VSCROLL
を指定しスクロールバーを表示します。
詳細はこちらの記事を参照してください。 protected override CreateParams CreateParams {
get
{
CreateParams cp = base.CreateParams;
cp.Style |= WS_HSCROLL + WS_VSCROLL;
return cp;
}
}
public void SetVScrollBar(int Position, uint Page, int nMin, int nMax, out int TrackPosition)
{
SCROLLINFO VScrInfo = new SCROLLINFO();
GetScrollInfo(this.Handle, (int)ScrollBarDirection.SB_VERT, ref VScrInfo);
VScrInfo.cbSize = (uint)Marshal.SizeOf(VScrInfo);
VScrInfo.nPos = Position;
VScrInfo.nPage = Page;
VScrInfo.nMin = nMin;
VScrInfo.nMax = nMax;
TrackPosition = VScrInfo.nTrackPos;
VScrInfo.fMask = (int)ScrollInfoMask.SIF_POS + (int)ScrollInfoMask.SIF_PAGE + (int)ScrollInfoMask.SIF_RANGE + (int)ScrollInfoMask.SIF_DISABLENOSCROLL;
SetScrollInfo(this.Handle, (int)ScrollBarDirection.SB_VERT, ref VScrInfo, true);
}
public void SetHScrollBar(int Position, uint Page, int nMin, int nMax, out int TrackPosition)
{
SCROLLINFO HScrInfo = new SCROLLINFO();
GetScrollInfo(this.Handle, (int)ScrollBarDirection.SB_VERT, ref HScrInfo);
HScrInfo.cbSize = (uint)Marshal.SizeOf(HScrInfo);
HScrInfo.nPos = Position;
HScrInfo.nPage = Page;
HScrInfo.nMin = nMin;
HScrInfo.nMax = nMax;
TrackPosition = HScrInfo.nTrackPos;
HScrInfo.fMask = (int)ScrollInfoMask.SIF_POS + (int)ScrollInfoMask.SIF_PAGE + (int)ScrollInfoMask.SIF_RANGE + (int)ScrollInfoMask.SIF_DISABLENOSCROLL;
SetScrollInfo(this.Handle, (int)ScrollBarDirection.SB_HORZ, ref HScrInfo, true);
}
protected override void InitLayout()
{
base.InitLayout();
int tp;
SetVScrollBar(sc.Y, (uint)ClientSize.Height, 0, (64 * 12) + ScrollOffset, out tp);
SetHScrollBar(sc.X, (uint)ClientSize.Width, 0, (64 * 12) + ScrollOffset, out tp);
}
SetVScrollBar
SetHScrollBar
メソッドを呼び出してスクロールバーのトラックの位置を変更します。(64 * 12) - ClientRectangle.(Width または Height) + ScrollOffset
より大きくなる場合は、Minメソッドを使用して、
scの値をコンテンツからクライアント領域を引いた幅や高さを超えないよう設定します。 protected override void WndProc(ref Message message)
{
base.WndProc(ref message);
short lo;
int tp;
switch (message.Msg) {
case WM_HSCROLL:
lo = LoWord((long)message.WParam);
switch (lo) {
case SB_ENDSCROLL:
break;
case SB_LEFT:
break;
case SB_RIGHT:
break;
case SB_LINELEFT:
sc.X = Math.Max(sc.X -= 8, 0);
break;
case SB_LINERIGHT:
sc.X = Math.Min(sc.X += 8, (64 * 12) - ClientRectangle.Width + ScrollOffset);
break;
case SB_PAGELEFT:
sc.X = Math.Max(sc.X -= 32, 0);
break;
case SB_PAGERIGHT:
sc.X = Math.Min(sc.X += 32, (64 * 12) - ClientRectangle.Width + ScrollOffset);
break;
case SB_THUMBPOSITION:
break;
case SB_THUMBTRACK:
SCROLLINFO HScrInfo = new SCROLLINFO();
HScrInfo.fMask = (uint)ScrollInfoMask.SIF_TRACKPOS;
GetScrollInfo(this.Handle, (int)ScrollBarDirection.SB_HORZ, ref HScrInfo);
sc.X = HScrInfo.nTrackPos;
break;
}
SetHScrollBar(sc.X, (uint)ClientSize.Width, 0, (64 * 12) + ScrollOffset, out tp);
this.Invalidate();
break;
case WM_VSCROLL:
lo = LoWord((long)message.WParam);
switch (lo) {
case SB_BOTTOM:
break;
case SB_ENDSCROLL:
break;
case SB_LINEDOWN:
sc.Y = Math.Min(sc.Y += 8, (64 * 12) - ClientRectangle.Height+ ScrollOffset);
break;
case SB_LINEUP:
sc.Y = Math.Max(sc.Y -= 8, 0);
break;
case SB_PAGEDOWN:
sc.Y = Math.Min(sc.Y += 32, (64 * 12) - ClientRectangle.Height + ScrollOffset);
break;
case SB_PAGEUP:
sc.Y = Math.Max(sc.Y -= 32, 0);
break;
case SB_THUMBPOSITION:
break;
case SB_THUMBTRACK:
SCROLLINFO VScrInfo = new SCROLLINFO();
VScrInfo.fMask = (uint)ScrollInfoMask.SIF_TRACKPOS;
GetScrollInfo(this.Handle, (int)ScrollBarDirection.SB_VERT, ref VScrInfo);
sc.Y = VScrInfo.nTrackPos;
break;
case SB_TOP:
break;
}
SetVScrollBar(sc.Y, (uint)ClientSize.Height, 0, (64 * 12) + ScrollOffset, out tp);
this.Invalidate();
break;
}
}
SetVScrollBar
SetHScrollBar
メソッドを呼び出してスクロールバーを設定します。 protected override void OnResize(EventArgs e)
{
base.OnResize(e);
int tp;
SetVScrollBar(sc.Y, (uint)ClientSize.Height, 0, (64 * 12) + ScrollOffset, out tp);
SetHScrollBar(sc.X, (uint)ClientSize.Width, 0, (64 * 12) + ScrollOffset, out tp);
}
protected override void OnPaint(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++;
}
}
}