はじめに
ブラウザでは、マウス中ボタンを押すとアンカーが置かれ、カーソルを離した方向へ自動スクロールできます。
WPFアプリでも、コード一覧、ログビュー、長いフォームなどでは同じ操作があると便利です。この記事では、AutoScrollHelper.AttachToWindow(this)の1行で、近くのScrollViewerにブラウザ風オートスクロールを追加する実装を紹介します。
使う場面
- コードビューアやログビューを長くスクロールしたい
- ホイールだけでは移動が遅い
- 複数ペインのうち、マウスを置いたペインだけをスクロールさせたい
- 既存画面のXAMLを大きく変えずに操作性を上げたい
- AvalonEditなど内部にScrollViewerを持つコントロールにも効かせたい
実装コード
コードを読み込み中...ルート要素やWindowのLoadedなどで、次のように呼び出します。
AutoScrollHelper.AttachToWindow(this);
仕組み
処理の流れは次の通りです。
PreviewMouseDownでマウス中ボタンを検知する- クリックされた要素からビジュアルツリーをたどり、最寄りの
ScrollViewerを探す - 中ボタン押下位置をアンカーとして保存する
- DispatcherTimerで20msごとにカーソル位置との差分を計算する
- 差分がデッドゾーンを超えたら、縦横のスクロール位置を更新する
- 任意のクリックまたはEscキーで停止する
調整ポイント
deadZoneは、アンカー付近でスクロールが暴れないようにするための幅です。今回の例では10pxにしています。
speedFactorは、カーソルがアンカーから離れた距離をスクロール量へ変換する係数です。大きくすると速く、小さくするとゆっくりになります。
FindAncestorScrollViewerで最寄りのScrollViewerを探しているため、複数ペインがある画面でも、中ボタンを押したペインだけを対象にできます。
このテクニックを使っているアプリ
このノウハウは、配布アプリ「階層化フォーム (IKI-Kaiso VSTO版)」で実装・確認しています。
注意点
- 静的ヘルパーなので、複数ウィンドウで同時に使う場合は状態管理を見直す余地があります。
Mouse.OverrideCursorを使うため、停止時に必ずnullへ戻します。- ScrollViewerが破棄された直後にTimerが動くことがあるため、Tick内は例外を握りつぶしています。
- タッチ操作やトラックパッド操作とは別のマウス操作向けの改善です。
まとめ
WPFでブラウザ風の中ボタンオートスクロールを実装すると、長い一覧やコードビューアの操作性が上がります。
中ボタン検知、最寄りScrollViewer探索、DispatcherTimerによる連続更新を小さなヘルパーに閉じ込めると、既存画面にも1行で追加しやすいUI改善になります。
