//-----------------------------------------------------------------------------
// 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; }
}
}