我正在尝试在 Windows CE 6 上实现用于绘图的多点触控手势。我尝试了两种不同的方法……使用内置手势事件并覆盖 WndProc 来给我鼠标按下和鼠标移动事件。
使用内置手势事件,我找不到获取多点触控手势两个点坐标的方法。
通过覆盖 WndProc 或使用 MouseDown/MouseMove 事件,当您在屏幕上按住一根手指时,它会隐藏鼠标按下消息,从而停止多点触控手势。
有什么建议吗?
对不起,它是 CE 7,而不是 6!仍然不确定如何处理多点触控...
我正在尝试在 Windows CE 6 上实现用于绘图的多点触控手势。我尝试了两种不同的方法……使用内置手势事件并覆盖 WndProc 来给我鼠标按下和鼠标移动事件。
使用内置手势事件,我找不到获取多点触控手势两个点坐标的方法。
通过覆盖 WndProc 或使用 MouseDown/MouseMove 事件,当您在屏幕上按住一根手指时,它会隐藏鼠标按下消息,从而停止多点触控手势。
有什么建议吗?
对不起,它是 CE 7,而不是 6!仍然不确定如何处理多点触控...
根据这个http://msdn.microsoft.com/en-us/library/jj838851.aspx只有 Compact 2013 支持多点触控手势。OEM 必须实施驱动程序以支持多点触控。
您可以使用设备上的 Internet 浏览器测试您的设备,或调用 TouchPanelGetDeviceCaps API 以获取有关多点触控支持的信息 ( http://msdn.microsoft.com/en-us/library/gg159143.aspx )。
如果设备是 Windows CE 6 而不是 Compact 2013,我担心现在有办法在任何应用程序中获取多点触控信息。
几年前我使用过这个 API,今天这个手势在某些设备上不起作用。我正在寻找答案。我的代码看起来像这样,正在工作。您需要捕获 WM_GESTURE 消息来执行缩放或平移等操作。
using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
namespace SmartDeviceCommon
{
/// <summary>
/// Gesture info structure
/// </summary>
[StructLayout(LayoutKind.Sequential)]
public struct GestureInfo
{
/// <summary>
/// Specifies the size of the structure in bytes. This must be set to Marshal.SizeOf(typeof(GESTUREINFO))
/// </summary>
public uint Size;
/// <summary>
/// Gesture Flags
/// </summary>
public GestureState State;
/// <summary>
/// Gesture Id
/// </summary>
public GestureKind Kind;
/// <summary>
/// HWND of the target winndow
/// </summary>
public IntPtr TargetWindow;
/// <summary>
/// Coordinates of start of gesture
/// </summary>
public short LocationX;
/// <summary>
/// Coordinates of start of gesture
/// </summary>
public short LocationY;
/// <summary>
/// Gesture Instance Id
/// </summary>
public uint InstanceId;
/// <summary>
/// Gesture Sequence Id
/// </summary>
public uint SequenceId;
/// <summary>
/// Arguments specific to gesture
/// </summary>
public ulong Arguments;
/// <summary>
/// Size of extra arguments in bytes
/// </summary>
public uint ExtraArguments;
}
[Flags]
public enum GestureState : uint
{
/// <summary>
/// The gesture has no associated state
/// </summary>
None = 0,
/// <summary>
/// The gesture is the beginning of pan gesture
/// </summary>
Begin = 1,
/// <summary>
/// The gesture is the end of a pan gesture that will transition into a scroll gesture
/// </summary>
Inertia = 2,
/// <summary>
/// The gesture is the end of a pan gesture
/// </summary>
End = 4
}
public enum GestureFlags : uint
{
Begin = 0x0001,
Inertia = 0x0002,
End = 0x0004,
Symmetric = 0x0010
}
/// <summary>
/// The kind of gesture.
/// </summary>
public enum GestureKind : uint
{
/// <summary>
/// The beginning of a gesture operation.
/// </summary>
Begin = 1,
/// <summary>
/// The end of a gesture operation.
/// </summary>
End = 2,
/// <summary>
/// A pan gesture.
/// </summary>
Pan = 4,
/// <summary>
/// A scroll gesture.
/// </summary>
Scroll = 8,
/// <summary>
/// A hold gesture.
/// </summary>
Hold = 9,
/// <summary>
/// A select gesture.
/// </summary>
Select = 10,
/// <summary>
/// A double-select gesture.
/// </summary>
DoubleSelect = 11,
/// <summary>
/// Direct manipulation.
/// </summary>
DirectManipulation = 12,
}
/// <summary>
/// See Gestures.pdf
/// </summary>
public class Gestures
{
/// <summary>
/// Enable gesture
/// </summary>
/// <param name="hwnd">handle of windows (IntPtr)</param>
/// <param name="flags">TGestureMask</param>
/// <param name="scope">TagGestureFlags => (ulong)TagGestureFlags</param>
/// <returns></returns>
[DllImport("coredll.dll", SetLastError = true)]
public static extern bool EnableGestures(IntPtr hwnd, GestureMask flags, uint scope);
/// <summary>
/// Disable gesture
/// </summary>
/// <param name="hwnd">handle of windows (IntPtr)</param>
/// <param name="flags">TGestureMask</param>
/// <param name="scope">TagGestureFlags => (ulong)TagGestureFlags</param>
/// <returns></returns>
[DllImport("coredll.dll", SetLastError = true)]
public static extern bool DisableGestures(IntPtr hwnd, GestureMask flags, uint scope);
[DllImport("coredll.dll")]
public static extern bool GetGestureInfo(IntPtr hGestureInfo, ref GestureInfo pGestureInfo);
}
[Flags]
public enum GestureMask : ulong
{
TGF_GID_PAN = 0x10,
TGF_GID_SCROLL = 0x100,
TGF_GID_HOLD = 0x200,
TGF_GID_SELECT = 0x400,
TGF_GID_DOUBLESELECT = 0x800,
TGF_GID_DIRECTMANIPULATION = 0x1000,
TGF_GID_ALL = TGF_GID_PAN | TGF_GID_SCROLL | TGF_GID_HOLD | TGF_GID_SELECT | TGF_GID_DOUBLESELECT,// | TGF_GID_DIRECTMANIPULATION,
}
public enum TagGestureFlags : ulong
{
BEGIN = 0x0000000000000002,
END = 0x0000000000000008,
PAN = 0x0000000000000100,
ROTATE = 0x0000000000000200,
SCROLL = 0x0000000000001000,
HOLD = 0x0000000000002000,
SELECT = 0x0000000000004000,
DOUBLESELECT = 0x0000000000008000,
LAST = 0x0000000000008000,
MAX = 0x8000000000000000,
SCOPE_WINDOW = 0,
SCOPE_PROCESS = 1
}
}
//typedef struct tagGESTUREINFO
//{
// UINT cbSize; /* Initialised to structure size */
// DWORD dwFlags; /* Gesture Flags */
// DWORD dwID; /* Gesture ID */
// HWND hwndTarget; /* HWND of target window */
// POINTS ptsLocation; /* Coordinates of start of gesture */
// DWORD dwInstanceID; /* Gesture Instance ID */
// DWORD dwSequenceID; /* Gesture Sequence ID */
// ULONGLONG ullArguments; /* Arguments specific to gesture */
// UINT cbExtraArguments; /* Size of extra arguments in bytes */
//} GESTUREINFO, * PGESTUREINFO;
// winuser.h (C:\Program Files (x86)\Windows CE Tools\SDKs\BT-W SDK-HT\Include\Armv4i\winuser.h)
//**LINE 310**// Touch gesture command
//#define WM_GESTURE 0x0119
///*
// * Gesture flags - GESTUREINFO.dwFlags
// */
//#define GF_BEGIN 0x00000001
//#define GF_INERTIA 0x00000002
//#define GF_END 0x00000004
//#define GF_SYMMETRIC 0x00000010
///*
// * Gesture IDs
// */
//#define GID_BEGIN 1
//#define GID_END 2
//#define GID_PAN 4
//#define GID_ROTATE 5
//#define GID_SCROLL 8
//#define GID_HOLD 9
//#define GID_SELECT 10
//#define GID_DOUBLESELECT 11
//#define GID_DIRECTMANIPULATION 12
//#define GID_LAST 12
示例用法:
public partial class PreviewForm : Form
{
public PreviewForm()
{
InitializeComponent();
}
private void PreviewForm_Load(object sender, EventArgs e)
{
if (SmartDeviceApplication.DesignTime) return;
WndProcHooker.HookWndProc(this, hooker_OnGestureCallback, (uint)WM.GESTURE);
bool enabled = Gestures.EnableGestures(Handle, GestureMask.TGF_GID_ALL, (uint)TagGestureFlags.SCOPE_WINDOW);
if (!enabled)
{
MessageBox.Show("Gestures not enabled");
}
//hooker = new WndProcHooker(this, (uint)WM.GESTURE);
//hooker.OnCallback += new WndProcHooker.WndProcCallback(hooker_OnGestureCallback);
//hooker.Start();
zoom = 1.0F;
}
public float Zoom
{
get { return zoom; }
set
{
if (value > 3.0F)
{
value = 3.0F;
delta = 0.25f;
}
else if (value < 1.0F)
{
previewPictureBox.Location = previewPicLocation;
value = 1.0F;
delta = 1.0f;
}
var _width = previewPictureBox.Width;
var _height = previewPictureBox.Height;
previewPictureBox.Width = (int)(previewPicSize.Width * value);
previewPictureBox.Height = (previewPictureBox.Width * previewPicSize.Height) / previewPicSize.Width;
previewPictureBox.Refresh();
zoom = value;
}
}
GestureInfo lastInfo;
Point StartPos;
Point OffSet;
bool inMove;
float zoom;
float delta = 0.1f;
//float deltaDirection = 1.0F;
// 0,41 - 239,160
int hooker_OnGestureCallback(IntPtr hwnd, uint msg, uint wParam, int lParam, ref bool handled)
{
GestureInfo gesture = new GestureInfo();
gesture.Size = (uint)Marshal.SizeOf(typeof(GestureInfo));
bool result = Gestures.GetGestureInfo((IntPtr)lParam, ref gesture);
if (!result) return 0;
switch (gesture.Kind)
{
case GestureKind.Begin:
StartPos = new Point(gesture.LocationX, gesture.LocationY);
OffSet = StartPos;
inMove = false;
break;
case GestureKind.End:
inMove = false;
StartPos = new Point(0, 0);
break;
case GestureKind.Pan:
var prevDelta = Math.Abs(((lastInfo.LocationY - StartPos.Y) + (lastInfo.LocationX - StartPos.X)));
var curDelta = Math.Abs(((gesture.LocationY - StartPos.Y) + (gesture.LocationX - StartPos.X)));
if (!inMove)
{
if (curDelta > prevDelta)
{
Zoom += delta;
}
else
{
Zoom -= delta;
}
Debug.WriteLine(string.Format("StartPos X:{0} Y:{1}", StartPos.X, StartPos.Y));
Debug.WriteLine(string.Format("GesturePos X:{0} Y:{1}", gesture.LocationX, gesture.LocationY));
Debug.WriteLine(string.Format("Zoom {0} Delta {1} curDelta {2} prevDelta {3}",
Zoom, delta, curDelta, prevDelta));
//previewPictureBox.Location = new Point(previewPictureBox.Location.X - OffSet.X, previewPictureBox.Location.Y - OffSet.Y);
//previewPictureBox.Size = new Size(previewPictureBox.Size.Width + OffSet.X, previewPictureBox.Size.Height + OffSet.Y);
}
else
{
OffSet.X = gesture.LocationX - lastInfo.LocationX;
if (OffSet.X > 0) OffSet.X = 5; else OffSet.X = -5;
OffSet.Y = gesture.LocationY - lastInfo.LocationY;
if (OffSet.Y > 0) OffSet.Y = 5; else OffSet.Y = -5;
previewPictureBox.Location = new Point(previewPictureBox.Location.X + (OffSet.X * (int)Zoom), previewPictureBox.Location.Y + (OffSet.Y * (int)Zoom));
Debug.WriteLine(string.Format("pic Location X:{0} Y:{1}", previewPictureBox.Location.X, previewPictureBox.Location.Y));
}
previewPictureBox.Refresh();
break;
case GestureKind.Scroll:
break;
case GestureKind.Hold:
inMove = true;
Vibration.Play(1, 50, 0);
break;
case GestureKind.Select:
break;
case GestureKind.DoubleSelect:
previewPictureBox.Location = previewPicLocation;
previewPictureBox.Size = previewPicSize;
picPanel.AutoScroll = false;
picPanel.Location = backPanel.Location;
picPanel.Size = backPanel.Size;
Vibration.Play(2, 30, 20);
previewPictureBox.Refresh();
break;
case GestureKind.DirectManipulation:
break;
default:
break;
}
lastInfo = gesture;
var pt1 = this.previewPictureBox.PointToScreen(previewPictureBox.Location);
var gpt = new Point(gesture.LocationX, gesture.LocationY);
var rect = new Rectangle(pt1.X, pt1.Y, previewPictureBox.Size.Width, previewPictureBox.Size.Height);
//
//System.Diagnostics.Debug.WriteLine(string.Format("Gesture Kind {0} State {1} Gesture{2} gpt{3},{4}",
// gesture.Kind, gesture.State, gesture.Size, gpt.X, gpt.Y));
//
//System.Diagnostics.Debug.WriteLine(string.Format("Gestures Result {0} Position X:{1} Y:{2}",
// result, gesture.LocationX, gesture.LocationY));
if (rect.Contains(pt1))
{
//System.Diagnostics.Debug.WriteLine("Handled");
handled = true;
return 1;
}
return 0;
}
private void PreviewForm_Closed(object sender, EventArgs e)
{
Gestures.DisableGestures(Handle, (ulong)TagGestureFlags.SCOPE_WINDOW, (uint)TagGestureFlags.SCOPE_WINDOW);
WndProcHooker.UnhookWndProc(this, true);
}
}
using System;
using System.Linq;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Diagnostics;
namespace SmartDeviceCommon
{
public class WndProcHooker : IDisposable
{
#region IDisposable Members
private Control ctrl;
private uint msg;
public event WndProcCallback OnCallback;
private bool started;
public WndProcHooker(Control ctrl, WM msg)
{
this.ctrl = ctrl;
this.msg = (uint)msg;
started = false;
}
public WndProcHooker(Control ctrl, uint msg)
{
this.ctrl = ctrl;
this.msg = msg;
started = false;
}
public int ControlCallBack(uint wParam, int lParam)
{
return WndProcHooker.OldCallback(ctrl.Handle, msg, wParam, lParam);
}
public void Start()
{
if (OnCallback == null) throw new InvalidOperationException("Callback is not set");
if (ctrl == null) throw new InvalidOperationException("Hooker was disposed");
WndProcHooker.HookWndProc(ctrl, OnCallback, msg);
}
public void Stop()
{
if (started)
{
started = false;
WndProcHooker.UnhookWndProc(ctrl, false);
}
}
public void Dispose()
{
if (ctrl != null && OnCallback != null && started)
{
WndProcHooker.UnhookWndProc(ctrl, true);
ctrl = null;
started = false;
}
}
#endregion
public delegate int WndProcCallback(IntPtr hwnd, uint msg, uint wParam, int lParam, ref bool handled);
private static Dictionary<IntPtr, HookedProcInformation> hwndDict =
new Dictionary<IntPtr, HookedProcInformation>();
private static Dictionary<Control, HookedProcInformation> ctlDict =
new Dictionary<Control, HookedProcInformation>();
public static WndProcHooker.WndProcCallback HookWndProc(Control ctl, WndProcCallback callback, uint msg)
{
return HookWndProc(ctl, callback, msg, false, true);
}
public static WndProcHooker.WndProcCallback HookWndProc(Control ctl, WndProcCallback callback, uint msg, bool replic)
{
return HookWndProc(ctl, callback, msg, replic, true);
}
public static WndProcHooker.WndProcCallback HookWndProc(Control ctl, WndProcCallback callback, uint msg, bool replic, bool uniqueMsg)
{
WndProcCallback retValue = null;
HookedProcInformation hpi = null;
if (ctlDict.ContainsKey(ctl))
{
hpi = ctlDict[ctl];
}
else if (hwndDict.ContainsKey(ctl.Handle))
{
hpi = hwndDict[ctl.Handle];
}
if (hpi == null)
{
// If new control, create a new
// HookedProcInformation for it.
hpi = new HookedProcInformation(ctl, new Win32.WndProc(WndProcHooker.WindowProc), replic);
ctl.HandleCreated += new EventHandler(ctl_HandleCreated);
ctl.HandleDestroyed += new EventHandler(ctl_HandleDestroyed);
ctl.Disposed += new EventHandler(ctl_Disposed);
// If the handle has already been created set the hook. If it
// hasn't been created yet, the hook will get set in the
// ctl_HandleCreated event handler.
if (ctl.Handle != IntPtr.Zero)
{
hpi.SetHook();
}
}
// Stick hpi into the correct dictionary.
if (ctl.Handle == IntPtr.Zero)
{
ctlDict[ctl] = hpi;
}
else
{
hwndDict[ctl.Handle] = hpi;
if (hpi.messageMap.ContainsKey(msg))
{
if (uniqueMsg) throw new ArgumentException("Hook already exists for this control");
retValue = hpi.messageMap[msg];
}
}
// Add the message/callback into the message map.
hpi.messageMap[msg] = callback;
return retValue;
}
static void ctl_Disposed(object sender, EventArgs e)
{
Control ctl = sender as Control;
if (ctlDict.ContainsKey(ctl))
{
UnhookWndProc(ctl, true);
//ctlDict.Remove(ctl);
}
// not necessary to show error, if not hooked
//else
// System.Diagnostics.Debug.Assert(false);
}
static void ctl_HandleDestroyed(object sender, EventArgs e)
{
// When the handle for a control is destroyed, we want to
// unhook its wndproc and update our lists
Control ctl = sender as Control;
if (hwndDict.ContainsKey(ctl.Handle))
{
HookedProcInformation hpi = hwndDict[ctl.Handle];
UnhookWndProc(ctl, false);
}
//else
// System.Diagnostics.Debug.Assert(false);
}
static void ctl_HandleCreated(object sender, EventArgs e)
{
Control ctl = sender as Control;
if (ctlDict.ContainsKey(ctl))
{
HookedProcInformation hpi = ctlDict[ctl];
hwndDict[ctl.Handle] = hpi;
ctlDict.Remove(ctl);
hpi.SetHook();
}
else
System.Diagnostics.Debug.Assert(false);
}
private static int WindowProc(IntPtr hwnd, uint msg, uint wParam, int lParam)
{
if (hwndDict.ContainsKey(hwnd))
{
HookedProcInformation hpi = hwndDict[hwnd];
if (hpi.messageMap.ContainsKey(msg))
{
WndProcCallback callback = hpi.messageMap[msg];
bool handled = false;
int retval = callback(hwnd, msg, wParam, lParam, ref handled);
if (hpi.Replicate)
{
var hpi2 = from c in hwndDict
where c.Key != hwnd && c.Value.messageMap.ContainsKey(msg) && c.Value.Replicate
select new { Handle = c.Key, MessageMap = c.Value.messageMap };
var item = hpi2.FirstOrDefault();
if (item != null)
{
var callback2 = item.MessageMap[msg];
var handled2 = true;
callback2(item.Handle, msg, wParam, lParam, ref handled2);
}
Debug.WriteLine("Replicate the message");
}
// if Handled
if (handled)
{
return retval;
}
}
// If the callback didn't set the handled property to true,
// call the original window procedure.
var retValue = hpi.CallOldWindowProc(hwnd, msg, wParam, lParam);
return retValue;
}
//TODO : Check this Assert
//System.Diagnostics.Debug.Assert(false, "WindowProc called for hwnd we don't know about");
System.Diagnostics.Debug.WriteLine("WindowProc called for hwnd we don't know about");
return Win32.DefWindowProc(hwnd, msg, wParam, lParam);
}
public static int OldCallback(IntPtr hwnd, uint msg, uint wParam, int lParam)
{
if (!hwndDict.ContainsKey(hwnd)) return -1;
HookedProcInformation hpi = hwndDict[hwnd];
if (!hpi.messageMap.ContainsKey(msg)) return -1;
// call the original window procedure.
var retValue = hpi.CallOldWindowProc(hwnd, msg, wParam, lParam);
return retValue;
}
public static void UnhookWndProc(Control ctl, uint msg)
{
if (ctl.IsDisposed) return;
HookedProcInformation hpi = null;
try
{
if (ctlDict.ContainsKey(ctl))
hpi = ctlDict[ctl];
else if (hwndDict.ContainsKey(ctl.Handle))
hpi = hwndDict[ctl.Handle];
}
catch (ObjectDisposedException)
{
return;
}
// if we couldn't find a HookedProcInformation, throw
if (hpi == null) throw new ArgumentException("No hook exists for this control");
// look for the message we are removing in the messageMap
if (hpi.messageMap.ContainsKey(msg))
hpi.messageMap.Remove(msg);
else
// if we couldn't find the message, throw
throw new ArgumentException(string.Format("No hook exists for message ({0:X4}) on this control", msg));
}
public static void UnhookWndProc(Control ctl, bool disposing)
{
HookedProcInformation hpi = null;
IntPtr ctl_Handle1 = IntPtr.Zero;
try
{
if (ctl.IsDisposed) return;
if (ctl.Handle == IntPtr.Zero) return;
ctl_Handle1 = ctl.Handle;
}
catch (ObjectDisposedException odex)
{
Debug.WriteLine(odex.ToString());
}
// Control was disposed
if (ctl_Handle1 == IntPtr.Zero) return;
//
if (ctlDict.ContainsKey(ctl))
{
hpi = ctlDict[ctl];
}
else if (hwndDict.ContainsKey(ctl_Handle1))
{
hpi = hwndDict[ctl.Handle];
}
if (hpi == null) throw new ArgumentException("No hook exists for this control");
// If we found our HookedProcInformation in ctlDict and we are
// disposing remove it from ctlDict.
if (ctlDict.ContainsKey(ctl) && disposing) ctlDict.Remove(ctl);
// If we found our HookedProcInformation in hwndDict, remove it
// and if we are not disposing stick it in ctlDict.
if (hwndDict.ContainsKey(ctl.Handle))
{
hpi.Unhook();
hwndDict.Remove(ctl.Handle);
if (!disposing) ctlDict[ctl] = hpi;
}
}
class HookedProcInformation
{
public Dictionary<uint, WndProcCallback> messageMap;
private IntPtr oldWndProc;
private Win32.WndProc newWndProc;
private Control control;
private bool replicate;
public bool Replicate
{
get { return replicate; }
}
public HookedProcInformation(Control ctl, Win32.WndProc wndproc, bool replic)
{
control = ctl;
newWndProc = wndproc;
replicate = replic;
messageMap = new Dictionary<uint, WndProcCallback>();
}
public HookedProcInformation(Control ctl, Win32.WndProc wndproc)
: this(ctl, wndproc, false)
{
}
public void SetHook()
{
IntPtr hwnd = control.Handle;
if (hwnd == IntPtr.Zero) throw new InvalidOperationException("Handle for control has not been created");
oldWndProc = Win32.SetWindowLong(hwnd, Win32.GWL_WNDPROC, Marshal.GetFunctionPointerForDelegate(newWndProc));
}
public void Unhook()
{
IntPtr hwnd = control.Handle;
if (hwnd == IntPtr.Zero)
{
throw new InvalidOperationException("Handle for control has not been created");
}
Win32.SetWindowLong(hwnd, Win32.GWL_WNDPROC, oldWndProc);
}
public int CallOldWindowProc(IntPtr hwnd, uint msg, uint wParam, int lParam)
{
return Win32.CallWindowProc(oldWndProc, hwnd, msg, wParam, lParam);
}
}
}
}
这就是我所做的一切。如果您发现任何遗漏,请告诉我。
问候,
安东尼奥·H·洛佩斯