WPF --- 触摸屏下的两个问题

引言

本片文章分享一下之前遇到的WPF应用在触摸屏下使用时的两个问题。

场景

具体场景就是一个配置界面, ScrollViewer 中包含一个StackPanel 然后纵向堆叠,已滚动的方式查看,然后包含多个 TextBlockTextBox 以及DataGrid ,期间遇到了两个问题:

  • WPF在触摸屏下,如果有滚动条(ScrollViewer)的情况下,默认包含触底反馈的功能,就是触摸屏滑动到底或从底滑到顶,界面都会出现抖动的情况。
  • 触摸屏下,当触点处于 DataGrid 中时,无法滚动界面。

大概像这样:

解决方案

触底反馈抖动的问题

先来看第一个问题,这个其实是由于 ManipulationBoundaryFeedback 这个事件引起的:

最简单的做法,就是在对应包含ScrollViewer 的 UI 元素绑定它的反馈事件,然后在注册方法中设置 e.Handled = true; ,这样中断了事件继续冒泡或隧道传播,比如这样

csharp 复制代码
// 在Xaml中,在对应的 UIElement 上绑定ManipulationBoundaryFeedback="UIElement_ManipulationBoundaryFeedback"

//Code-Behind中 ,
private void UIElement_ManipulationBoundaryFeedback(object sender, ManipulationBoundaryFeedbackEventArgs e)
{
    e.Handled = true;
}

但是这样就需要你在每一个界面都添加该事件,代码冗余,那么就可以使用附加属性的方式,写一个 ManipulationBoundaryFeedbackAttachedProperties,各个界面直接使用,像这样实现:

csharp 复制代码
public class ManipulationBoundaryFeedbackAttachedProperties
{
    public static bool GetIsFeedback(DependencyObject obj)
    {
        return (bool)obj.GetValue(IsFeedbackProperty);
    }
    public static void SetIsFeedback(DependencyObject obj, bool value)
    {
        obj.SetValue(IsFeedbackProperty, value);
    }
    public static readonly DependencyProperty IsFeedbackProperty =
        DependencyProperty.RegisterAttached("IsFeedback", typeof(bool), typeof(UIElement), new PropertyMetadata(true,
            (s, e) =>
            {
                var target = s as UIElement;
                if (target != null)
                    target.ManipulationBoundaryFeedback += Target_ManipulationBoundaryFeedback;
            }));

    private static void Target_ManipulationBoundaryFeedback(object sender, ManipulationBoundaryFeedbackEventArgs e)
    {
        var target = sender as UIElement;
        if (target != null)
        {
            if (!GetIsFeedback(target))
            {
                e.Handled = true;
            }
        }
    }
}

像这样使用:

xml 复制代码
 <ScrollViewer local:ManipulationBoundaryFeedbackAttachedProperties.IsFeedback="true">
     ...
 </ScrollViewer>    

这样就完美解决了!

触点在DataGrid中无法滚动的问题

这个问题,其实不光在 DataGrid中有,触点在 TextBoxListViewListBox,这一类内置有 ScrollViewer 的控件内,都有同样的问题,而且不光是触摸屏无法滚动,鼠标滑轮也无法滚动。我处理这个问题的时候,是先处理的鼠标滑轮无法滚动,处理方案就是根据鼠标的偏移量,手动设置 ScrollViewer 的位置,如下:

csharp 复制代码
private void DataGrid_MouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e)
{
    var dataGrid = (DataGrid)sender;
    // 获取
    var scrollViewer = GetScrollViewer(dataGrid);

    if (scrollViewer != null)
    {
        if (scrollViewer.ViewportHeight + scrollViewer.VerticalOffset >= scrollViewer.ExtentHeight && e.Delta <= 0)
        {
            scrollViewer.LineDown();
        }
        else if (scrollViewer.VerticalOffset == 0 && e.Delta >= 0)
        {
            scrollViewer.LineUp();
        }
    }
}

