koudenpaのブログ

趣味のブログです。株式会社はてなでWebアプリケーションエンジニアをやっています。職業柄IT関連の記事が多いと思います。

System.Windows.Forms.ScrollBar をキー入力でスクロールしないようにする

Windows Forms には水平、垂直スクロールバーHScrollBar, VScrollBar)コントロールがある。これらはフォーカスが当たっている際にテンキーの上下左右を入力するとスクロールする。この動作をなくしてマウス操作時だけスクロールするようにしたい。

すんなりは行かない。

キー操作を制御するプロパティは存在しないし、派生クラスを作ってキー操作処理自体を無効化してしまうと親コントロールへのキー操作の伝搬も止まってしまって都合が悪い。

仕方ないのでソースコードを眺めてキー入力に伴うスクロールのメッセージだけ処理しないようにした。

かなりいい加減なハックなのでそのうち動かなくなるかもしれないけれど、とりあえず目的は達成されたので満足した。

こんな感じ。

SRC/SrcScrollBar.cs at 29e96eeb18b28837bc9b02790568252fdea1edfb · 7474/SRC · GitHub

水平スクロールバーの例を貼っておく。垂直も同様に処理できる。

public class SrcHScrollBar : HScrollBar
{
    private bool onMouseProc;
    protected override void WndProc(ref Message m)
    {
        onMouseProc |= isMouseDown(m);
        onMouseProc &= !isMouseUp(m);

        // マウスでの操作中でなく、スクロールのメッセージなら無視する
        if (onMouseProc || !isScrollMsg(m))
        {
            base.WndProc(ref m);
        }
    }
    //WM_LBUTTONDOWN = 0x0201,
    //WM_LBUTTONUP = 0x0202,
    private static bool isMouseDown(Message msg)
    {
        return msg.Msg == 0x0201;
    }
    private static bool isMouseUp(Message msg)
    {
        return msg.Msg == 0x0202;
    }
    //    case NativeMethods.WM_REFLECT + NativeMethods.WM_HSCROLL:
    //    case NativeMethods.WM_REFLECT + NativeMethods.WM_VSCROLL:
    //    WM_HSCROLL = 0x0114,
    //    WM_VSCROLL = 0x0115,
    //    WM_USER = 0x0400,
    //    WM_REFLECT = NativeMethods.WM_USER + 0x1C00,
    private static bool isScrollMsg(Message msg)
    {
        return msg.Msg == 0x0400 + 0x1c00 + 0x0114
            || msg.Msg == 0x0400 + 0x1c00 + 0x0115;
    }
}

最近VBからC# .NETに移植しているSRCというゲームは、上下左右キーでマップをスクロールできるようになっていて、そのスクロール状態が水平、垂直スクロールバーと同期している。

この上下左右キーのスクロールと、スクロールバーが持っているキー入力でのスクロール処理が競合してフォーカスの状態に応じて変なスクロールをする状態だった。

これになんか非常にイラついていた。

そもそもイリーガルなスクロールバーの使い方だろうから、スクロールバーを使わないようにするとか、ScrollableControlを使うようにするとかした方がいいのだろうとは思う。

けれどなんとなくこの素朴なスクロールバーの使い方が気に入っていたり、キー操作だけ無効化できない作りにイラついていたりしたので、そのイラつきを燃料にしてハックした形。

おかげで今はとてもスッキリした気分だ。

スクロール処理が競合している様子

f:id:koudenpa:20210507192216g:plain
上下左右同時にスクロールしたり、1操作で2操作分スクロールしたりしている

競合しなくなった様子

f:id:koudenpa:20210507192308g:plain
上下左右に1操作分ずつスクロールする