WPF 如何实现屏幕边缘滑动检测,解决从屏幕外划入的触摸输入无法识别问题
问题根因
WPF的原生路由触摸事件中,PreviewTouchDown仅在触摸按压动作的起始点落在应用窗口客户区内时才会触发。当触摸从屏幕边框(非窗口客户区)开始按压再滑入窗口时,窗口无法收到初始的PreviewTouchDown事件,既没有起始触摸点记录,部分场景下甚至不会触发后续的PreviewTouchMove事件,就会出现你遇到的异常。
解决方案
我们不需要依赖PreviewTouchDown作为唯一的触摸起始触发点,补充触摸进入场景的检测逻辑,同时显式捕获触摸设备保证滑动事件连续性即可解决,同时可以使用更底层的全局触摸帧事件进一步提升兼容性。
修改后代码
XAML代码(无需修改)
<Window x:Class="touch_test.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:touch_test" mc:Ignorable="d" Title="MainWindow" Height="450" Width="800" Stylus.IsPressAndHoldEnabled="False" > <Canvas> <Rectangle Width="50" Height="50" x:Name="rect" Fill="Red"/> </Canvas> </Window>
C#代码(推荐使用全局触摸帧方案,兼容性最强)
using System.Collections.Generic; using System.Windows; using System.Windows.Input; using System.Windows.Shapes; namespace touch_test { public partial class MainWindow : Window { // 存储每个触摸ID对应的起始点,支持多点触控 private readonly Dictionary<int, TouchPoint> _touchStartPoints = new Dictionary<int, TouchPoint>(); public MainWindow() { InitializeComponent(); // 注册全局触摸帧事件,所有触摸动作都会触发该事件,不受窗口区域限制 Touch.FrameReported += Touch_FrameReported; WindowState = WindowState.Maximized; WindowStyle = WindowStyle.None; } private void Touch_FrameReported(object sender, TouchFrameEventArgs e) { // 获取当前帧所有相对于当前窗口的触摸点 var allTouches = e.GetTouchPoints(this); foreach (var touchPoint in allTouches) { int touchId = touchPoint.TouchDevice.Id; switch (touchPoint.Action) { // 正常按下的触摸点 case TouchAction.Down: if (!_touchStartPoints.ContainsKey(touchId)) { // 捕获触摸设备,保证后续事件连续发送到当前窗口 touchPoint.TouchDevice.Capture(this); _touchStartPoints[touchId] = touchPoint; } break; // 触摸移动 case TouchAction.Move: // 没有记录的触摸点,说明是从窗口外滑入的,直接初始化 if (!_touchStartPoints.ContainsKey(touchId)) { touchPoint.TouchDevice.Capture(this); _touchStartPoints[touchId] = touchPoint; } // 矩形跟随触摸点移动,减去宽高的一半是让触摸点在矩形中心 Canvas.SetLeft(rect, touchPoint.Position.X - rect.Width / 2); Canvas.SetTop(rect, touchPoint.Position.Y - rect.Height / 2); break; // 触摸抬起或离开窗口,清理缓存 case TouchAction.Up: if (_touchStartPoints.ContainsKey(touchId)) { _touchStartPoints.Remove(touchId); touchPoint.TouchDevice.Capture(null); } break; } } } } }
注意事项
如果修改代码后边缘滑动仍然无法触发,大概率是Windows系统的边缘手势优先拦截了触摸事件(比如底部上滑调出任务栏、侧边滑动调出通知中心/小组件),可以在Windows系统设置的「蓝牙和其他设备-触摸」中关闭对应边缘手势即可。
内容的提问来源于stack exchange,提问作者Sajtospoga
相关产品推荐
相关产品推荐

