マウスジェスチャー実装

C#でフォームのコントロールなどにマウスジェスチャー機能をつけるための実装例。
MouseDown/MouseMove/MouseUpが使えるコントロールにつなげてやれば使えます。
いろいろと突っ込みどころ満載なソースなので、突っ込み歓迎です。

これを例のプログラムに追加したいけど時間がないなぁ
年末・年始にかけてできたらやっておきたい。


MouseGestureCommand.cs

using System;

namespace Panda728.Utility {
    /// 
    /// ジェスチャーのコマンドと対応するイベントを管理する。
    /// 
    public class MouseGestureCommand {
        private string gesture = string.Empty;
        private string menuTitle = string.Empty;
        private EventHandler e = null;

        /// 
        /// 初期化
        /// 
        /// ジェスチャー
        /// コマンド名
        /// コマンドに対応したイベント
        public MouseGestureCommand( string gesture, string menuTitle, EventHandler e ) {
            this.gesture = gesture;
            this.menuTitle = menuTitle;
            this.e = e;
        }

        /// 
        /// ジェスチャー
        /// 
        public string Gesture {
            get { return gesture; }
            set { gesture = value; }
        }

        /// 
        /// コマンド名
        /// 
        public string MenuTitle {
            get { return menuTitle; }
            set { menuTitle = value; }
        }

        /// 
        /// コマンドに対応したイベント
        /// 
        public EventHandler RaiseEvent {
            get { return e; }
            set { e = value; }
        }
    }
}


MouseGestureManager.cs

using System;
using System.Drawing;
using System.Collections;

namespace Panda728.Utility {
    /// 
    /// マウスジェスチャーのコマンド入力を管理する
    /// 
    public class MouseGestureManager {
        // 感度
        private const int MOUSE_SENSITIVITY = 4;

        // コマンド文字
        private const string COMMAND_UP = "↑";
        private const string COMMAND_DOWN = "↓";
        private const string COMMAND_LEFT = "←";
        private const string COMMAND_RIGHT = "→";

        // 前回入力されたジェスチャー
        private string preArrow = "";

        // 前回の位置
        private Point prePos = new Point(0,0);
        
        // 入力済みのジェスチャー
        private System.Text.StringBuilder commands = new System.Text.StringBuilder();
        
        // ジェスチャー判定用の命令群
        private ArrayList menu = new ArrayList();

        // ステータス表示用イベント
        private EventHandler update = null;
        private EventHandler refresh = null;

        public MouseGestureManager() {
            this.Refresh();
        }

        #region properties
        /// 
        /// 入力済みのジェスチャー
        /// 
        public string Gesture {
            get { return commands.ToString(); }
        }

        /// 
        /// 一致するメニューのタイトル
        /// 
        public string MenuTitle {
            get {
                foreach( MouseGestureCommand m in menu )
                    if ( m.Gesture == commands.ToString() )
                        return "(" + m.MenuTitle + ")";
                return string.Empty;
            }
        }

        /// 
        /// ジェスチャー判定用の命令群
        /// 
        public ArrayList Menu {
            get { return menu; }
            set { menu = value; }
        }

        /// 
        /// ステータス表示用イベント
        /// 
        public EventHandler OnGestureCommandUpdate {
            get { return update; }
            set { update = value; }
        }

        /// 
        /// ステータスクリア
        /// 
        public EventHandler OnGestureCommandRefresh {
            get { return refresh; }
            set { refresh = value; }
        }
        #endregion

        /// 
        /// 入力済みコマンドを初期化
        /// 
        public void Refresh() {
            commands.Length = 0;
            preArrow = "";
            prePos = new Point(0,0);
            
            if ( OnGestureCommandRefresh != null)
                OnGestureCommandRefresh(this, new EventArgs());
        }

        /// 
        /// ジェスチャー判定結果から処理を起動する
        /// 
        public void ExecuteGesture(object sender, System.EventArgs e) {
            string gesture = this.Gesture;
            this.Refresh();

            foreach( MouseGestureCommand m in menu ) {
                if ( m.Gesture == gesture ) {
                    m.RaiseEvent( sender, e );
                    break;
                }
            }
        }

