Windows7のマルチタッチジェスチャを使う
とりあえずそれっぽい動きをしたので簡単なメモを残す。
今回やったのはマルチタッチジェスチャ。タッチ認識ではないほう。
基本的なこと
- ウィンドウメッセージ WM_GESTURE でジェスチャコマンドが送られてくる
- lParam を GetGestureInfo に渡して GESTUREINFO 構造体でデータを得る
- GESTUREINFO.dwID で何のジェスチャが実行されたか判別する。GCI_xxxx が対応する定数。
- GESTUREINFO.ptsLocation とかで位置を取れるけど、その位置が何を示すかはよくわからなかった。
ドキュメントとか
- マネージドでマルチタッチをやるサンプルはSDKの中に「Adding Manipulation support to Managed Code」にあるけど、なんかおかしい。
- リファレンスはGetting Started with Windows Touch Gestures (Windows)から関連するリンクをめぐるとよさそう。
とりあえず、自分が動かしたコード
Form1.cs (真ん中にpictureBox1をおいたフォーム)
using System; using System.Drawing; using EbiSoft.Library; namespace WindowsFormsApplication1 { public partial class Form1 : TouchableForm { private Point beginLocation = new Point(0, 0); private Size baseSize; private Point startCenter; private double baseDelta; public Form1() { InitializeComponent(); this.GestureBegin += new EventHandler<GestureEventArgs>(Form1_GestureBegin); this.GestureZoom += new EventHandler<GestureEventArgs>(Form1_GestureZoom); // 基準となるパラメータを記録する baseSize = pictureBox1.Size; startCenter = new Point(pictureBox1.Location.X + (baseSize.Width / 2), pictureBox1.Location.Y + (baseSize.Height / 2)); baseDelta = Math.Sqrt(Math.Pow((double)baseSize.Width, 2) + Math.Pow((double)baseSize.Height, 2)); } void Form1_GestureBegin(object sender, GestureEventArgs e) { // ジェスチャ開始位置を記録する beginLocation = e.Location; } void Form1_GestureZoom(object sender, GestureEventArgs e) { double sizeDelta = (baseDelta + Math.Sqrt(Math.Pow((double)(e.Location.X - beginLocation.X), 2) + Math.Pow((double)(e.Location.Y - beginLocation.Y), 2))) / baseDelta; // 新しいサイズにする pictureBox1.Size = new Size((int)(baseSize.Width * sizeDelta), (int)(baseSize.Height * sizeDelta)); // 新しい中心点を得る pictureBox1.Location = new Point(startCenter.X - (pictureBox1.Size.Width / 2), startCenter.Y - (pictureBox1.Size.Height / 2)); } } }
TouchableForm.cs
using System; using System.Windows.Forms; using System.Runtime.InteropServices; using System.Drawing; namespace EbiSoft.Library { public class TouchableForm : Form { #region P/Invoke 宣言 // タッチ有効・無効の切り替え [DllImport("user32")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool RegisterTouchWindow(System.IntPtr hWnd, ulong ulFlags); [DllImport("user32")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool UnregisterTouchWindow(System.IntPtr hWnd, ulong ulFlags); // ジェスチャ有効・無効の切り替え [DllImport("user32")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool RegisterGestureHandlerWindow(System.IntPtr hWnd, ulong ulFlags); [DllImport("user32")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool UnregisterGestureHandlerWindow(System.IntPtr hWnd, ulong ulFlags); // ジェスチャ詳細取得・開放 [DllImport("user32")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool GetGestureInfo(IntPtr hGestureInfo, ref GESTUREINFO pGestureInfo); [DllImport("user32")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool CloseGestureInfoHandle(IntPtr hGestureInfo); // ウィンドウメッセージ private const int WM_GESTURE = 0x0119; // ジェスチャフラグ private const int GF_BEGIN = 0x00000001; private const int GF_INERTIA = 0x00000002; private const int GF_END = 0x00000004; // ジェスチャコマンド private const int GID_BEGIN = 1; private const int GID_END = 2; private const int GID_ZOOM = 3; private const int GID_PAN = 4; private const int GID_ROTATE = 5; private const int GID_TWOFINGERTAP = 6; private const int GID_ROLLOVER = 7; // ポイント [StructLayout(LayoutKind.Sequential)] private struct POINTS { public short x; public short y; } // ジェスチャ情報 [StructLayout(LayoutKind.Sequential)] private struct GESTUREINFO { public int cbSize; public int dwFlags; public int dwID; public IntPtr hwndTarget; [MarshalAs(UnmanagedType.Struct)] public POINTS ptsLocation; public int dwInstanceID; public int dwSequenceID; public UInt64 ullArguments; public uint cbExtraArgs; } #endregion #region イベントハンドラ public event EventHandler<GestureEventArgs> GestureBegin; public event EventHandler<GestureEventArgs> GestureEnd; public event EventHandler<GestureEventArgs> GestureZoom; public event EventHandler<GestureEventArgs> GesturePan; public event EventHandler<GestureEventArgs> GestureRotate; public event EventHandler<GestureEventArgs> GestureTwoFingerTap; public event EventHandler<GestureEventArgs> GestureRollover; #endregion private bool canTouch; private bool canGesture; #region 初期化・開放 public TouchableForm() : base() { // タッチが有効かどうか確認する try { if (RegisterTouchWindow(this.Handle, 0)) { canTouch = true; UnregisterTouchWindow(this.Handle, 0); } else { canTouch = false; } } catch (Exception) { canTouch = false; } // ジェスチャが有効かどうか確認する try { if (RegisterGestureHandlerWindow(this.Handle, 0)) { canGesture = true; } else { canGesture = false; } } catch (Exception) { canGesture = false; } } protected override void Dispose(bool disposing) { if (disposing) { UnregisterGestureHandlerWindow(this.Handle, 0); UnregisterTouchWindow(this.Handle, 0); } base.Dispose(disposing); } #endregion protected override void WndProc(ref Message m) { switch (m.Msg) { case WM_GESTURE: DecodeGesture(ref m); break; default: break; } base.WndProc(ref m); } private void DecodeGesture(ref Message m) { GESTUREINFO gi = new GESTUREINFO(); gi.cbSize = Marshal.SizeOf(typeof(GESTUREINFO)); if (GetGestureInfo(m.LParam, ref gi)) { try { Point gestureLocation = new Point(gi.ptsLocation.x, gi.ptsLocation.y); switch (gi.dwID) { case GID_BEGIN: System.Diagnostics.Debug.WriteLine("GID_BEGIN"); if (GestureBegin != null) GestureBegin(this, new GestureEventArgs(gestureLocation)); break; case GID_END: System.Diagnostics.Debug.WriteLine("GID_END"); if (GestureEnd != null) GestureEnd(this, new GestureEventArgs(gestureLocation)); break; case GID_ZOOM: System.Diagnostics.Debug.WriteLine("GID_ZOOM"); if (GestureZoom != null) GestureZoom(this, new GestureEventArgs(gestureLocation)); break; case GID_PAN: System.Diagnostics.Debug.WriteLine("GID_PAN"); if (GesturePan != null) GesturePan(this, new GestureEventArgs(gestureLocation)); break; case GID_ROTATE: System.Diagnostics.Debug.WriteLine("GID_ROTATE"); if (GestureRotate != null) GestureRotate(this, new GestureEventArgs(gestureLocation)); break; case GID_TWOFINGERTAP: System.Diagnostics.Debug.WriteLine("GID_TWOFINGERTAP"); if (GestureTwoFingerTap != null) GestureTwoFingerTap(this, new GestureEventArgs(gestureLocation)); break; case GID_ROLLOVER: System.Diagnostics.Debug.WriteLine("GID_ROLLOVER"); if (GestureRollover != null) GestureRollover(this, new GestureEventArgs(gestureLocation)); break; default: // You have encountered an unknown gesture break; } } finally { CloseGestureInfoHandle(m.LParam); } } } #region プロパティ // ジェスチャ機能が使用可能かどうかを取得します。 public bool CanGesture { get { return canGesture; } } /// <summary> /// タッチ機能が有効かどうかを取得します。 /// </summary> public bool CanTouch { get { return canTouch; } } #endregion } /// <summary> /// ジェスチャのイベントデータ /// </summary> public class GestureEventArgs : System.EventArgs { //------------------------ // Private members //------------------------ private Point location; //------------------------ // Public members //------------------------ public Point Location { get { return location; } } //----------------------- // Public methods //------------------------ public GestureEventArgs(Point location) { this.location = location; } } /// <summary> /// タッチのイベントデータ /// </summary> public class TouchEventArgs : System.EventArgs { //------------------------ // Private members //------------------------ private int id; private int mask; private int time; private Point location; private Point contactLocation; //------------------------ // Public members //------------------------ public Point Location { get { return location; } } public Point ContactLocation { get { return contactLocation; } } public int Id { get { return id; } } public int Mask { get { return mask; } } public int Time { get { return time; } } //----------------------- // Public methods //------------------------ public TouchEventArgs(Point location, int id, int mask, int time, Point contactLocation) { this.location = location; this.id = id; this.mask = mask; this.time = time; this.contactLocation = contactLocation; } } }