UWP ScrollViewer -区分用户滚动和编程滚动

本文关键字:滚动 编程 用户 ScrollViewer -区 UWP | 更新日期: 2023-09-27 18:09:33

我有一个应用程序,当用户到达ScrollViewer中的某个位置时,我需要采取特定的操作。此操作有时包括以编程方式将ScrollViewer滚动到不同的位置。

为了监视用户的滚动动作,我正在监听ScrollViewerViewChanged事件。问题是,当我从ViewChanged事件处理程序中以编程方式滚动时,相同的事件处理程序最终会再次被调用,导致不希望的额外滚动发生。

我已经尝试创建一个自定义方法来删除事件处理程序之前调用ScrollViewer.ChangeView(),但这似乎没有效果。

谁能想出一个解决这个问题的方法,或者一个方法来区分用户的滚动操作和我的编程操作?

private void MyScrollViewer_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
    if (conditionals) 
    {
        ScrollTo(location);
    }
}
private void ScrollTo(double offset)
{
    MyScrollViewer.ViewChanged -= MyScrollViewer_ViewChanged;
    MyScrollViewer.ChangeView(offset, null, null);
    MyScrollViewer.ViewChanged += MyScrollViewer_ViewChanged;
}

UWP ScrollViewer -区分用户滚动和编程滚动

不幸的是,无法确定是什么触发了ViewChanged事件。然而,解决这个问题是有可能的。

问题是ChangeView()是异步的,所以在调用ChangeView之后立即重新添加事件处理程序太快了。ChangeView将引发一系列ViewChanged事件,最后一个是e.IsIntermediate == false;只有在发生这种情况时,才应该重新挂钩事件处理程序。处理此问题的最佳方法可能是使用一个临时事件处理程序,等待e.IsIntermediate == false,然后重新挂钩原始处理程序。

为了防止用户在执行ChangeView期间与ScrollViewer交互,可以暂时禁用滚动和缩放模式。

最后,如果用户在满足条件的情况下操作ScrollViewer,则需要在调用ScrollTo()之前取消该操作。

EDIT:在我的实现中,出现了一个问题,由于调用这些处理程序的次数,事件处理程序被添加了不止一次。为了解决这个问题,我从这里采取了一个简单的策略。

private void MyScrollViewer_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e) 
{
    if (!conditionals) return;
    if (e.IsIntermediate) 
    {
        var uiElement = MyScrollViewer.Content as UIElement;
        uiElement?.CancelDirectManipulations();
    }
    ScrollTo(location);
}
private void Temporary_ViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
    if (e.IsIntermediate) return;
    MyScrollViewer.ViewChanged -= Temporary_ViewChanged;
    MyScrollViewer.ViewChanged -= MyScrollViewer_ViewChanged;
    MyScrollViewer.ViewChanged += MyScrollViewer_ViewChanged;
    MyScrollViewer.HorizontalScrollMode = ScrollMode.Enabled;
    MyScrollViewer.VerticalScrollMode = ScrollMode.Enabled;
    MyScrollViewer.ZoomMode = ZoomMode.Enabled;
}
private void ScrollTo(double offset)
{
    MyScrollViewer.ViewChanged -= MyScrollViewer_ViewChanged;
    MyScrollViewer.ViewChanged -= Temporary_ViewChanged;
    MyScrollViewer.ViewChanged += Temporary_ViewChanged;
    MyScrollViewer.HorizontalScrollMode = ScrollMode.Disabled;
    MyScrollViewer.VerticalScrollMode = ScrollMode.Disabled;
    MyScrollViewer.ZoomMode = ZoomMode.Disabled;
    MyScrollViewer.ChangeView(offset, null, null);
}