public ScrollViewer GetScrollViewer(UIElement element)
{
    if (element == null) return null;

    ScrollViewer retour = null;
    for (int i = 0; i < VisualTreeHelper.GetChildrenCount(element) && retour == null; i++)
    {
        if (VisualTreeHelper.GetChild(element, i) is ScrollViewer)
        {
            retour = (ScrollViewer)(VisualTreeHelper.GetChild(element, i));
        }
        else
        {
            retour = GetScrollViewer(VisualTreeHelper.GetChild(element, i) as UIElement);
        }
    }
    return retour;
}

这样就解决了,当鼠标位于 DataGrid 中时,使用滑轮界面无法滚动的问题,那么解决触摸屏触点在 DataGrid 中无法滚动的问题,也是一样的思路,根据触点的偏移量,模拟鼠标滚轮的偏移量,在调用鼠标滚动事件,模拟滚动,代码如下:

csharp 复制代码
private const double TouchMoveThreshold = 20; // 触摸滚动的阈值

private Point lastTouchPosition; // 上一次触摸的位置

private void DataGrid_PreviewTouchMove(object sender, System.Windows.Input.TouchEventArgs e)
{
    // 获取当前触摸位置
    Point currentTouchPosition = e.GetTouchPoint((IInputElement)sender).Position;

    // 计算触摸移动的差值
    double deltaY = currentTouchPosition.Y - lastTouchPosition.Y;

    // 如果触摸移动超过阈值,则模拟鼠标滚动
    if (Math.Abs(deltaY) > TouchMoveThreshold)
    {
        // 设置鼠标滚动的差值
        int mouseWheelDelta = (int)(deltaY / TouchMoveThreshold) * SystemParameters.WheelScrollLines;

        // 创建模拟的鼠标滚动事件参数
        var mouseWheelEventArgs = new MouseWheelEventArgs(Mouse.PrimaryDevice, Environment.TickCount, mouseWheelDelta);
        mouseWheelEventArgs.RoutedEvent = UIElement.MouseWheelEvent;

        DataGrid_MouseWheel(sender, mouseWheelEventArgs);
        // 更新上一次触摸位置
        lastTouchPosition = currentTouchPosition;
    }
}

这样,触摸屏下,触点在 DataGrid 中无法滚动的问题,就解决了。

小结

总的来说,大部分鼠标和触摸屏事件是类似的,但是有些场景下,可能两者不通用的。所以可能需要自行测试一下,保证软件的稳定性。

本文中的解决方案不一定最完美的解决方案,如果各位看官有更好的解决方案,望不吝赐教。

相关推荐
2601_962174172 天前
Redis 简介
数据库·redis·wpf
SamChan903 天前
Python+PyMuPDF提取PDF表格并翻译:表格结构检测与双语重建实战
windows·python·ai·pdf·wpf
SamChan903 天前
Python+OpenCV检测PDF翻译后排版偏移:像素级格式一致性验证
python·opencv·ai·pdf·wpf
FuckPatience3 天前
WPF 拿到控件的最初样式,修改获得焦点样式
wpf
主宰者3 天前
【WPF】MainWindow前添加加载窗口,发现加载过程中有白色无内容窗口闪烁一下的解决方案
wpf
Vae_Mars4 天前
WPF中的ControlTemplate(控件模板)
wpf
SamChan904 天前
Python+ReportLab自动生成PDF翻译质量审计报告:从数据到可视化的完整方案
开发语言·python·ai·pdf·wpf
circuitsosk4 天前
多智能体协同在能源数据分析中的实践:分配-执行-校验闭环架构设计
python·数据分析·wpf·能源·并行计算·多智能体系统·agent协作
weixin199701080165 天前
《大促护航:电商API限流与降级,双11峰值500万调用的架构复盘》(附Python源码)
python·架构·wpf
hh9506 天前
gent Plan x DeepSeek Harness:多 Agent 协作场景下的中央编排实践
人工智能·wpf·adg·火山引擎·agent plan·adg成都社区