UWP - 父控件拦截指针事件(其中子控件为 ScrollViewer)

Posted

技术标签:

【中文标题】UWP - 父控件拦截指针事件(其中子控件为 ScrollViewer)【英文标题】:UWP - Interception of pointer events by parent control (where child is ScrollViewer) 【发布时间】:2021-10-11 12:35:30 【问题描述】:

我正在寻找一种方法来拦截所有事件,例如父级的 PointerReleased。

我的问题是我无法拦截 ScrollViewer 的父 PointerReleased 事件。

我想用手指将手指(光标)放入 ScrollViewer 移动 100 像素,然后松开;它应该像 Scrollviewer.IsHitTestVisible=false 一样工作,并将所有事件接收到 outerStack(scrollerviewer 的父级),但 scrollviewer 应该像 IsHitTestVisible=true 一样工作,并继续将所有相关事件获取到自身(scrollviewer)及其子级(scrollviewer 子级,如 innerStack )。

我收到以下日志

innerStack_PointerPressed
scrollView_PointerPressed
outerStack_PointerPressed
CoreWindow_PointerPressed

但我期待看到这个

innerStack_PointerPressed
scrollView_PointerPressed
outerStack_PointerPressed
CoreWindow_PointerPressed

outerStack_PointerReleased
CoreWindow_PointerReleased

有代码示例

<Page
    x:Class="App9.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="ThemeResource ApplicationPageBackgroundThemeBrush">

    <StackPanel x:Name="outerStack"
                Background="Red" 
                Padding="40"
                PointerPressed="outerStack_PointerPressed"
                PointerReleased="outerStack_PointerReleased">
        <ScrollViewer x:Name="scrollView"
                      Background="Blue" 
                      Padding="40"
                      PointerPressed="scrollView_PointerPressed"
                      PointerReleased="scrollView_PointerReleased"
                      VerticalScrollBarVisibility="Visible"
                      VerticalScrollMode="Enabled"
                      HorizontalScrollBarVisibility="Visible"
                      HorizontalScrollMode="Enabled"
                      Height="500">
            <StackPanel x:Name="innerStack"
                        Background="Green" 
                        Padding="40" 
                        PointerPressed="innerStack_PointerPressed"
                        PointerReleased="innerStack_PointerReleased">
                <TextBlock Text="A" FontSize="100"/>
                <TextBlock Text="B" FontSize="100"/>
                <TextBlock Text="C" FontSize="100"/>
                <TextBlock Text="D" FontSize="100"/>
                <TextBlock Text="E" FontSize="100"/>
                <TextBlock Text="F" FontSize="100"/>
                <TextBlock Text="G" FontSize="100"/>
                <TextBlock Text="H" FontSize="100"/>
                <TextBlock Text="I" FontSize="100"/>
                <TextBlock Text="J" FontSize="100"/>
                <TextBlock Text="K" FontSize="100"/>
                <TextBlock Text="L" FontSize="100"/>
                <TextBlock Text="M" FontSize="100"/>
                <TextBlock Text="N" FontSize="100"/>
                <TextBlock Text="O" FontSize="100"/>
                <TextBlock Text="P" FontSize="100"/>
                <TextBlock Text="Q" FontSize="100"/>
                <TextBlock Text="R" FontSize="100"/>
                <TextBlock Text="S" FontSize="100"/>
                <TextBlock Text="T" FontSize="100"/>
                <TextBlock Text="U" FontSize="100"/>
                <TextBlock Text="V" FontSize="100"/>
                <TextBlock Text="W" FontSize="100"/>
                <TextBlock Text="X" FontSize="100"/>
                <TextBlock Text="Y" FontSize="100"/>
                <TextBlock Text="Z" FontSize="100"/>
            </StackPanel>

        </ScrollViewer>
    </StackPanel>
</Page>

using System.Diagnostics;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Input;

