ScrollViewer.ChangeView处于活动状态时在屏幕上触摸会破坏应用程序UWP

时间:2016-12-07 11:40:34

标签: c# xaml listview uwp scrollview

我有windows phone 10(C#UWP)应用程序,我在listview中使用scrollviewer来显示图像的幻灯片。在滚动查看器的视图更改事件上,我检查哪个图像容器对于用户更加可见,并使用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时,滚动正在进行中,我点击并按住项目,项目调整大小,我可以在按住时移动它们,当我放开应用程序从上方处理未处理的异常时。

有什么想法吗?

1 个答案:

答案 0 :(得分:2)

您可能会干扰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?