DataGridViewのスクロールの拡張

チルトホイールによる水平スクロール

DataGridViewはマウスホイールの水平スクロールのメッセージ (WM_MOUSEHWHEEL) に応答しないため、それでスクロールさせるにはDataGridViewを拡張する必要があります。

protected override void WndProc(ref Message m)
{
    const int WM_MOUSEHWHEEL = 0x020E;
    if (m.Msg == WM_MOUSEHWHEEL)
    {
        int delta = ((int)m.WParam.ToInt64()) >> 16;

        int offset = HorizontalScrollingOffset + delta;
        if (offset < 0) offset = 0;

        HorizontalScrollingOffset = offset;
        m.Result = IntPtr.Zero;
    }

    base.WndProc(ref m);
}

この方法ではWM_MOUSEHWHEELメッセージを処理しているため、このメッセージを送信するデバイスならば、タッチパッドのスクロールのジェスチャにも対応できます。

なお既定でも、垂直スクロールによる操作時にCtrlキーが押下されていると、それで水平スクロールします。

オートスクロール (auto scroll)

マウスポインタの移動でスクロールするようにするには、Reader modeを用います。

[DllImport("comctl32.dll", EntryPoint = "#383")] // 383は、DoReaderMode()の序数
public static extern void DoReaderMode(ref READERMODEINFO prmi);

public delegate bool ReaderScroll(ref READERMODEINFO prmi, int dx, int dy);
public delegate bool TranslateDispatch(ref MSG lpmsg);


[StructLayout(LayoutKind.Sequential)]
public struct READERMODEINFO
{
    public uint cbSize;
    public IntPtr hwnd;
    public UInt32 fFlags;
    public IntPtr prc;
    public ReaderScroll pfnScroll;
    public TranslateDispatch pfnTranslate;
    public IntPtr lParam;
}

[StructLayout(LayoutKind.Sequential)]
public struct MSG
{
    public IntPtr hwnd;
    public uint message;
    public IntPtr wParam;
    public IntPtr lParam;
    public UInt32 time;
    public POINT pt;
    public UInt32 lPrivate;
}

[StructLayout(LayoutKind.Sequential)]
public struct POINT
{
    public Int32 x;
    public Int32 y;
}

[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
    public Int32 left;
    public Int32 top;
    public Int32 right;
    public Int32 bottom;
}


protected override void OnMouseDown(MouseEventArgs e)
{
    base.OnMouseDown(e);
    if (e.Button == MouseButtons.Middle)
    {
        // マウスの中ボタンの押下で、Reader modeに入る
        EnterReaderMode(e.Location);
    }
}

private void EnterReaderMode(Point point)
{
    // カーソルのサイズを基準に、スクロール領域を算出
    Size size = Cursor.Current.Size;
    int width = size.Width / 2;
    int height = size.Height / 2;

    RECT scrollingArea = new RECT
    {
        left = point.X - width,
        right = point.X + width,
        top = point.Y - height,
        bottom = point.Y + height,
    };

    IntPtr pScrollingArea = Marshal.AllocHGlobal(Marshal.SizeOf<RECT>());
    Marshal.StructureToPtr(scrollingArea, pScrollingArea, false);

    const uint RMF_ZEROCURSOR = 0x01;
    READERMODEINFO readerInfo = new READERMODEINFO
    {
        cbSize = (uint)Marshal.SizeOf<READERMODEINFO>(),
        hwnd = Handle,
        fFlags = RMF_ZEROCURSOR,
        prc = pScrollingArea,
        pfnScroll = new ReaderScroll(ReaderScrollCallback),
        pfnTranslate = new TranslateDispatch(TranslateDispatchCallback),
        lParam = IntPtr.Zero,
    };

    DoReaderMode(ref readerInfo);

    Marshal.FreeHGlobal(pScrollingArea);
}

private bool ReaderScrollCallback(ref READERMODEINFO prmi, int dx, int dy)
{
    // コントロールをスクロールする
    HorizontalScrollingOffset += dy;
    FirstDisplayedScrollingRowIndex += dy;

    return true;
}

private bool TranslateDispatchCallback(ref MSG lpmsg)
{
    return false;
}
Microsoft Learnから検索