namespace App9

    public sealed partial class MainPage : Page
    
        public MainPage()
        
            this.InitializeComponent();

            Window.Current.CoreWindow.PointerPressed += CoreWindow_PointerPressed;
            Window.Current.CoreWindow.PointerReleased += CoreWindow_PointerReleased;
        

        private void CoreWindow_PointerPressed(Windows.UI.Core.CoreWindow sender, Windows.UI.Core.PointerEventArgs args)
        
            Debug.WriteLine("CoreWindow_PointerPressed");
        

        private void CoreWindow_PointerReleased(Windows.UI.Core.CoreWindow sender, Windows.UI.Core.PointerEventArgs args)
        
            Debug.WriteLine("CoreWindow_PointerReleased");
        

        private void outerStack_PointerPressed(object sender, PointerRoutedEventArgs e)
        
            Debug.WriteLine("outerStack_PointerPressed");
        

        private void outerStack_PointerReleased(object sender, PointerRoutedEventArgs e)
        
            Debug.WriteLine("outerStack_PointerReleased");
        

        private void scrollView_PointerPressed(object sender, PointerRoutedEventArgs e)
        
            Debug.WriteLine("scrollView_PointerPressed");
        

        private void scrollView_PointerReleased(object sender, PointerRoutedEventArgs e)
        
            Debug.WriteLine("scrollView_PointerReleased");
        

        private void innerStack_PointerPressed(object sender, PointerRoutedEventArgs e)
        
            Debug.WriteLine("innerStack_PointerPressed");
        

        private void innerStack_PointerReleased(object sender, PointerRoutedEventArgs e)
        
            Debug.WriteLine("innerStack_PointerReleased");
        
    

【问题讨论】:

【参考方案1】:

最简单的解决方法是将next属性放到ScrollViewer的内容视图中

ScrollViewer.Content.ManipulationMode="TranslateX,System"

解决方案并不完美,但很简单。

更多信息https://docs.microsoft.com/ru-ru/archive/blogs/wsdevsol/where-did-all-my-gestures-go

【讨论】:

如果您已经解决了您的问题,请mark它被接受【参考方案2】:

我的问题是我无法拦截 ScrollViewer 的父 PointerReleased 事件。

请参阅Events and routed events overview 文档,将 Handled 属性设置为 true 会影响事件系统行为。当 Handled 为 true 时,大多数事件处理程序的路由停止;事件不会继续沿着路由通知其他附加的处理程序该特定事件情况。

所以你可以将滚动视图的PointerReleased Handled 设置为 true

private void scrollView_PointerPressed(object sender, PointerRoutedEventArgs e)

    Debug.WriteLine("scrollView_PointerPressed");
    e.Handled = true;


private void scrollView_PointerReleased(object sender, PointerRoutedEventArgs e)

    Debug.WriteLine("scrollView_PointerReleased");
    e.Handled = true;

【讨论】:

感谢您的回答!该链接真的很有帮助;我正在寻找 AddHandler 方法。但无论如何,它无济于事。如果用户在 ScrollViewer 上滚动手指(触摸模式,而不是鼠标),则 ScrollViewer 会引发 PointerCaptureLost 事件。并且 ScrollViewer 的 PARENT 不会知道任何未来的事件,即使使用 AddHandler 也是如此。目前尚不清楚如何处理手指运动,直到有关手指释放的最新事实。 从上面的案例描述中,你想拦截ScrollViewer的父级PointerReleased事件,为什么你的cmets告诉从父级捕获路由事件,你的真正目的是什么? 感谢您的回答!是的,我想拦截 ScrollViewer 的父事件。我有两条评论,一条是我现在得到的,第二条是我想要得到的,抱歉造成混乱。我的主要目标是为我的自定义手势识别器提供高级事件。因此,我想确保 ScrollViewer 在默认情况下正常工作,并且我的自定义手势识别器从 ScrollViewer 的父级获取所有事件以检测全屏手势。 根据测试,在设计的触摸模型中滚动时,滚动视图将中断点释放事件。并且为滚动视图父级注册手势不是一个好习惯,它会使交互过于复杂。 我们建议您使用按钮或其他控件来处理您上面提到的全局事件。

以上是关于UWP - 父控件拦截指针事件(其中子控件为 ScrollViewer)的主要内容,如果未能解决你的问题,请参考以下文章

Android知识点:设置父控件事件拦截

Android开发之解决父控件拦截子控件事件问题

Android-View的事件分发及拦截-父控件和子控件都处理触摸事件的方式

将子控件的点击事件传递给父控件

将子控件的点击事件传递给父控件

Antd Tree控件onCheck选中子节点时把父节点也选中