//----------------------------------------------------------------------------- // Copyright (C) Microsoft Corporation. All rights reserved. //----------------------------------------------------------------------------- using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.InteropServices; using Windows7.Multitouch.Interop; using System.Drawing; namespace Windows7.Multitouch { /// /// Handles gesture events /// /// /// The handler simplifies handling gesture such as rotate, zoom and pan /// by keeping the requires knowledge of the previous and first event in /// the gesture event sequence. /// public class GestureHandler : Handler { //Simplifying event handling by not dealing with empty event private static readonly EventHandler _emptyFunc = (s,e) => {}; //Using this map, fireing event is easy private readonly Dictionary> _eventMap = new Dictionary> { {EventMapID.Begin, _emptyFunc}, {EventMapID.End, _emptyFunc}, {EventMapID.PanBegin, _emptyFunc}, {EventMapID.Pan, _emptyFunc}, {EventMapID.PanEnd, _emptyFunc}, {EventMapID.PressAndTap, _emptyFunc}, {EventMapID.RotateBegin, _emptyFunc}, {EventMapID.Rotate, _emptyFunc}, {EventMapID.RotateEnd, _emptyFunc}, {EventMapID.TwoFingerTap, _emptyFunc}, {EventMapID.ZoomBegin, _emptyFunc}, {EventMapID.Zoom, _emptyFunc}, {EventMapID.ZoomEnd, _emptyFunc} }; //All gesture events private static class EventMapID { public static readonly uint Begin = MapWM2EventId(User32.GID_BEGIN, 0); public static readonly uint End = MapWM2EventId(User32.GID_END, 0); public static readonly uint PanBegin = MapWM2EventId(User32.GID_PAN, User32.GF_BEGIN); public static readonly uint Pan = MapWM2EventId(User32.GID_PAN, 0); public static readonly uint PanEnd = MapWM2EventId(User32.GID_PAN, User32.GF_END); public static readonly uint PressAndTap = MapWM2EventId(User32.GID_PRESSANDTAP, 0); public static readonly uint RotateBegin = MapWM2EventId(User32.GID_ROTATE, User32.GF_BEGIN); public static readonly uint Rotate = MapWM2EventId(User32.GID_ROTATE, 0); public static readonly uint RotateEnd = MapWM2EventId(User32.GID_ROTATE, User32.GF_END); public static readonly uint TwoFingerTap = MapWM2EventId(User32.GID_TWOFINGERTAP, 0); public static readonly uint ZoomBegin = MapWM2EventId(User32.GID_ZOOM, User32.GF_BEGIN); public static readonly uint Zoom = MapWM2EventId(User32.GID_ZOOM, 0); public static readonly uint ZoomEnd = MapWM2EventId(User32.GID_ZOOM, User32.GF_END); } //A magical mapping of WM Gesture id and flags to a unique event entry in the event map private static uint MapWM2EventId(uint dwID, uint dwFlags) { return (dwID << 3) + (dwID == User32.GID_TWOFINGERTAP || dwID == User32.GID_PRESSANDTAP || dwID == User32.GID_BEGIN || dwID == User32.GID_END ? 0 : dwFlags & 5); } /// /// Construct a gesture handler instance /// /// The target control wrapper internal GestureHandler(IHwndWrapper hWndWrapper) : base(hWndWrapper) { } /// /// Register the form to get gesture events /// /// true if succeeded protected override bool SetHWndTouchInfo() { GESTURECONFIG[] gestureConfig = new[] { new GESTURECONFIG { dwID = 0, dwWant = User32.GC_ALLGESTURES, dwBlock = 0 } }; return User32.SetGestureConfig(ControlHandle, 0, 1, gestureConfig, (uint)Marshal.SizeOf(typeof(GESTURECONFIG))); } /// /// The event that started the current gesture /// internal GestureEventArgs LastBeginEvent { get; set; } /// /// The last event in the current gesture event sequence /// internal GestureEventArgs LastEvent { get; set; } // Gesture event handlers /// /// Indicate a that a gesture is beginning /// public event EventHandler Begin { add { _eventMap[EventMapID.Begin] += value; } remove { _eventMap[EventMapID.Begin] -= value; } } /// /// Indicate an end of a gesture /// public event EventHandler End { add { _eventMap[EventMapID.End] += value; } remove { _eventMap[EventMapID.End] -= value; } } /// /// Start the pannin sequence /// public event EventHandler PanBegin { add { _eventMap[EventMapID.PanBegin] += value; } remove { _eventMap[EventMapID.PanBegin] -= value; } } /// /// Panning continue /// /// /// /// Use the PanTranslation property of the event argument to get the /// relative translation size (relative to the last pan event) /// public event EventHandler Pan { add { _eventMap[EventMapID.Pan] += value; } remove { _eventMap[EventMapID.Pan] -= value; } } /// /// End pan event /// public event EventHandler PanEnd { add { _eventMap[EventMapID.PanEnd] += value; } remove { _eventMap[EventMapID.PanEnd] -= value; } } /// /// RollOver gesture event, this is a single event /// public event EventHandler PressAndTap { add { _eventMap[EventMapID.PressAndTap] += value; } remove { _eventMap[EventMapID.PressAndTap] -= value; } } /// /// Starting rotate gesture /// public event EventHandler RotateBegin { add { _eventMap[EventMapID.RotateBegin] += value; } remove { _eventMap[EventMapID.RotateBegin] -= value; } } /// /// Continue rotating /// /// /// Use the RotateAngle in the event argument to get the relative /// rotation angle /// public event EventHandler Rotate { add { _eventMap[EventMapID.Rotate] += value; } remove { _eventMap[EventMapID.Rotate] -= value; } } /// /// Rotate end /// public event EventHandler RotateEnd { add { _eventMap[EventMapID.RotateEnd] += value; } remove { _eventMap[EventMapID.RotateEnd] -= value; } } /// /// Two fingers tap event. /// /// /// This is a single event /// public event EventHandler TwoFingerTap { add { _eventMap[EventMapID.TwoFingerTap] += value; } remove { _eventMap[EventMapID.TwoFingerTap] -= value; } } /// /// Start zoom gesture /// public event EventHandler ZoomBegin { add { _eventMap[EventMapID.ZoomBegin] += value; } remove { _eventMap[EventMapID.ZoomBegin] -= value; } } /// /// Continue zooming /// /// /// Use the ZoomFactor to know the relative zoom factor /// public event EventHandler Zoom { add { _eventMap[EventMapID.Zoom] += value; } remove { _eventMap[EventMapID.Zoom] -= value; } } /// /// Zoom End event /// public event EventHandler ZoomEnd { add { _eventMap[EventMapID.ZoomEnd] += value; } remove { _eventMap[EventMapID.ZoomEnd] -= value; } } /// /// The Windows message interception for gesture events handling /// /// WndProc hWnd /// WndProc msg /// WndProc wParam /// WndProc lParam /// WndProc return protected override uint WindowProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam) { //We care only for gesture events if (msg != User32.WM_GESTURE) return 0; GESTUREINFO gestureInfo = new GESTUREINFO { cbSize = (uint)Marshal.SizeOf(typeof(GESTUREINFO)) }; bool result = User32.GetGestureInfo(lParam, ref gestureInfo); if (!result) throw new Exception("Cannot get gesture information"); //Decode the gesture info and get the message event argument GestureEventArgs eventArgs = new GestureEventArgs(this, ref gestureInfo); try { //Fire the event using the event map _eventMap[MapWM2EventId(gestureInfo.dwID, gestureInfo.dwFlags)].Invoke(this, eventArgs); } catch (ArgumentOutOfRangeException) //In case future releases will introduce new event values { } //Keep the last message for relative calculations LastEvent = eventArgs; //Keep the first message for relative calculations if (eventArgs.IsBegin) LastBeginEvent = eventArgs; User32.CloseGestureInfoHandle(lParam); return 1; } } /// /// Event arguments for all gesture events /// /// /// Some of the properties are related to specific messages: /// Panning: PanTranslation /// Zooming: ZoomFactor /// Rotation: RotateAngle /// public class GestureEventArgs : EventArgs { private readonly uint _dwFlags; /// /// Create new gesture event instance and decode the gesture info structure /// /// The gesture handler /// The gesture information internal GestureEventArgs(GestureHandler handler, ref GESTUREINFO gestureInfo) { _dwFlags = gestureInfo.dwFlags; GestureId = gestureInfo.dwID; GestureArguments = gestureInfo.ullArguments; //Get the last event from the handler LastEvent = handler.LastEvent; //Get the last begin event from the handler LastBeginEvent = handler.LastBeginEvent; DecodeGesture(handler.HWndWrapper, ref gestureInfo); //new gesture, clear last and first event fields if (IsBegin) { LastBeginEvent = null; LastEvent = null; } } //Decode the gesture private void DecodeGesture(IHwndWrapper hWndWrapper, ref GESTUREINFO gestureInfo) { Location = hWndWrapper.PointToClient(new Point(gestureInfo.ptsLocation.x, gestureInfo.ptsLocation.y)); Center = Location; switch (GestureId) { case User32.GID_ROTATE: ushort lastArguments = (ushort)(IsBegin ? 0 : LastEvent.GestureArguments); RotateAngle = User32.GID_ROTATE_ANGLE_FROM_ARGUMENT((ushort)(gestureInfo.ullArguments - lastArguments)); break; case User32.GID_ZOOM: Point first = IsBegin ? Location : LastBeginEvent.Location; Center = new Point((Location.X + first.X) / 2, (Location.Y + first.Y) / 2); ZoomFactor = IsBegin ? 1 : (double)gestureInfo.ullArguments / LastEvent.GestureArguments; //DistanceBetweenFingers = User32.LoDWord(gestureInfo.ullArguments); break; case User32.GID_PAN: PanTranslation = IsBegin ? new Size(0, 0) : new Size(Location.X - LastEvent.Location.X, Location.Y - LastEvent.Location.Y); int panVelocity = User32.HiDWord((long)(gestureInfo.ullArguments)); PanVelocity = new Size(User32.LoWord(panVelocity), User32.HiWord(panVelocity)); //DistanceBetweenFingers = User32.LoDWord(gestureInfo.ullArguments); break; } } /// /// The windows gesture id /// public uint GestureId { get; private set; } /// /// the raw Gesture arguments /// public ulong GestureArguments { get; private set; } /// /// The gesture location translated into client area /// public Point Location { get; private set; } /// /// The first event of a gesture /// public bool IsBegin { get { return (_dwFlags & User32.GF_BEGIN) != 0; } } /// /// The last event of a gesture /// public bool IsEnd { get { return (_dwFlags & User32.GF_END) != 0; } } /// /// The gesture has triggered inertia /// public bool IsInertia { get { return (_dwFlags & User32.GF_INERTIA) != 0; } } /// /// The relative rotation angle, used by the Rotate event /// public double RotateAngle { get; private set; } /// /// The calculated gesture center /// public Point Center {get; private set; } /// /// The zoom factor, used by the Zoom event /// public double ZoomFactor {get; private set; } /// /// The relative panning translation, used by the Pan event /// public Size PanTranslation {get; private set; } /// /// The velocity vector of the pan gesture, can be used for custom inertia /// public Size PanVelocity { get; private set; } /// /// The distance between fingers in the Pan and Zoom gestures /// //public uint DistanceBetweenFingers { get; private set; } /// /// The first gesture in this gesture event sequence /// public GestureEventArgs LastBeginEvent {get; internal set; } /// /// The last gesture in this gesture event sequence /// public GestureEventArgs LastEvent {get; internal set; } } }