翼度科技»论坛 编程开发 .net 查看内容

WPF 设备输入事件封装

6

主题

6

帖子

18

积分

新手上路

Rank: 1

积分
18
本文主要介绍WPF应用对鼠标输入、触摸屏触笔以及触摸事件的封装
之前有简单说明设备输入类型 WPF 屏幕点击的设备类型 - 唐宋元明清2188 - 博客园 (cnblogs.com)
1、鼠标 - 通过Mouse相关的事件参数MouseButtonEventArgs中的数据,e.StylusDecice==null表示没有触摸设备,所以设备为鼠标
 2、触笔 or 触摸 - 根据StylusDown事件参数StylusDownEventArgs,e.StylusDevice.TabletDevice.Type == TabletDeviceType.Stylus,True表示触摸设备为触笔,False则为触摸。
通过上面设备类型的判断,就可以封装一套设备点击事件,DeviceDown、DeviceUp、DeviceMove。事件参数中添加设备类型DeviceType(鼠标、触笔、触摸),然后具体业务中可以通过设备类型区分相关的交互操作。
博客园有个小伙伴问我设备类型具体是如何封装的,那本文就补充下设备输入事件的封装使用,希望给大家提供一点帮助、省去你们磨代码的时间。
除了设备输入类型,输入事件也有多种状态。简单介绍下事件区分,具体以鼠标事件为例:
  1. 1     private void RegisterMouse()
  2. 2     {
  3. 3         //鼠标-冒泡
  4. 4         if (_element is Button button)
  5. 5         {
  6. 6             //Button类型的冒泡事件,被内部消化处理了,所以需要重新添加路由事件订阅
  7. 7             button.AddHandler(UIElement.MouseLeftButtonDownEvent, new MouseButtonEventHandler(Button_MouseLeftButtonDown), true);
  8. 8             button.AddHandler(UIElement.MouseRightButtonDownEvent, new MouseButtonEventHandler(Button_MouseRightButtonDown), true);
  9. 9             button.AddHandler(UIElement.MouseLeftButtonUpEvent, new MouseButtonEventHandler(Button_MouseLeftButtonUp), true);
  10. 10             button.AddHandler(UIElement.MouseRightButtonUpEvent, new MouseButtonEventHandler(Button_MouseRightButtonUp), true);
  11. 11         }
  12. 12         else
  13. 13         {
  14. 14             _element.MouseLeftButtonDown += Button_MouseLeftButtonDown;
  15. 15             _element.MouseRightButtonDown += Button_MouseRightButtonDown;
  16. 16             _element.MouseLeftButtonUp += Button_MouseLeftButtonUp;
  17. 17             _element.MouseRightButtonUp += Button_MouseRightButtonUp;
  18. 18         }
  19. 19         _element.MouseMove += Element_MouseMove;
  20. 20
  21. 21         //鼠标-隧道事件
  22. 22         _element.PreviewMouseLeftButtonDown += Button_PreviewMouseLeftButtonDown;
  23. 23         _element.PreviewMouseRightButtonDown += Button_PreviewMouseRightButtonDown;
  24. 24         _element.PreviewMouseLeftButtonUp += Button_PreviewMouseLeftButtonUp;
  25. 25         _element.PreviewMouseRightButtonUp += Button_PreviewMouseRightButtonUp;
  26. 26         _element.PreviewMouseMove += Element_PreviewMouseMove;
  27. 27     }
复制代码
上面区分了按钮与其它的FrameworkElement的鼠标事件,因为Button对冒泡事件是做了拦截再暴露Click事件,需要订阅路由事件来完成鼠标的监听。
如上方代码,对鼠标的左右键、按下抬起、移动以及冒泡隧道都做了完整的封装。
鼠标事件:
  1. 1     private void Element_MouseDown(MouseEventArgs e, int deviceId)
  2. 2     {
  3. 3         if (e.StylusDevice != null) return;
  4. 4         var positionLazy = new Lazy<Point>(() => e.GetPosition(_element));
  5. 5         var deviceInputArgs= new DeviceInputArgs()
  6. 6         {
  7. 7             DeviceId = deviceId,
  8. 8             DeviceType = DeviceType.Mouse,
  9. 9             PositionLazy = positionLazy,
  10. 10             PointsLazy = new Lazy<StylusPointCollection>(() =>
  11. 11             {
  12. 12                 var point = positionLazy.Value;
  13. 13                 return new StylusPointCollection(new List<StylusPoint>() { new StylusPoint(point.X, point.Y) });
  14. 14             }),
  15. 15             GetPositionFunc = (element, args) => e.GetPosition(element),
  16. 16             SourceArgs = e
  17. 17         };
  18. 18         _deviceDown?.Invoke(_element, deviceInputArgs);
  19. 19     }
复制代码
触摸面积获取:
  1. 1     /// <summary>
  2. 2     /// 获取含面积的触摸点集合
  3. 3     /// </summary>
  4. 4     /// <param name="stylusEventArgs"></param>
  5. 5     /// <param name="uiElement"></param>
  6. 6     /// <returns></returns>
  7. 7     public static Rect GetTouchPointArea(TouchEventArgs stylusEventArgs, UIElement uiElement)
  8. 8     {
  9. 9         Rect touchArea = Rect.Empty;
  10. 10         var touchPoints = stylusEventArgs.GetIntermediateTouchPoints(uiElement);
  11. 11         foreach (var stylusPoint in touchPoints)
  12. 12         {
  13. 13             var stylusPointArea = stylusPoint.Bounds;
  14. 14             touchArea.Union(stylusPointArea);
  15. 15         }
  16. 16         return touchArea;
  17. 17     }
