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 中无法滚动的问题,就解决了。

小结

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

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

相关推荐
军训猫猫头1 小时前
20.抽卡只有金,带保底(WPF) C#
ui·c#·wpf
明耀1 小时前
WPF 设置平均布局 如果隐藏的话,能够自动扩展
wpf
晚安苏州14 小时前
WPF DataTemplate 数据模板
wpf
甜甜不吃芥末1 天前
WPF依赖属性详解
wpf
Hat_man_2 天前
WPF制作图片闪烁的自定义控件
wpf
晚安苏州3 天前
WPF Binding 绑定
wpf·wpf binding·wpf 绑定
wangnaisheng3 天前
【WPF】RenderTargetBitmap的使用
wpf
dotent·4 天前
WPF 完美解决改变指示灯的颜色
wpf
orangapple5 天前
WPF 用Vlc.DotNet.Wpf实现视频播放、停止、暂停功能
wpf·音视频
ysdysyn5 天前
wpf mvvm 数据绑定数据(按钮文字表头都可以),根据长度进行换行,并把换行的文字居中
c#·wpf·mvvm