インターフェイスとデータ構造
マウスジェスチャを検出するために次のクラスと列挙型を作ります。
namespace MouseGesture_Net { /// マウスジェスチャの判定を行う public class MouseGesture { /// 判定距離(プロパティ) public int Range /// マウスジェスチャの有効無効 private bool enable; /// 判定距離 private int range; /// 四方向それぞれについての情報(要素の大きさは4) private Direction []direction; /// マウスをキャプチャしているコントロール private Control control_; /// 移動距離計算用の古い位置 private Point old; /// コンストラクタ public MouseGesture(); /// マウスジェスチャの開始 /// <param name="control">マウスジェスチャの /// 対象となるコントロール</param> /// <param name="pos">現在のマウス位置</param> public void Start(Control control,Point pos); /// マウスジェスチャの終わり public void End(); /// マウスジェスチャの判定 /// <param name="pos">マウスの位置</param> /// <returns>ジェスチャ</returns> public Arrow Test(Point pos); /// 4方向の情報をリセットする private void resetDirection(); #region インラインクラス /// 一定の方向に関する情報を持つ。 public class Direction { /// 有効無効 public bool Enable; /// 累計移動距離 public int Length; /// リセットします public void Reset(); /// コンストラクタ public Direction(); } #endregion } /// 方向 public enum Arrow { none =-1, // 移動無し up = 0, // 上への移動 right= 1, // 右への移動 down = 2, // 下への移動 left = 3 // 左への移動 } }
とりあえずは、これらを実装すればちゃんとマウスジェスチャになります。
以下では、重点項目だけを説明しています。
マウスジェスチャのスタート
このメソッドは「MouseDown」イベントに仕掛けます。
/// <summary> /// マウスジェスチャの開始 /// </summary> /// <param name="control">マウスジェスチャの対象となるコントロール /// </param> /// <param name="pos">現在のマウス位置</param> public void Start(Control control, Point pos) { enable =true; resetDirection(); old =pos; control_ =control; control_.Capture =true; }
方向情報の初期化、位置のセット、コントロールから取得したウィンドウハンドルを使って、マウスキャプチャを行います。
マウスジェスチャの判定
ここがマウスジェスチャの心臓部です。これは「MouseMove」イベントに仕掛けるのが妥当でしょう。
/// <summary> /// マウスジェスチャの判定 /// </summary> /// <param name="pos">マウスの位置</param> /// <returns>ジェスチャ</returns> public Arrow Test(Point pos) { //有効なときだけ判定する。 if(enable) { int ox = old.X ,oy = old.Y; Arrow arrow = Arrow.none; //情報を入れ替えておく old = pos; //移動量を判定して縦横どっちに動くかを判定 if(Math.Abs(ox - pos.X) > Math.Abs(oy - pos.Y)) { if(ox > pos.X) { direction[(int)Arrow.left].Length += ox - pos.X; direction[(int)Arrow.right].Length = 0; arrow = Arrow.left; } else if(pos.X >ox) { direction[(int)Arrow.right].Length += pos.X - ox; direction[(int)Arrow.left].Length = 0; arrow = Arrow.right; } } else { if(oy > pos.Y) { direction[(int)Arrow.up].Length += oy - pos.Y; direction[(int)Arrow.down].Length = 0; arrow = Arrow.up; } else if(pos.Y >oy) { direction[(int)Arrow.down].Length += pos.Y - oy; direction[(int)Arrow.up].Length = 0; arrow = Arrow.down; } } //移動を検知したとき if(arrow != Arrow.none) { if(direction[(int)arrow].Enable && direction[(int)arrow].Length > range) { resetDirection(); //同じ向きが2度入力されないようにする。 direction[(int)arrow].Enable=false; return arrow; } } } return Arrow.none; }
直感に反しないよう、私なりに調整してあります。流れは次のようになります。
- マウスジェスチャが有効かの判定。
- 縦横の移動量を判定。
- 左右(または上下)どちらへ移動しているのかを判定。
- 移動量を加算。反対への移動量を0。
- その移動が有効であるかを判定。
- 移動量が検出量に達しているかを判定。
もっと良い工夫をお持ちの方もいると思います。いろいろな人に試してもらいながらどんな仕組みにするのかを決定すると良いと思います。
マウスジェスチャの終わり
ボタンが離されると、マウスキャプチャの魔法が解けてしまいます。よって、「MouseUp」イベントあたりでこのメソッドを呼ぶようにするのが妥当だと思います。
/// <summary> /// マウスジェスチャの終わり /// </summary> public void End() { if(enable) { enable =false; control_.Capture = false; } }
一応、control_.Capture
にfalseを代入しておいてください。
まとめて使う
まずは、MouseGesture
クラスを作ってください。
そしたら、イベントの設定をします。「MouseDown」のなかでStart()
を、「MouseMove」のなかでTest()
を、「MouseUp」のなかでEnd()
を呼び出すようにセットしてください。
これでTest()
を呼び出すたびに、Arrow
列挙型で値が帰ってくるようになります。
まとめ
マウスジェスチャはアプリケーションに大きな魅力を付加します。使いこなせば、ボタンにカーソルを移動することなく素早く操作できるようになります。
ぜひ、実装を検討してみてください。
それと、今回はサンプルなので、使いやすさよりも説明のしやすさを優先しました。せっかくイベントという仕組みがあるのですから、それをしっかりと利用した方がより良くなると思います。
参考資料
- 猫でも分かるプログラミング Windows SDK編 第2部 『第101章 マウス・キャプチャー』
- WisdomSoft 『標準 Windows API』
- @IT .NET TIPS 『Win32 APIやDLL関数を呼び出すには?』
- Microsoft Win32とMicrosoft .NET Framework APIとの対応