        /// 
        /// マウスが移動した場合の処理
        /// 
        public void Gesuture_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e) {
            
            // 初回は無効
            if ( prePos.X == 0 ) {
                prePos = new Point(e.X, e.Y);
                return;
            }

            // 矢印の向きを取得
            string arrow = GetArrow( e.X - prePos.X, e.Y - prePos.Y );

            // 今回の位置を保持
            prePos = new Point(e.X, e.Y);

            // 前回と同じ向きの場合は無視
            if ( arrow == preArrow || arrow == string.Empty )
                return;

            // ジェスチャー追加
            commands.Append(arrow);

            // 今回のジェスチャーを保持
            preArrow = arrow;

            // この辺にステータスバーへ表示する処理を追加すると便利
            // (ex: StatusBar.Panels[0].Text = "ジェスチャー:" + this.Gesture + " " + this.MenuTitle;)
            if ( OnGestureCommandUpdate != null)
                OnGestureCommandUpdate( this, new EventArgs() );
        }

        /// 
        /// 始点と終点の差異から、移動した方向を決定する
        /// 
        /// 横軸の移動距離
        /// 縦軸の移動距離
        /// 移動の向き
        private string GetArrow(int xDiffer, int yDiffer) {
            // 感度以下の動きの場合は無視
            if ( Math.Abs(xDiffer) < MOUSE_SENSITIVITY 
                && Math.Abs(yDiffer) < MOUSE_SENSITIVITY )
                return string.Empty;

            // より縦向きにであれば縦と判定
            if ( Math.Abs(xDiffer) < Math.Abs(yDiffer) 
                && MOUSE_SENSITIVITY < Math.Abs(yDiffer) ) {
                // 縦方向判定
                if ( yDiffer > 0 ) 
                    return COMMAND_DOWN;
                
                return COMMAND_UP;
            }

            // 横方向判定
            if ( Math.Abs(xDiffer) < MOUSE_SENSITIVITY )
                return string.Empty;
        
            if ( xDiffer > 0) 
                return COMMAND_RIGHT;

            return COMMAND_LEFT;
        }
    }
}


使用例
Form1.cs

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;
using Panda728.Utility;

namespace MouseGesture
{
    /// 
    /// Form1 の概要の説明です。
    /// 
    public class Form1 : System.Windows.Forms.Form
    {
        private System.Windows.Forms.StatusBar statusBar1;
        private System.Windows.Forms.Panel gesturePanel;

        /// 
        /// 必要なデザイナ変数です。
        /// 
        private System.ComponentModel.Container components = null;

        // MouseGesture管理クラス
        private MouseGestureManager gestureManager = null;

        public Form1()
        {
            //
            // Windows フォーム デザイナ サポートに必要です。
            //
            InitializeComponent();

            // マウスジェスチャーの設定
            gestureManager = new MouseGestureManager();

            // ジェスチャーのステータス表示
            gestureManager.OnGestureCommandUpdate = new EventHandler(this.OnGestureCommandUpdate);
            gestureManager.OnGestureCommandRefresh = new EventHandler(this.OnGestureCommandRefresh);

            // 固有処理の追加
            gestureManager.Menu.Add( new MouseGestureCommand( "→", "進む", new EventHandler(gestureNext_Click)));
            gestureManager.Menu.Add( new MouseGestureCommand( "←", "戻る", new EventHandler(gesturePrevious_Click)));
            gestureManager.Menu.Add( new MouseGestureCommand( "↑", "上る", new EventHandler(gestureUp_Click)));
            
            // ジェスチャーイベント登録
            gesturePanel.MouseDown += new MouseEventHandler(this.gesturedControl_MouseDown);
            gesturePanel.MouseUp += new MouseEventHandler(this.gesturedControl_MouseUp);
        }

        /// 
        /// 使用されているリソースに後処理を実行します。
        /// 
        protected override void Dispose( bool disposing )
        {
            if( disposing )
            {
                if (components != null) 
                {
                    components.Dispose();
                }
            }
            base.Dispose( disposing );
        }