复制代码
返回新的触笔输入点集:
  1. 1     /// <summary>
  2. 2     /// 获取触摸点集
  3. 3     /// </summary>
  4. 4     /// <param name="stylusEventArgs"></param>
  5. 5     /// <param name="element"></param>
  6. 6     /// <returns></returns>
  7. 7     private StylusPointCollection GetStylusPoints(StylusEventArgs stylusEventArgs, FrameworkElement element)
  8. 8     {
  9. 9         // 临时去除description
  10. 10         var pointCollection = new StylusPointCollection();
  11. 11         var stylusPointCollection = stylusEventArgs.GetStylusPoints(element);
  12. 12         foreach (var stylusPoint in stylusPointCollection)
  13. 13         {
  14. 14             pointCollection.Add(new StylusPoint(stylusPoint.X, stylusPoint.Y, stylusPoint.PressureFactor));
  15. 15         }
  16. 16         return pointCollection;
  17. 17     }
复制代码
这里是直接把StylusPoint.Description舍弃,防止在应用层未能统一好这个设备描述、导致异常
当然这些获取信息根据业务需要来获取、此处设备事件封装不损耗处理延时,所以需要添加Lazy函数如:
PositionLazy = new Lazy(() => e.GetPosition(_element)),
PointsLazy = new Lazy(() => GetStylusPoints(e, _element)),
暴露的通用设备按下事件,可以看如下定义:
  1. 1     /// <summary>
  2. 2     /// 设备按下
  3. 3     /// </summary>
  4. 4     public event EventHandler<DeviceInputArgs> DeviceDown
  5. 5     {
  6. 6         add => _deviceDown.Add(value, value.Invoke);
  7. 7         remove => _deviceDown.Remove(value);
  8. 8     }
复制代码
UI控件订阅DeviceDown、DeviceMove、DeviceUp事件,从事件参数DeviceInputArgs获取详细数据:
  1. 1         /// <summary>
  2. 2         /// 设备ID
  3. 3         /// <para>默认为鼠标设备,鼠标左键-1,鼠标右键-2 </para>
  4. 4         /// </summary>
  5. 5         public int DeviceId { get; set; }
  6. 6         /// <summary>
  7. 7         /// 设备类型
  8. 8         /// </summary>
  9. 9         public DeviceType DeviceType { get; set; }
  10. 10
  11. 11         /// <summary>
  12. 12         /// 位置
  13. 13         /// </summary>
  14. 14         public Point Position
  15. 15         {
  16. 16             get => PositionLazy?.Value ?? default;
  17. 17             set => PositionLazy = new Lazy<Point>(() => value);
  18. 18         }
  19. 19
  20. 20         /// <summary>
  21. 21         /// 触笔点集
  22. 22         /// </summary>
  23. 23         public StylusPointCollection Points
  24. 24         {
  25. 25             get => PointsLazy?.Value;
  26. 26             set => PointsLazy = new Lazy<StylusPointCollection>(() => value);
  27. 27         }
  28. 28
  29. 29         /// <summary>
  30. 30         /// 获取相对元素的位置
  31. 31         /// </summary>
  32. 32         public Func<FrameworkElement, Point> GetPosition
  33. 33         {
  34. 34             get => relativeElement => GetPositionFunc(relativeElement, SourceArgs);
  35. 35             set => GetPositionFunc = (relativeElement, args) => value(relativeElement);
  36. 36         }
  37. 37
  38. 38         /// <summary>
  39. 39         /// 事件触发源数据
  40. 40         /// </summary>
  41. 41         public InputEventArgs SourceArgs { get; set; }
  42. 42
  43. 43         /// <summary>
  44. 44         /// 触摸面积
  45. 45         /// </summary>
  46. 46         public Rect TouchArea => TouchAreaLazy?.Value ?? Rect.Empty;
复制代码
DeviceInputArgs继承Windows路由事件参数类RoutedEventArgs
为何要封装通用事件?因为2点原因:
1. 大部分业务并不需要区分鼠标、触笔、触摸类型
2. 有些业务如白板书写、多指操作需要区分鼠标、触笔、触摸类型,这类场景因为操作事件订阅太多,业务逻辑搞复杂了
通过将鼠标、触笔、触摸事件封装为通用输入事件,下游应用业务对设备输入事件处理逻辑就简单化了。
封装Demo可详见:kybs00/DeviceInputEventDemo (github.com)
 
关键字:鼠标、触笔、触摸
出处:http://www.cnblogs.com/kybs0/本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文连接,否则保留追究法律责任的权利。
来源:https://www.cnblogs.com/kybs0/p/18325065
免责声明:由于采集信息均来自互联网,如果侵犯了您的权益,请联系我们【E-Mail:cb@itdo.tech】 我们会及时删除侵权内容,谢谢合作!
来自手机

举报 回复 使用道具