【问题标题】:Touch on screen while ScrollViewer.ChangeView is active breaks app UWP在 ScrollViewer.ChangeView 处于活动状态时触摸屏幕会中断应用程序 UWP
【发布时间】:2017-04-22 08:11:30
【问题描述】:

我有 windows phone 10 (C# UWP) 应用程序,我在列表视图中使用滚动查看器来显示图像幻灯片。在滚动查看器的视图更改事件中,我检查哪个图像容器对用户更可见,并使用 ChangeView 方法将该图像移动为唯一显示的图像。这一切都很好,但是如果我在 ChangeView 处于活动状态时按住触摸屏,图像会调整大小,并且我会收到一个错误“此对象已被密封,因此不再允许此更改”,我无法捕捉到。

XAML: (注意,CurrentSizeConverter 只是根据参数给出可见的页面边界)

<Page.Resources>             
    <DataTemplate x:Key="dtPhotoView">
        <Grid x:Name="grPhotoView" Width="{Binding Id, Converter={StaticResource CurrentSizeConverter}, ConverterParameter=Width}" Height="{Binding Id, Converter={StaticResource CurrentSizeConverter}, ConverterParameter=Height}">
            <Grid.RowDefinitions>
                <RowDefinition Height="*"/>
                <RowDefinition Height="50"/>
            </Grid.RowDefinitions>
            <Image x:Name="imgFullSize" Source="{Binding ImageSource}" Stretch="Fill" Grid.RowSpan="2"/>
            <Grid Grid.Row="1" x:Name="grDeleteFullImage" Background="#66000000">
                <Button x:Name="btnDeletePhoto" Style="{StaticResource btnActionCommandButtonStyle}" Tag="{Binding Id}" Canvas.ZIndex="10" Margin="0" Click="btnDeletePhoto_Click" Background="#66000000" Padding="10">
                    <Button.Foreground>
                        <ImageBrush Stretch="Uniform" ImageSource="Assets/delete_icon.png"/>
                    </Button.Foreground>
                </Button>
            </Grid>
        </Grid>
    </DataTemplate>
</Page.Resources>

<Grid>
    <ListView HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Visibility="Collapsed" SelectionMode="None" IsItemClickEnabled="False" x:Name="lvPhotoView" Grid.Row="0" DataContext="{Binding}" Background="Transparent" BorderThickness="0" ItemTemplate="{StaticResource dtPhotoView}" ScrollViewer.ZoomMode="Disabled"  ScrollViewer.VerticalScrollBarVisibility="Hidden" ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.HorizontalScrollMode="Enabled" ScrollViewer.VerticalScrollMode="Disabled" PointerEntered="lvPhotoView_PointerEntered">
        <ListView.ItemContainerStyle>
            <Style TargetType="ListViewItem">
                <Setter Property="HorizontalAlignment" Value="Stretch" />
                <Setter Property="VerticalAlignment" Value="Stretch" />
                <Setter Property="HorizontalContentAlignment" Value="Stretch"/>
                <Setter Property="Margin" Value="2" />
            </Style>
        </ListView.ItemContainerStyle>
        <ListView.ItemsPanel>
            <ItemsPanelTemplate>
                <ItemsStackPanel Orientation="Horizontal"/>
            </ItemsPanelTemplate>
        </ListView.ItemsPanel>
    </ListView> .........

私有变量:

private static bool _imagesShowing = false;
private ScrollViewer _imagesViewer;

事件处理:

private void lvPhotoView_PointerEntered(object sender, Windows.UI.Xaml.Input.PointerRoutedEventArgs e)
{
    try
    {
        if (!_imagesShowing)
        {
            _imagesShowing = true;

            if (_imagesViewer == null)
            {
                _imagesViewer = GetScrollViewer(lvPhotoView);
            }

            if (_imagesViewer != null)
            {
                _imagesViewer.ViewChanged += ImagesScrollViewer_OnViewChanged;
            }
        }
    }
    catch (Exception ex)
    {
        return;
    }
}

public static ScrollViewer GetScrollViewer(DependencyObject depObj)
{
    try
    {
        if (depObj is ScrollViewer)
        {
            return depObj as ScrollViewer;
        }

        for (var i = 0; i < VisualTreeHelper.GetChildrenCount(depObj); i++)
        {
            var child = VisualTreeHelper.GetChild(depObj, i);

            var result = GetScrollViewer(child);
            if (result != null)
            {
                return result;
            }
        }
        return null;
    }
    catch
    {
        return null;
    }
}

private async void ImagesScrollViewer_OnViewChanged(object sender, ScrollViewerViewChangedEventArgs e)
{
    if (!e.IsIntermediate)
    {
        var isDone = false;
        try
        {
            _imagesViewer.ViewChanged -= ImagesScrollViewer_OnViewChanged;
            _imagesViewer.HorizontalScrollMode = ScrollMode.Disabled;
            _imagesViewer.IsScrollInertiaEnabled = false;

            for (var i = 0; i < lvPhotoView.Items.Count; i++)
            {
                var item = lvPhotoView.Items[i];
                var itemContainer = lvPhotoView.ContainerFromItem(item) as ListViewItem;

                double firstVisValue;

                var isVisible = itemContainer.IsVisibileToUserHorizontal(sender as ScrollViewer, out firstVisValue);

                if (isVisible && lvPhotoView.Items.Count - 1 > i)
                {
                    var nextItem = lvPhotoView.Items[i + 1];
                    var secondItemContainer = lvPhotoView.ContainerFromItem(nextItem) as ListViewItem;

                    double secondVisValue;

                    var isNextVisible = secondItemContainer.IsVisibileToUserHorizontal(sender as ScrollViewer, out secondVisValue);

                    if (isNextVisible)
                    {

                        _imagesViewer.ScrollToElement(firstVisValue < secondVisValue ? secondItemContainer : itemContainer);
                    }

                    await Task.Delay(800);
                    _imagesViewer.HorizontalScrollMode = ScrollMode.Enabled;
                    _imagesViewer.IsScrollInertiaEnabled = true;
                    _imagesShowing = false;
                    isDone = true;
                }
            }
        }
        finally
        {
            if (!isDone)
            {
                await Task.Delay(500);
                _imagesViewer.HorizontalScrollMode = ScrollMode.Auto;
                _imagesViewer.IsScrollInertiaEnabled = true;
                _imagesShowing = false;
            }
        }
    }
}

public static bool IsVisibileToUserHorizontal(this FrameworkElement element, FrameworkElement container, out double visValue)
{
    visValue = 0;

    if (element == null || container == null)
    {
        return false;
    }

    if (element.Visibility != Visibility.Visible)
    {
        return false;
    }

    var elementBounds = element.TransformToVisual(container).TransformBounds(new Rect(0.0, 0.0, element.ActualWidth, element.ActualHeight));
    var containerBounds = new Rect(0.0, 0.0, container.ActualWidth, container.ActualHeight);

    if (elementBounds.Left >= containerBounds.Left && elementBounds.Left < containerBounds.Right)
    {
        visValue = containerBounds.Right - elementBounds.Left;
        return true;
    }

    if (elementBounds.Right >= containerBounds.Left && elementBounds.Right < containerBounds.Right)
    {
        visValue = elementBounds.Right - containerBounds.Left;
        return true;
    }

    return false;
}

public static void ScrollToElement(this ScrollViewer scrollViewer, UIElement element, bool isHorizontalScrolling = true, bool smoothScrolling = true, float? zoomFactor = null)
{
    var transform = element.TransformToVisual((UIElement)scrollViewer.Content);
    var position = transform.TransformPoint(new Point(0, 0));

    if (isHorizontalScrolling)
    {
        scrollViewer.ChangeView(position.X, null, zoomFactor, !smoothScrolling);
    }
    else
    {
        scrollViewer.ChangeView(null, position.Y, zoomFactor, !smoothScrolling);
    }
}

最后,将任何可观察的集合列表与 xaml 绑定属性绑定,其中 ImageSource 属性是图像 StorageFile(在我的情况下它们都是 .jpg)到列表视图。

基本上当 smoothScrolling 为 true 时,滚动正在进行,我点击并按住项目,项目调整大小,我可以在按住的同时移动它们,当我放开应用程序刹车时,来自上方的未处理异常。

有什么想法吗?

【问题讨论】:

  • 没有足够的代码来重现您的问题。请参考How to Ask提供转载项目。至少在哪里以及如何调用ScrollToElement 方法。
  • 感谢您的提示,我更新了代码 sn-ps,现在一切都应该可以重现了。因此,当您滚动并松开 ScrollToElement 时,会调用滚动视图,并且滚动视图会自动滚动到对用户更可见的图像。在此自动滚动进行时,点击屏幕并向上/向下滑动,问题就会发生。

标签: c# xaml listview uwp scrollview


【解决方案1】:

您可能会干扰 DirectManipulation,因此滚动时出现异常。

发生的事情是,每当有“导致 FlipView 动画的触摸事件”时,导致该动画的是 FlipView 中内置的 ScrollViewer,用于翻转。滚动查看器会带走指针输入,直到平移完成并且无法取回。这是一个称为 DirectManipulation 的功能,它现在在单独的线程上处理输入,以提供尽可能平滑的平移处理。 (指针事件在 UI 线程中触发)http://social.msdn.microsoft.com/Forums/windowsapps/en-US/1e6732d3-0457-4ddc-b762-963ab974491c/pointerreleased-and-flipview

这里遇到同样的问题:Why ScrollViewer fired PointerCaptureLost when starting scroll?

【讨论】:

  • 滚动时没有异常。我也没有使用 FlipView。我正在以编程方式启动 ListView 中内置的 ScrollViewer 的动画,然后,在动画进行时,当我轻触并向上/向下(不是向左/向右)移动时,我得到了异常。
  • 是的,我给出的示例来自 FlipView,但在内部它是一个 ScrollViewer,与您的情况相同。关键是,ScrollViewer 使用直接操作,这是一个硬件加速的专有管道。一旦您尝试摸索管道中涉及的数据,您总会遇到异常。
  • 我明白了,这确实让我更好地了解了导致问题的原因以及在哪里查看。您对一些解决方法有什么好主意(不允许用户在 ScrollView 持有指针时点击 ListView,类似这样)?
  • 您绝对应该管理何时允许用户交互。就像在忙碌时取消订阅触摸事件并在返回之前重新订阅一样简单。
猜你喜欢
  • 2012-05-30
  • 1970-01-01
  • 2015-02-22
  • 1970-01-01
  • 2012-07-18
  • 2014-12-30
  • 1970-01-01
  • 2011-06-11
  • 1970-01-01
相关资源
最近更新 更多