917 lines
31 KiB
C#
917 lines
31 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Text;
|
|
using System.Windows.Forms;
|
|
using System.Drawing;
|
|
using System.Runtime.InteropServices;
|
|
using System.Data;
|
|
|
|
namespace DevComponents.DotNetBar.Touch
|
|
{
|
|
internal class TouchHandler
|
|
{
|
|
#region Events
|
|
public event EventHandler<GestureEventArgs> PanBegin;
|
|
/// <summary>
|
|
/// Raises PanBegin event.
|
|
/// </summary>
|
|
/// <param name="e">Provides event arguments.</param>
|
|
protected virtual void OnPanBegin(GestureEventArgs e)
|
|
{
|
|
EventHandler<GestureEventArgs> handler = PanBegin;
|
|
if (handler != null)
|
|
handler(this, e);
|
|
}
|
|
public event EventHandler<GestureEventArgs> PanEnd;
|
|
/// <summary>
|
|
/// Raises PanBegin event.
|
|
/// </summary>
|
|
/// <param name="e">Provides event arguments.</param>
|
|
protected virtual void OnPanEnd(GestureEventArgs e)
|
|
{
|
|
EventHandler<GestureEventArgs> handler = PanEnd;
|
|
if (handler != null)
|
|
handler(this, e);
|
|
}
|
|
public event EventHandler<GestureEventArgs> Pan;
|
|
/// <summary>
|
|
/// Raises PanBegin event.
|
|
/// </summary>
|
|
/// <param name="e">Provides event arguments.</param>
|
|
protected virtual void OnPan(GestureEventArgs e)
|
|
{
|
|
EventHandler<GestureEventArgs> handler = Pan;
|
|
if (handler != null)
|
|
handler(this, e);
|
|
}
|
|
|
|
public event EventHandler<GestureEventArgs> Begin;
|
|
/// <summary>
|
|
/// Raises Begin event.
|
|
/// </summary>
|
|
/// <param name="e">Provides event arguments.</param>
|
|
protected virtual void OnBegin(GestureEventArgs e)
|
|
{
|
|
EventHandler<GestureEventArgs> handler = Begin;
|
|
if (handler != null)
|
|
handler(this, e);
|
|
}
|
|
|
|
public event EventHandler<GestureEventArgs> End;
|
|
/// <summary>
|
|
/// Raises End event.
|
|
/// </summary>
|
|
/// <param name="e">Provides event arguments.</param>
|
|
protected virtual void OnEnd(GestureEventArgs e)
|
|
{
|
|
EventHandler<GestureEventArgs> handler = End;
|
|
if (handler != null)
|
|
handler(this, e);
|
|
}
|
|
|
|
public event EventHandler<GestureEventArgs> PressAndTap;
|
|
/// <summary>
|
|
/// Raises PressAndTap event.
|
|
/// </summary>
|
|
/// <param name="e">Provides event arguments.</param>
|
|
protected virtual void OnPressAndTap(GestureEventArgs e)
|
|
{
|
|
EventHandler<GestureEventArgs> handler = PressAndTap;
|
|
if (handler != null)
|
|
handler(this, e);
|
|
}
|
|
|
|
public event EventHandler<GestureEventArgs> RotateBegin;
|
|
/// <summary>
|
|
/// Raises RotateBegin event.
|
|
/// </summary>
|
|
/// <param name="e">Provides event arguments.</param>
|
|
protected virtual void OnRotateBegin(GestureEventArgs e)
|
|
{
|
|
EventHandler<GestureEventArgs> handler = RotateBegin;
|
|
if (handler != null)
|
|
handler(this, e);
|
|
}
|
|
|
|
public event EventHandler<GestureEventArgs> Rotate;
|
|
/// <summary>
|
|
/// Raises Rotate event.
|
|
/// </summary>
|
|
/// <param name="e">Provides event arguments.</param>
|
|
protected virtual void OnRotate(GestureEventArgs e)
|
|
{
|
|
EventHandler<GestureEventArgs> handler = Rotate;
|
|
if (handler != null)
|
|
handler(this, e);
|
|
}
|
|
|
|
public event EventHandler<GestureEventArgs> RotateEnd;
|
|
/// <summary>
|
|
/// Raises RotateEnd event.
|
|
/// </summary>
|
|
/// <param name="e">Provides event arguments.</param>
|
|
protected virtual void OnRotateEnd(GestureEventArgs e)
|
|
{
|
|
EventHandler<GestureEventArgs> handler = RotateEnd;
|
|
if (handler != null)
|
|
handler(this, e);
|
|
}
|
|
|
|
public event EventHandler<GestureEventArgs> TwoFingerTap;
|
|
/// <summary>
|
|
/// Raises TwoFingerTap event.
|
|
/// </summary>
|
|
/// <param name="e">Provides event arguments.</param>
|
|
protected virtual void OnTwoFingerTap(GestureEventArgs e)
|
|
{
|
|
EventHandler<GestureEventArgs> handler = TwoFingerTap;
|
|
if (handler != null)
|
|
handler(this, e);
|
|
}
|
|
|
|
public event EventHandler<GestureEventArgs> ZoomBegin;
|
|
/// <summary>
|
|
/// Raises ZoomBegin event.
|
|
/// </summary>
|
|
/// <param name="e">Provides event arguments.</param>
|
|
protected virtual void OnZoomBegin(GestureEventArgs e)
|
|
{
|
|
EventHandler<GestureEventArgs> handler = ZoomBegin;
|
|
if (handler != null)
|
|
handler(this, e);
|
|
}
|
|
|
|
public event EventHandler<GestureEventArgs> Zoom;
|
|
/// <summary>
|
|
/// Raises Zoom event.
|
|
/// </summary>
|
|
/// <param name="e">Provides event arguments.</param>
|
|
protected virtual void OnZoom(GestureEventArgs e)
|
|
{
|
|
EventHandler<GestureEventArgs> handler = Zoom;
|
|
if (handler != null)
|
|
handler(this, e);
|
|
}
|
|
|
|
public event EventHandler<GestureEventArgs> ZoomEnd;
|
|
/// <summary>
|
|
/// Raises ZoomEnd event.
|
|
/// </summary>
|
|
/// <param name="e">Provides event arguments.</param>
|
|
protected virtual void OnZoomEnd(GestureEventArgs e)
|
|
{
|
|
EventHandler<GestureEventArgs> handler = ZoomEnd;
|
|
if (handler != null)
|
|
handler(this, e);
|
|
}
|
|
|
|
// Touch events
|
|
public event EventHandler<TouchEventArgs> TouchDown;
|
|
/// <summary>
|
|
/// Raises TouchDown event.
|
|
/// </summary>
|
|
/// <param name="e">Provides event arguments.</param>
|
|
protected virtual void OnTouchDown(TouchEventArgs e)
|
|
{
|
|
EventHandler<TouchEventArgs> handler = TouchDown;
|
|
if (handler != null)
|
|
handler(this, e);
|
|
}
|
|
|
|
public event EventHandler<TouchEventArgs> TouchUp;
|
|
/// <summary>
|
|
/// Raises TouchDown event.
|
|
/// </summary>
|
|
/// <param name="e">Provides event arguments.</param>
|
|
protected virtual void OnTouchUp(TouchEventArgs e)
|
|
{
|
|
EventHandler<TouchEventArgs> handler = TouchUp;
|
|
if (handler != null)
|
|
handler(this, e);
|
|
}
|
|
|
|
public event EventHandler<TouchEventArgs> TouchMove;
|
|
/// <summary>
|
|
/// Raises TouchDown event.
|
|
/// </summary>
|
|
/// <param name="e">Provides event arguments.</param>
|
|
protected virtual void OnTouchMove(TouchEventArgs e)
|
|
{
|
|
EventHandler<TouchEventArgs> handler = TouchMove;
|
|
if (handler != null)
|
|
handler(this, e);
|
|
}
|
|
#endregion
|
|
|
|
#region Constructor
|
|
private Control _ParentControl = null;
|
|
private eTouchHandlerType _HandlerType = eTouchHandlerType.Gesture;
|
|
/// <summary>
|
|
/// Initializes a new instance of the TouchHandler class.
|
|
/// </summary>
|
|
/// <param name="parentControl"></param>
|
|
public TouchHandler(Control parentControl) : this(parentControl, eTouchHandlerType.Gesture) { }
|
|
|
|
/// <summary>
|
|
/// Initializes a new instance of the TouchHandler class.
|
|
/// </summary>
|
|
/// <param name="parentControl"></param>
|
|
public TouchHandler(Control parentControl, eTouchHandlerType handlerType)
|
|
{
|
|
_ParentControl = parentControl;
|
|
_HandlerType = handlerType;
|
|
if (IsTouchEnabled)
|
|
{
|
|
if (_ParentControl.IsHandleCreated)
|
|
Initialize();
|
|
else
|
|
_ParentControl.HandleCreated += new EventHandler(ParentControlHandleCreated);
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Implementation
|
|
void ParentControlHandleCreated(object sender, EventArgs e)
|
|
{
|
|
Initialize();
|
|
}
|
|
|
|
private IntPtr _originalWindowProcId;
|
|
private WinApi.WindowProcDelegate _windowProcDelegate;
|
|
/// <summary>
|
|
/// Initializes handler
|
|
/// </summary>
|
|
private void Initialize()
|
|
{
|
|
if (!RegisterTouchWindow())
|
|
{
|
|
throw new NotSupportedException("Cannot register window");
|
|
}
|
|
|
|
_windowProcDelegate = WindowProc;
|
|
|
|
_originalWindowProcId = IntPtr.Size == 4 ?
|
|
WinApi.SubclassWindow(_ParentControl.Handle, WinApi.GWLP_WNDPROC, _windowProcDelegate) :
|
|
WinApi.SubclassWindow64(_ParentControl.Handle, WinApi.GWLP_WNDPROC, _windowProcDelegate);
|
|
|
|
//take the desktop DPI
|
|
using (Graphics graphics = Graphics.FromHwnd(_ParentControl.Handle))
|
|
{
|
|
DpiX = graphics.DpiX;
|
|
DpiY = graphics.DpiY;
|
|
}
|
|
}
|
|
|
|
|
|
public Control ParentControl
|
|
{
|
|
get { return _ParentControl; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// The Windows message handler.
|
|
/// </summary>
|
|
private uint WindowProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam)
|
|
{
|
|
if(msg == WinApi.WM_TOUCH && (_HandlerType & eTouchHandlerType.Touch)== eTouchHandlerType.Touch)
|
|
{
|
|
foreach (TouchEventArgs arg in DecodeMessage(hWnd, msg, wParam, lParam, DpiX, DpiY))
|
|
{
|
|
|
|
if (arg.IsTouchDown)
|
|
OnTouchDown(arg);
|
|
|
|
if (arg.IsTouchMove)
|
|
OnTouchMove(arg);
|
|
|
|
if (arg.IsTouchUp)
|
|
OnTouchUp(arg);
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
// Handle only gesture message
|
|
if (msg != WinApi.WM_GESTURE)
|
|
{
|
|
return WinApi.CallWindowProc(_originalWindowProcId, hWnd, msg, wParam, lParam);
|
|
}
|
|
|
|
WinApi.GESTUREINFO gestureInfo = new WinApi.GESTUREINFO();
|
|
gestureInfo.cbSize = (uint)Marshal.SizeOf(typeof(WinApi.GESTUREINFO));
|
|
|
|
bool result = WinApi.GetGestureInfo(lParam, ref gestureInfo);
|
|
|
|
if (!result)
|
|
throw new Exception("Cannot retrieve 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
|
|
uint gestureId = GetGestureEventId(gestureInfo.dwID, gestureInfo.dwFlags);
|
|
if (gestureId == GestureEventId.Begin)
|
|
OnBegin(eventArgs);
|
|
else if (gestureId == GestureEventId.End)
|
|
OnEnd(eventArgs);
|
|
else if (gestureId == GestureEventId.Pan)
|
|
OnPan(eventArgs);
|
|
else if (gestureId == GestureEventId.PanBegin)
|
|
OnPanBegin(eventArgs);
|
|
else if (gestureId == GestureEventId.PanEnd)
|
|
OnPanEnd(eventArgs);
|
|
else if (gestureId == GestureEventId.PressAndTap)
|
|
OnPressAndTap(eventArgs);
|
|
else if (gestureId == GestureEventId.Rotate)
|
|
OnRotate(eventArgs);
|
|
else if (gestureId == GestureEventId.RotateBegin)
|
|
OnRotateBegin(eventArgs);
|
|
else if (gestureId == GestureEventId.RotateEnd)
|
|
OnRotateEnd(eventArgs);
|
|
else if (gestureId == GestureEventId.TwoFingerTap)
|
|
OnTwoFingerTap(eventArgs);
|
|
else if (gestureId == GestureEventId.Zoom)
|
|
OnZoom(eventArgs);
|
|
else if (gestureId == GestureEventId.ZoomBegin)
|
|
OnZoomBegin(eventArgs);
|
|
else if (gestureId == GestureEventId.ZoomEnd)
|
|
OnZoomEnd(eventArgs);
|
|
}
|
|
catch (ArgumentOutOfRangeException) //In case future releases will introduce new event values
|
|
{
|
|
}
|
|
|
|
//Keep the last message for relative calculations
|
|
LastEventArgs = eventArgs;
|
|
|
|
//Keep the first message for relative calculations
|
|
if (eventArgs.IsBegin)
|
|
LastBeginEventArgs = eventArgs;
|
|
|
|
WinApi.CloseGestureInfoHandle(lParam);
|
|
|
|
return 1;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Decode the message and create a collection of event arguments
|
|
/// </summary>
|
|
private IEnumerable<TouchEventArgs> DecodeMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, float dpiX, float dpiY)
|
|
{
|
|
// More than one touch input can be associated with a touch message
|
|
int inputCount = WinApi.LoWord(wParam.ToInt32()); // Number of touch inputs, actual per-contact messages
|
|
|
|
WinApi.TOUCHINPUT[] inputs; // Array of TOUCHINPUT structures
|
|
inputs = new WinApi.TOUCHINPUT[inputCount]; // Allocate the storage for the parameters of the per-contact messages
|
|
try
|
|
{
|
|
// Unpack message parameters into the array of TOUCHINPUT structures, each representing a message for one single contact.
|
|
if (!WinApi.GetTouchInputInfo(lParam, inputCount, inputs, Marshal.SizeOf(inputs[0])))
|
|
{
|
|
// Touch info failed.
|
|
throw new Exception("Error calling GetTouchInputInfo API");
|
|
}
|
|
|
|
// For each contact, dispatch the message to the appropriate message handler.
|
|
// For WM_TOUCHDOWN you can get down & move notifications and for WM_TOUCHUP you can get up & move notifications
|
|
// WM_TOUCHMOVE will only contain move notifications and up & down notifications will never come in the same message
|
|
for (int i = 0; i < inputCount; i++)
|
|
{
|
|
TouchEventArgs touchEventArgs = new TouchEventArgs(this, dpiX, dpiY, ref inputs[i]);
|
|
yield return touchEventArgs;
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
WinApi.CloseTouchInputHandle(lParam);
|
|
}
|
|
}
|
|
|
|
private GestureEventArgs _LastBeginEventArgs;
|
|
/// <summary>
|
|
/// The event arguments that started the current gesture
|
|
/// </summary>
|
|
internal GestureEventArgs LastBeginEventArgs
|
|
{
|
|
get { return _LastBeginEventArgs; }
|
|
set { _LastBeginEventArgs = value; }
|
|
}
|
|
/// <summary>
|
|
/// The last event in the current gesture event sequence
|
|
/// </summary>
|
|
private GestureEventArgs _LastEventArgs;
|
|
internal GestureEventArgs LastEventArgs
|
|
{
|
|
get { return _LastEventArgs; }
|
|
set { _LastEventArgs = value; }
|
|
}
|
|
|
|
|
|
private static class GestureEventId
|
|
{
|
|
public static readonly uint Begin = GetGestureEventId(WinApi.GID_BEGIN, 0);
|
|
public static readonly uint End = GetGestureEventId(WinApi.GID_END, 0);
|
|
public static readonly uint PanBegin = GetGestureEventId(WinApi.GID_PAN, WinApi.GF_BEGIN);
|
|
public static readonly uint Pan = GetGestureEventId(WinApi.GID_PAN, 0);
|
|
public static readonly uint PanEnd = GetGestureEventId(WinApi.GID_PAN, WinApi.GF_END);
|
|
public static readonly uint PressAndTap = GetGestureEventId(WinApi.GID_PRESSANDTAP, 0);
|
|
public static readonly uint RotateBegin = GetGestureEventId(WinApi.GID_ROTATE, WinApi.GF_BEGIN);
|
|
public static readonly uint Rotate = GetGestureEventId(WinApi.GID_ROTATE, 0);
|
|
public static readonly uint RotateEnd = GetGestureEventId(WinApi.GID_ROTATE, WinApi.GF_END);
|
|
public static readonly uint TwoFingerTap = GetGestureEventId(WinApi.GID_TWOFINGERTAP, 0);
|
|
public static readonly uint ZoomBegin = GetGestureEventId(WinApi.GID_ZOOM, WinApi.GF_BEGIN);
|
|
public static readonly uint Zoom = GetGestureEventId(WinApi.GID_ZOOM, 0);
|
|
public static readonly uint ZoomEnd = GetGestureEventId(WinApi.GID_ZOOM, WinApi.GF_END);
|
|
}
|
|
private static uint GetGestureEventId(uint dwID, uint dwFlags)
|
|
{
|
|
return (dwID << 3) + (dwID == WinApi.GID_TWOFINGERTAP || dwID == WinApi.GID_PRESSANDTAP
|
|
|| dwID == WinApi.GID_BEGIN || dwID == WinApi.GID_END ?
|
|
0 : dwFlags & 5);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Register for touch event
|
|
/// </summary>
|
|
/// <returns>true if succeeded</returns>
|
|
private bool RegisterTouchWindow()
|
|
{
|
|
bool result = false;
|
|
if ((_HandlerType & eTouchHandlerType.Gesture) == eTouchHandlerType.Gesture)
|
|
{
|
|
WinApi.GESTURECONFIG[] gestureConfig = new WinApi.GESTURECONFIG[] { new WinApi.GESTURECONFIG(0, WinApi.GC_ALLGESTURES, 0) };
|
|
result = WinApi.SetGestureConfig(_ParentControl.Handle, 0, 1, gestureConfig, (uint)Marshal.SizeOf(typeof(WinApi.GESTURECONFIG)));
|
|
}
|
|
|
|
if ((_HandlerType & eTouchHandlerType.Touch) == eTouchHandlerType.Touch)
|
|
{
|
|
result |= WinApi.RegisterTouchWindow(_ParentControl.Handle, _DisablePalmRejection ? WinApi.TouchWindowFlag.WantPalm : 0);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
private bool _DisablePalmRejection;
|
|
/// <summary>
|
|
/// Gets or sets whether palm rejection is enabled.
|
|
/// </summary>
|
|
public bool DisablePalmRejection
|
|
{
|
|
get
|
|
{
|
|
return _DisablePalmRejection;
|
|
}
|
|
set
|
|
{
|
|
if (_DisablePalmRejection == value)
|
|
return;
|
|
|
|
_DisablePalmRejection = value;
|
|
|
|
if (_ParentControl.IsHandleCreated)
|
|
{
|
|
WinApi.UnregisterTouchWindow(_ParentControl.Handle);
|
|
RegisterTouchWindow();
|
|
}
|
|
}
|
|
}
|
|
|
|
private float _DpiX;
|
|
public float DpiX
|
|
{
|
|
get { return _DpiX; }
|
|
set { _DpiX = value; }
|
|
}
|
|
|
|
private float _DpiY;
|
|
public float DpiY
|
|
{
|
|
get { return _DpiY; }
|
|
set { _DpiY = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check if Multi-touch support device is ready
|
|
/// </summary>
|
|
public static bool IsTouchEnabled
|
|
{
|
|
get
|
|
{
|
|
return (WinApi.GetDigitizerStatus() & (WinApi.DigitizerStatus.StackReady | WinApi.DigitizerStatus.MultiInput)) != 0;
|
|
}
|
|
}
|
|
#endregion
|
|
}
|
|
[Flags]
|
|
internal enum eTouchHandlerType : short
|
|
{
|
|
Gesture = 1,
|
|
Touch = 2
|
|
}
|
|
|
|
internal class GestureEventArgs : EventArgs
|
|
{
|
|
private readonly uint _Flags;
|
|
|
|
/// <summary>
|
|
/// Create new gesture event instance and decode the gesture info structure
|
|
/// </summary>
|
|
/// <param name="handler">The gesture handler</param>
|
|
/// <param name="gestureInfo">The gesture information</param>
|
|
internal GestureEventArgs(TouchHandler handler, ref WinApi.GESTUREINFO gestureInfo)
|
|
{
|
|
_Flags = gestureInfo.dwFlags;
|
|
GestureId = gestureInfo.dwID;
|
|
GestureArguments = gestureInfo.ullArguments;
|
|
|
|
//Get the last event from the handler
|
|
LastEvent = handler.LastEventArgs;
|
|
|
|
//Get the last begin event from the handler
|
|
LastBeginEvent = handler.LastBeginEventArgs;
|
|
|
|
ParseGesture(handler.ParentControl, ref gestureInfo);
|
|
|
|
//new gesture, clear last and first event fields
|
|
if (IsBegin)
|
|
{
|
|
LastBeginEvent = null;
|
|
LastEvent = null;
|
|
}
|
|
}
|
|
|
|
//Decode the gesture
|
|
private void ParseGesture(Control parentControl, ref WinApi.GESTUREINFO gestureInfo)
|
|
{
|
|
Location = parentControl.PointToClient(new Point(gestureInfo.ptsLocation.x, gestureInfo.ptsLocation.y));
|
|
|
|
Center = Location;
|
|
|
|
switch (GestureId)
|
|
{
|
|
case WinApi.GID_ROTATE:
|
|
ushort lastArguments = (ushort)(IsBegin ? 0 : LastEvent.GestureArguments);
|
|
|
|
RotateAngle = WinApi.GID_ROTATE_ANGLE_FROM_ARGUMENT((ushort)(gestureInfo.ullArguments - lastArguments));
|
|
break;
|
|
|
|
|
|
case WinApi.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 = WinApi.LoDWord(gestureInfo.ullArguments);
|
|
break;
|
|
|
|
case WinApi.GID_PAN:
|
|
PanTranslation = IsBegin ? new Size(0, 0) :
|
|
new Size(Location.X - LastEvent.Location.X, Location.Y - LastEvent.Location.Y);
|
|
int panVelocity = WinApi.HiDWord((long)(gestureInfo.ullArguments));
|
|
PanVelocity = new Size(WinApi.LoWord(panVelocity), WinApi.HiWord(panVelocity));
|
|
//DistanceBetweenFingers = WinApi.LoDWord(gestureInfo.ullArguments);
|
|
break;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
private uint _GestureId;
|
|
public uint GestureId
|
|
{
|
|
get { return _GestureId; }
|
|
private set { _GestureId = value; }
|
|
}
|
|
|
|
private ulong _GestureArguments;
|
|
public ulong GestureArguments
|
|
{
|
|
get { return _GestureArguments; }
|
|
private set { _GestureArguments = value; }
|
|
}
|
|
|
|
private Point _Location;
|
|
/// <summary>
|
|
/// The client location of gesture.
|
|
/// </summary>
|
|
public Point Location
|
|
{
|
|
get { return _Location; }
|
|
private set { _Location = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Is this the first event of a gesture.
|
|
/// </summary>
|
|
public bool IsBegin
|
|
{
|
|
get
|
|
{
|
|
return (_Flags & WinApi.GF_BEGIN) != 0;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// It this last event of a gesture.
|
|
/// </summary>
|
|
public bool IsEnd
|
|
{
|
|
get
|
|
{
|
|
return (_Flags & WinApi.GF_END) != 0;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Has gesture triggered inertia.
|
|
/// </summary>
|
|
public bool IsInertia
|
|
{
|
|
get
|
|
{
|
|
return (_Flags & WinApi.GF_INERTIA) != 0;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gesture relative rotation angle for Rotate event.
|
|
/// </summary>
|
|
private double _RotateAngle;
|
|
public double RotateAngle
|
|
{
|
|
get { return _RotateAngle; }
|
|
private set { _RotateAngle = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Indicates calculated gesture center.
|
|
/// </summary>
|
|
private Point _Center;
|
|
public Point Center
|
|
{
|
|
get { return _Center; }
|
|
private set { _Center = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gesture zoom factor for Zoom event.
|
|
/// </summary>
|
|
private double _ZoomFactor;
|
|
public double ZoomFactor
|
|
{
|
|
get { return _ZoomFactor; }
|
|
private set { _ZoomFactor = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gesture relative panning translation for Pan event.
|
|
/// </summary>
|
|
private Size _PanTranslation;
|
|
public Size PanTranslation
|
|
{
|
|
get { return _PanTranslation; }
|
|
set { _PanTranslation = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Gesture velocity vector of the pan gesture for custom inertia implementations.
|
|
/// </summary>
|
|
private Size _PanVelocity;
|
|
public Size PanVelocity
|
|
{
|
|
get { return _PanVelocity; }
|
|
private set { _PanVelocity = value; }
|
|
}
|
|
|
|
private GestureEventArgs _LastBeginEvent;
|
|
/// <summary>
|
|
/// The first touch arguments in this gesture event sequence.
|
|
/// </summary>
|
|
public GestureEventArgs LastBeginEvent
|
|
{
|
|
get { return _LastBeginEvent; }
|
|
internal set { _LastBeginEvent = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// The last touch arguments in this gesture event sequence.
|
|
/// </summary>
|
|
private GestureEventArgs _LastEvent;
|
|
public GestureEventArgs LastEvent
|
|
{
|
|
get { return _LastEvent; }
|
|
internal set { _LastEvent = value; }
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// EventArgs passed to Touch handlers
|
|
/// </summary>
|
|
internal class TouchEventArgs : EventArgs
|
|
{
|
|
private readonly TouchHandler _ParentHandler;
|
|
private readonly float _dpiXFactor;
|
|
private readonly float _dpiYFactor;
|
|
|
|
/// <summary>
|
|
/// Create new touch event argument instance
|
|
/// </summary>
|
|
/// <param name="hWndWrapper">The target control</param>
|
|
/// <param name="touchInput">one of the inner touch input in the message</param>
|
|
internal TouchEventArgs(TouchHandler parentHandler, float dpiX, float dpiY, ref WinApi.TOUCHINPUT touchInput)
|
|
{
|
|
_ParentHandler = parentHandler;
|
|
_dpiXFactor = 96F / dpiX;
|
|
_dpiYFactor = 96F / dpiY;
|
|
DecodeTouch(ref touchInput);
|
|
}
|
|
|
|
private bool CheckFlag(int value)
|
|
{
|
|
return (Flags & value) != 0;
|
|
}
|
|
|
|
|
|
|
|
// Decodes and handles WM_TOUCH* messages.
|
|
private void DecodeTouch(ref WinApi.TOUCHINPUT touchInput)
|
|
{
|
|
// TOUCHINFO point coordinates and contact size is in 1/100 of a pixel; convert it to pixels.
|
|
// Also convert screen to client coordinates.
|
|
if ((touchInput.dwMask & WinApi.TOUCHINPUTMASKF_CONTACTAREA) != 0)
|
|
ContactSize = new Size(AdjustDpiX(touchInput.cyContact / 100), AdjustDpiY(touchInput.cyContact / 100));
|
|
|
|
Id = touchInput.dwID;
|
|
|
|
Point p = _ParentHandler.ParentControl.PointToClient(new Point(touchInput.x / 100, touchInput.y / 100));
|
|
Location = p; // new Point(AdjustDpiX(p.X), AdjustDpiY(p.Y));
|
|
|
|
Time = touchInput.dwTime;
|
|
TimeSpan ellapse = TimeSpan.FromMilliseconds(Environment.TickCount - touchInput.dwTime);
|
|
AbsoluteTime = DateTime.Now - ellapse;
|
|
|
|
Mask = touchInput.dwMask;
|
|
Flags = touchInput.dwFlags;
|
|
}
|
|
|
|
|
|
private int AdjustDpiX(int value)
|
|
{
|
|
return (int)(value * _dpiXFactor);
|
|
}
|
|
|
|
private int AdjustDpiY(int value)
|
|
{
|
|
return (int)(value * _dpiYFactor);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Touch client coordinate in pixels
|
|
/// </summary>
|
|
private Point _Location;
|
|
public Point Location
|
|
{
|
|
get { return _Location; }
|
|
private set
|
|
{
|
|
_Location = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// A touch point identifier that distinguishes a particular touch input
|
|
/// </summary>
|
|
private int _Id;
|
|
public int Id
|
|
{
|
|
get { return _Id; }
|
|
private set
|
|
{
|
|
_Id = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// A set of bit flags that specify various aspects of touch point
|
|
/// press, release, and motion.
|
|
/// </summary>
|
|
private int _Flags;
|
|
public int Flags
|
|
{
|
|
get { return _Flags; }
|
|
private set
|
|
{
|
|
_Flags = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// mask which fields in the structure are valid
|
|
/// </summary>
|
|
private int _Mask;
|
|
public int Mask
|
|
{
|
|
get { return _Mask; }
|
|
private set
|
|
{
|
|
_Mask = value;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// touch event time
|
|
/// </summary>
|
|
private DateTime _AbsoluteTime;
|
|
public DateTime AbsoluteTime
|
|
{
|
|
get { return _AbsoluteTime; }
|
|
private set { _AbsoluteTime = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// touch event time from system up
|
|
/// </summary>
|
|
private int _Time;
|
|
public int Time
|
|
{
|
|
get { return _Time; }
|
|
private set
|
|
{
|
|
_Time = value;
|
|
}
|
|
}
|
|
/// <summary>
|
|
/// the size of the contact area in pixels
|
|
/// </summary>
|
|
private Size? _ContactSize;
|
|
public Size? ContactSize
|
|
{
|
|
get { return _ContactSize; }
|
|
private set { _ContactSize = value; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Is Primary Contact (The first touch sequence)
|
|
/// </summary>
|
|
public bool IsPrimaryContact
|
|
{
|
|
get { return (Flags & WinApi.TOUCHEVENTF_PRIMARY) != 0; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Specifies that movement occurred
|
|
/// </summary>
|
|
public bool IsTouchMove
|
|
{
|
|
get { return CheckFlag(WinApi.TOUCHEVENTF_MOVE); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Specifies that the corresponding touch point was established through a new contact
|
|
/// </summary>
|
|
public bool IsTouchDown
|
|
{
|
|
get { return CheckFlag(WinApi.TOUCHEVENTF_DOWN); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Specifies that a touch point was removed
|
|
/// </summary>
|
|
public bool IsTouchUp
|
|
{
|
|
get { return CheckFlag(WinApi.TOUCHEVENTF_UP); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Specifies that a touch point is in range
|
|
/// </summary>
|
|
public bool IsTouchInRange
|
|
{
|
|
get { return CheckFlag(WinApi.TOUCHEVENTF_INRANGE); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// specifies that this input was not coalesced.
|
|
/// </summary>
|
|
public bool IsTouchNoCoalesce
|
|
{
|
|
get { return CheckFlag(WinApi.TOUCHEVENTF_NOCOALESCE); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Specifies that the touch point is associated with a pen contact
|
|
/// </summary>
|
|
public bool IsTouchPen
|
|
{
|
|
get { return CheckFlag(WinApi.TOUCHEVENTF_PEN); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// The touch event came from the user's palm
|
|
/// </summary>
|
|
/// <remarks>Set <see cref="DisablePalmRejection"/> to true</remarks>
|
|
public bool IsTouchPalm
|
|
{
|
|
get { return CheckFlag(WinApi.TOUCHEVENTF_PALM); }
|
|
}
|
|
}
|
|
}
|