        #region Windows フォーム デザイナで生成されたコード 
        /// 
        /// デザイナ サポートに必要なメソッドです。このメソッドの内容を
        /// コード エディタで変更しないでください。
        /// 
        private void InitializeComponent()
        {
            this.gesturePanel = new System.Windows.Forms.Panel();
            this.statusBar1 = new System.Windows.Forms.StatusBar();
            this.gesturePanel.SuspendLayout();
            this.SuspendLayout();
            // 
            // gesturePanel
            // 
            this.gesturePanel.BackColor = System.Drawing.Color.SlateGray;
            this.gesturePanel.Controls.Add(this.statusBar1);
            this.gesturePanel.Dock = System.Windows.Forms.DockStyle.Fill;
            this.gesturePanel.Location = new System.Drawing.Point(0, 0);
            this.gesturePanel.Name = "gesturePanel";
            this.gesturePanel.Size = new System.Drawing.Size(292, 273);
            this.gesturePanel.TabIndex = 0;
            // 
            // statusBar1
            // 
            this.statusBar1.Location = new System.Drawing.Point(0, 251);
            this.statusBar1.Name = "statusBar1";
            this.statusBar1.Size = new System.Drawing.Size(292, 22);
            this.statusBar1.TabIndex = 0;
            // 
            // Form1
            // 
            this.AutoScaleBaseSize = new System.Drawing.Size(5, 12);
            this.ClientSize = new System.Drawing.Size(292, 273);
            this.Controls.Add(this.gesturePanel);
            this.Name = "Form1";
            this.Text = "MouseGestureSample";
            this.gesturePanel.ResumeLayout(false);
            this.ResumeLayout(false);

        }
        #endregion

        /// 
        /// アプリケーションのメイン エントリ ポイントです。
        /// 
        [STAThread]
        static void Main() {
            Application.Run(new Form1());
        }

        #region MouseGestureControlEvents
        /// 
        /// ジェスチャー入力開始
        /// 
        private void gesturedControl_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) {
            if ( e.Button != MouseButtons.Right )
                return;
            gesturePanel.MouseMove += new MouseEventHandler(this.gesturedControl_MouseMove);
        }

        /// 
        /// ジェスチャー入力
        /// 
        private void gesturedControl_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e) {
            gestureManager.Gesuture_MouseMove( sender, e );
        }

        /// 
        /// ジェスチャー確定
        /// 
        private void gesturedControl_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e) {
            if ( e.Button != MouseButtons.Right )
                return;
            
            gesturePanel.MouseMove -= new MouseEventHandler(this.gesturedControl_MouseMove);

            // コマンド実行
            gestureManager.ExecuteGesture(this, (System.EventArgs) e);
        }

        /// 
        /// 入力済みのジェスチャーを表示する
        /// 
        public void OnGestureCommandUpdate(object sender, EventArgs e) {
            this.statusBar1.Text = "ジェスチャー:" 
                + ((MouseGestureManager)sender).Gesture + " " 
                + ((MouseGestureManager)sender).MenuTitle;
        }

        /// 
        /// 入力済みのジェスチャーをクリア
        /// 
        public void OnGestureCommandRefresh(object sender, EventArgs e) {
            this.statusBar1.Text = string.Empty;
        }
        #endregion

        /// 
        /// 進む処理
        /// 
        public static void gestureNext_Click(object sender, System.EventArgs e) {
            MessageBox.Show("進むのDA!");
        }

        /// 
        /// 戻る処理
        /// 
        public static void gesturePrevious_Click(object sender, System.EventArgs e) {
            MessageBox.Show("戻るのだ!");
        }

        /// 
        /// 上る処理
        /// 
        private void gestureUp_Click(object sender, System.EventArgs e) {
            MessageBox.Show("上るのだ!");
        }
    }
}

C#の開発環境で、
MouseGestureManager.cs (ジェスチャー入力の管理する)
MouseGestureCommand.cs (ジェスチャーと名称とメソッドを一単位で管理)
のファイルを追加後、Form1.csの内容を上書きすれば動くはず。