C# マウスホバー状態のコントロールにホイールでスクロールさせる
例えば一覧上にマウスカーソルがある状態でホイールをコロコロすると
コントロールが選択状態ならスクロールするものの、
未選択の状態ではスクロールしてくれません。
これは結構不便で、私はクライアントにWheelRedirectorとかのアプリを
常駐させているのですが、これと同じことを
C#で実装する方法です。
ほぼ転載です。
この記事を参考にしました。
要するにマウスホバーした状態で発生したメッセージを
ホバーしているコントロールに引き渡すようにします。
参照元のソースをベースにちょっと手を加えてこんな感じにしてみました。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Forms; namespace hoge { /// <summary> /// マウスホバーによるメッセージリダイレクトを行うクラス /// </summary> class MessageRedirector : NativeWindow, IMessageFilter { private const int MSG_WHEEL_SCROLL = 0x20A; private System.Windows.Forms.Control _Control; private System.Windows.Forms.Control _PreviousParent; private HashSet<int> _Messages; private bool _IsMouseOverControl; public MessageRedirector(System.Windows.Forms.Control control) : this(control, new int[] { MSG_WHEEL_SCROLL }) { } public MessageRedirector(System.Windows.Forms.Control control, int message) : this(control, new int[] { message }) { } public MessageRedirector(System.Windows.Forms.Control control, IEnumerable<int> messages) { _Control = control; AssignHandle(control.Handle); _Messages = new HashSet<int>(messages); _PreviousParent = control.Parent; _IsMouseOverControl = false; control.ParentChanged += new EventHandler(control_ParentChanged); control.MouseEnter += new EventHandler(control_MouseEnter); control.MouseLeave += new EventHandler(control_MouseLeave); control.Leave += new EventHandler(control_Leave); if (control.Parent != null) { Application.AddMessageFilter(this); } } public bool PreFilterMessage(ref System.Windows.Forms.Message m) { if (_Messages.Contains(m.Msg) && _Control.CanFocus && !_Control.Focused && _IsMouseOverControl) { m.HWnd = _Control.Handle; WndProc(ref m); return true; } return false; } void control_ParentChanged(object sender, EventArgs e) { if (_Control.Parent == null) { Application.RemoveMessageFilter(this); } else { if (_PreviousParent == null) { Application.AddMessageFilter(this); } } _PreviousParent = _Control.Parent; } void control_MouseEnter(object sender, EventArgs e) { _IsMouseOverControl = true; } void control_MouseLeave(object sender, EventArgs e) { _IsMouseOverControl = false; } void control_Leave(object sender, EventArgs e) { _IsMouseOverControl = false; } public static void setRedirect(System.Windows.Forms.Control target, int message = MSG_WHEEL_SCROLL) { foreach (System.Windows.Forms.Control control in CommonUtility.GetAllControls(target)) { new MessageRedirector(control, message); } } } }
using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Text; using System.Windows.Forms; namespace hoge { public class CommonUtility { /// <summary> /// 配下すべてのコントロールを列挙する /// </summary> /// <param name="top"></param> /// <returns></returns> public static System.Windows.Forms.Control[] GetAllControls(System.Windows.Forms.Control top) { ArrayList buf = new ArrayList(); foreach (System.Windows.Forms.Control c in top.Controls) { buf.Add(c); buf.AddRange(GetAllControls(c)); } return (System.Windows.Forms.Control[])buf.ToArray(typeof(System.Windows.Forms.Control)); } } }
使う時にはこんな感じで使えると思います。
// tabControl1配下のコントロールにWheel操作をリダイレクト MessageRedirector.setRedirect(tabControl1);
以上でコントロールにフォーカスがなくてもスクロールできるようになります。
これはスマートですね。