TouchHelper.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Drawing;
  5. using System.Linq;
  6. using System.Runtime.InteropServices;
  7. using System.Text;
  8. using System.Threading.Tasks;
  9. using System.Windows.Forms;
  10. namespace Uni_Trend.MSO7000X.Touch
  11. {
  12. /// <summary>
  13. /// 触摸功能的帮助器
  14. /// </summary>
  15. public class TouchHelper
  16. {
  17. readonly Control _TouchControl; //帮助器绑定的控件对象
  18. IntPtr _ManipulationHelper = IntPtr.Zero; //操作帮助器指针
  19. bool _TouchingFlag = false; //表示控件对象是否正处于触摸状态
  20. //缩放功能相关变量
  21. ScaleDirection _SclDrection = ScaleDirection.Both;
  22. (int xSum, int ySum) _CurrentTouchPointSum = (0, 0);
  23. (int xSum, int ySum) _LastScaleTouchPointSum = (0, 0);
  24. //触摸回调函数对应的委托
  25. TouchWin32.OnManipulationStartedDelegate _OnManipulationStartedDelegate;
  26. TouchWin32.OnManipulationDeltaDelegate _OnManipulationDeltaDelegate;
  27. TouchWin32.OnManipulationCompletedDelegate _OnManipulationCompletedDelegate;
  28. //表示控件对象是否正处于触摸状态
  29. public bool TouchingFlag => _TouchingFlag;
  30. /// <summary>
  31. /// 参数1:触点的X坐标;
  32. /// 参数2:触点的Y坐标;
  33. /// </summary>
  34. public event Action<int, int> TouchDown;
  35. /// <summary>
  36. /// 参数1:触点当前的X坐标;
  37. /// 参数2:触点当前的Y坐标;
  38. /// 参数3:触点此刻移动的X距离;
  39. /// 参数4:触点此刻移动的Y距离;
  40. /// </summary>
  41. public event Action<int, int, int, int> TouchMove;
  42. /// <summary>
  43. /// 参数1:触点的X坐标;
  44. /// 参数2:触点的Y坐标;
  45. /// </summary>
  46. public event Action<int, int> TouchUp;
  47. /// <summary>
  48. /// 参数:旋转的角度;
  49. /// </summary>
  50. public event Action<float> TouchRotation;
  51. /// <summary>
  52. /// 参数:缩放的倍率;
  53. /// </summary>
  54. public event Action<float, ScaleDirection> TouchScale;
  55. public TouchHelper(Control touchCtl)
  56. {
  57. _TouchControl = touchCtl;
  58. RegisterTouch();
  59. }
  60. /// <summary>
  61. /// 用该方法来解析Touch消息,用在WndProc里面使用;
  62. /// </summary>
  63. /// <param name="touchMsg"></param>
  64. public void DecodeTouch(ref Message touchMsg)
  65. {
  66. if(_ManipulationHelper == IntPtr.Zero)
  67. {
  68. return;
  69. }
  70. int cinputs = (int)touchMsg.WParam; // Number of actual per-contact messages
  71. TouchWin32.TOUCHINPUT[] pinputs = new TouchWin32.TOUCHINPUT[cinputs]; // Allocate the storage for the parameters of the per-contact messages
  72. // Unpack message parameters into the array of TOUCHINPUT structures, each
  73. // representing a message for one single contact.
  74. if (TouchWin32.GetTouchInputInfo(touchMsg.LParam, cinputs, pinputs, Marshal.SizeOf(typeof(TouchWin32.TOUCHINPUT))))
  75. {
  76. _CurrentTouchPointSum.xSum = 0;
  77. _CurrentTouchPointSum.ySum = 0;
  78. pinputs.Cast<TouchWin32.TOUCHINPUT>().ToList().ForEach(input =>
  79. {
  80. _CurrentTouchPointSum.xSum += input.x;
  81. _CurrentTouchPointSum.ySum += input.y;
  82. });
  83. // For each contact, dispatch the message to the appropriate message handler.
  84. for (uint i = 0; i < cinputs; i++)
  85. {
  86. if ((pinputs[i].dwFlags & TouchWin32.TOUCHEVENTF_DOWN) != 0)
  87. {
  88. TouchWin32.ProcessDown(_ManipulationHelper, pinputs[i].dwID, pinputs[i].x, pinputs[i].y);
  89. }
  90. else if ((pinputs[i].dwFlags & TouchWin32.TOUCHEVENTF_MOVE) != 0)
  91. {
  92. TouchWin32.ProcessMove(_ManipulationHelper, pinputs[i].dwID, pinputs[i].x, pinputs[i].y);
  93. }
  94. else if ((pinputs[i].dwFlags & TouchWin32.TOUCHEVENTF_UP) != 0)
  95. {
  96. TouchWin32.ProcessUp(_ManipulationHelper, pinputs[i].dwID, pinputs[i].x, pinputs[i].y);
  97. }
  98. }
  99. }
  100. TouchWin32.CloseTouchInputHandle(touchMsg.LParam);
  101. }
  102. /// <summary>
  103. /// 处理触摸开始事件的委托函数;
  104. /// 注意:委托直接由IManipulationProcessor相关线程调用,由于该线程不能耗时,所以委托函数要用多线程处理;
  105. /// </summary>
  106. /// <param name="x"></param>
  107. /// <param name="y"></param>
  108. protected virtual async void OnManipulationStarted(
  109. float x,
  110. float y)
  111. {
  112. await Task.Run(() =>
  113. {
  114. Point clientpoint = TranslateTouchXYToClient(x, y);
  115. //触发TouchDown事件
  116. if (TouchDown != null)
  117. {
  118. _TouchControl.Invoke(TouchDown, clientpoint.X, clientpoint.Y);
  119. }
  120. });
  121. _TouchingFlag = true;
  122. }
  123. /// <summary>
  124. /// 处理触摸变化事件的委托函数;
  125. /// 注意:委托直接由IManipulationProcessor相关线程调用,由于该线程不能耗时,所以委托函数要用多线程处理;
  126. /// </summary>
  127. /// <param name="x"></param>
  128. /// <param name="y"></param>
  129. /// <param name="translationDeltaX"></param>
  130. /// <param name="translationDeltaY"></param>
  131. /// <param name="scaleDelta"></param>
  132. /// <param name="expansionDelta"></param>
  133. /// <param name="rotationDelta"></param>
  134. /// <param name="cumulativeTranslationX"></param>
  135. /// <param name="cumulativeTranslationY"></param>
  136. /// <param name="cumulativeScale"></param>
  137. /// <param name="cumulativeExpansion"></param>
  138. /// <param name="cumulativeRotation"></param>
  139. protected virtual void OnManipulationDelta(
  140. float x,
  141. float y,
  142. float translationDeltaX,
  143. float translationDeltaY,
  144. float scaleDelta,
  145. float expansionDelta,
  146. float rotationDelta,
  147. float cumulativeTranslationX,
  148. float cumulativeTranslationY,
  149. float cumulativeScale,
  150. float cumulativeExpansion,
  151. float cumulativeRotation)
  152. {
  153. //不在触摸状态,不用处理
  154. if(!_TouchingFlag)
  155. {
  156. return;
  157. }
  158. //触发相关事件
  159. Task.Run(() =>
  160. {
  161. Point clientpoint = TranslateTouchXYToClient(x, y);
  162. //触发TouchRotation事件
  163. if (TouchRotation != null && rotationDelta != 0)
  164. {
  165. _TouchControl.Invoke(TouchRotation, rotationDelta);
  166. }
  167. //触发TouchScale事件,当此事件发生事,TouchMove事件不发生;
  168. if (TouchScale != null && scaleDelta != 1F)
  169. {
  170. _SclDrection = CalculateDirctByXYSum();
  171. _TouchControl.Invoke(TouchScale, scaleDelta, _SclDrection);
  172. return;
  173. }
  174. //触发TouchMove事件
  175. if (TouchMove != null)
  176. {
  177. if (translationDeltaX != 0 || translationDeltaY != 0)
  178. {
  179. _TouchControl.Invoke(
  180. TouchMove,
  181. clientpoint.X,
  182. clientpoint.Y,
  183. (int)(translationDeltaX / 100),
  184. (int)(translationDeltaY / 100));
  185. }
  186. }
  187. });
  188. }
  189. /// <summary>
  190. /// 处理触摸完成事件的委托函数;
  191. /// 注意:委托直接由IManipulationProcessor相关线程调用,由于该线程不能耗时,所以委托函数要用多线程处理;
  192. /// </summary>
  193. /// <param name="x"></param>
  194. /// <param name="y"></param>
  195. /// <param name="cumulativeTranslationX"></param>
  196. /// <param name="cumulativeTranslationY"></param>
  197. /// <param name="cumulativeScale"></param>
  198. /// <param name="cumulativeExpansion"></param>
  199. /// <param name="cumulativeRotation"></param>
  200. protected virtual async void OnManipulationCompleted(
  201. float x,
  202. float y,
  203. float cumulativeTranslationX,
  204. float cumulativeTranslationY,
  205. float cumulativeScale,
  206. float cumulativeExpansion,
  207. float cumulativeRotation)
  208. {
  209. await Task.Run(() =>
  210. {
  211. Point clientpoint = TranslateTouchXYToClient(x, y);
  212. //触发TouchUp事件
  213. if (TouchUp != null)
  214. {
  215. _TouchControl.Invoke(TouchUp, clientpoint.X, clientpoint.Y);
  216. }
  217. });
  218. _TouchingFlag = false;
  219. }
  220. /// <summary>
  221. /// 注册触摸事件到控件
  222. /// </summary>
  223. /// <param name="ctlHandle">控件句柄</param>
  224. private void RegisterTouch()
  225. {
  226. _OnManipulationStartedDelegate = new TouchWin32.OnManipulationStartedDelegate(OnManipulationStarted);
  227. _OnManipulationDeltaDelegate = new TouchWin32.OnManipulationDeltaDelegate(OnManipulationDelta);
  228. _OnManipulationCompletedDelegate = new TouchWin32.OnManipulationCompletedDelegate(OnManipulationCompleted);
  229. _ManipulationHelper = TouchWin32.RegisterWnd(
  230. _TouchControl.Handle,
  231. Marshal.GetFunctionPointerForDelegate(_OnManipulationStartedDelegate),
  232. Marshal.GetFunctionPointerForDelegate(_OnManipulationDeltaDelegate),
  233. Marshal.GetFunctionPointerForDelegate(_OnManipulationCompletedDelegate));
  234. }
  235. /// <summary>
  236. /// 转换touch信息中的x和y坐标到绑定控件(_TouchControl)的坐标系
  237. /// </summary>
  238. /// <param name="touchX">相对与屏幕坐标系的X坐标,且精度为Pixel的1/100</param>
  239. /// <param name="touchY">相对与屏幕坐标系的Y坐标,且精度为Pixel的1/100</param>
  240. private Point TranslateTouchXYToClient(float touchX, float touchY)
  241. {
  242. Point clientpoint;
  243. Point screenpoint = new Point((int)(touchX / 100), (int)(touchY / 100));
  244. if(_TouchControl.InvokeRequired)
  245. {
  246. clientpoint = (Point)_TouchControl.Invoke(
  247. new Func<Point>(() => _TouchControl.PointToClient(screenpoint)));
  248. }
  249. else
  250. {
  251. clientpoint = _TouchControl.PointToClient(screenpoint);
  252. }
  253. return clientpoint;
  254. }
  255. /// <summary>
  256. /// 计算缩放方向的方法:通过XY的移动量来确定
  257. /// </summary>
  258. /// <param name="xTrans"></param>
  259. /// <param name="yTrans"></param>
  260. /// <returns></returns>
  261. private ScaleDirection CalculateDirctByXY(float xTrans, float yTrans)
  262. {
  263. if (Math.Abs(xTrans) > Math.Abs(yTrans))
  264. {
  265. return ScaleDirection.Horizontal;
  266. }
  267. else if (Math.Abs(xTrans) == Math.Abs(yTrans))
  268. {
  269. return ScaleDirection.Both;
  270. }
  271. else
  272. {
  273. return ScaleDirection.Veritcal;
  274. }
  275. }
  276. /// <summary>
  277. /// 计算缩放方向的方法:通过X,Y的和的变化量来确定
  278. /// </summary>
  279. /// <returns></returns>
  280. private ScaleDirection CalculateDirctByXYSum()
  281. {
  282. int xdelta = Math.Abs(_CurrentTouchPointSum.xSum - _LastScaleTouchPointSum.xSum);
  283. int ydelta = Math.Abs(_CurrentTouchPointSum.ySum - _LastScaleTouchPointSum.ySum);
  284. _LastScaleTouchPointSum.xSum = _CurrentTouchPointSum.xSum;
  285. _LastScaleTouchPointSum.ySum = _CurrentTouchPointSum.ySum;
  286. if (xdelta > ydelta)
  287. {
  288. return ScaleDirection.Horizontal;
  289. }
  290. else if(xdelta < ydelta)
  291. {
  292. return ScaleDirection.Veritcal;
  293. }
  294. return ScaleDirection.Both;
  295. }
  296. }
  297. /// <summary>
  298. /// 缩放的方向定义
  299. /// </summary>
  300. public enum ScaleDirection
  301. {
  302. Both,
  303. Horizontal,
  304. Veritcal
  305. }
  306. }