How to have photo viewer style pages? [closed]

2019-04-13 12:32发布

I don't know if I named it right, But I have an app which shows a row of pictures. If the user swipes left the previous picture in full screen appears, and if swipes right the next picture in full screen appears, both with a motion exactly the same as viewing pictures in Photo app, or a PDF reader. I thought I can manipulate the panorama control to fit this, but I couldn't show the picture in full screen and there is place for title at the top.

How can I do that? any tips

Note: Policies on this stackoverflow got annoying. There are some type of people who just can vote to close, or say some sentence snippets: what have you tried or where is your code. Close this question from the base to get a good feeling.

this is about asking guides to have a style of viewing.. what code should I show, if there is no idea of how to perform it? anyway I found my answer and no need to this.

1条回答
Melony?
2楼-- · 2019-04-13 13:01

I'll tell you what I did and maybe you'll find it adequate. I wanted a full-screen image viewer that lets me swipe to the next (or previous) image, but have it snap to the image instead of normal scrolling.

I used a fullscreen ListBox with the internal scrollViewer disabled (see XAML), then used some attached dependency properties to get a property for the Horizontal (and Vertical) Offset of the internal scrollViewer (so I can animate the scrolling myself). My implementation is a lot more involved because I wanted to also zoom (and then pan) the image, but the part that just goes to the next image is not hard to do.

Disclaimer: I've taken code from several sources on StackOverflow and other sites. I don't remember where I got them anymore, but I did not come up with these ideas all on my own. I'd be happy to give credit if I knew where to give it.

First, create a new class called ScrollViewerEx:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;

namespace ImageViewer    
{
    public class ScrollViewerEx
    {
    public static double GetHOffset(ScrollViewer obj)
    {
        return (double)obj.GetValue(ScrollViewer.HorizontalOffsetProperty);
    }

    public static void SetHOffset(ScrollViewer obj, double value)
    {
        obj.SetValue(HOffsetProperty, value);
    }

    // Using a DependencyProperty as the backing store for HOffset.  This enables animation, styling, binding, etc...  
    public static readonly DependencyProperty HOffsetProperty =
        DependencyProperty.RegisterAttached("HOffset", typeof(double), typeof(ScrollViewerEx), new PropertyMetadata(new PropertyChangedCallback(OnHOffsetChanged)));


    private static void OnHOffsetChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        var scroll = sender as ScrollViewer;

        scroll.ScrollToHorizontalOffset((double)e.NewValue);
    }

    public static double GetVOffset(ScrollViewer obj)
    {
        return (double)obj.GetValue(ScrollViewer.VerticalOffsetProperty);
    }

    public static void SetVOffset(ScrollViewer obj, double value)
    {
        obj.SetValue(VOffsetProperty, value);
    }

    // Using a DependencyProperty as the backing store for VOffset.  This enables animation, styling, binding, etc...  
    public static readonly DependencyProperty VOffsetProperty =
        DependencyProperty.RegisterAttached("VOffset", typeof(double), typeof(ScrollViewerEx), new PropertyMetadata(new PropertyChangedCallback(OnVOffsetChanged)));


    private static void OnVOffsetChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
    {
        var scroll = sender as ScrollViewer;

        scroll.ScrollToVerticalOffset((double)e.NewValue);
    }
}  

}

Ok, let's assume you have a listbox prepared like the one below. The Images property in my case was a class called PictureModel and inside there was an ImageSource. I'm not showing my ItemTemplate but just put an Image inside and bind the Source to your ImageSource. Notice the Rectangle below the ListBox. I put all my touch code there because when I was using zoomed images, my coordinate system was changing. Using the Rectangle overlay makes it so that I have the standard screen coordinates for all touches. You may not need this.

    <ListBox ItemsSource="{Binding Images}"
             x:Name="listBox"
             ScrollViewer.HorizontalScrollBarVisibility="Disabled"
             ScrollViewer.VerticalScrollBarVisibility="Disabled"
             ScrollViewer.ManipulationMode="Control"
             Loaded="listBox_Loaded_1" 

                  >
        <ListBox.Resources>
            <Storyboard x:Name="ScrollStoryboard">
                <DoubleAnimation x:Name="AnimationH" Duration="0:0:0.5">
                    <DoubleAnimation.EasingFunction>
                        <CubicEase EasingMode="EaseInOut"/>
                    </DoubleAnimation.EasingFunction>
                </DoubleAnimation>
                <DoubleAnimation x:Name="AnimationV" Duration="0:0:0.5">
                    <DoubleAnimation.EasingFunction>
                        <CubicEase EasingMode="EaseInOut"/>
                    </DoubleAnimation.EasingFunction>
                </DoubleAnimation>
            </Storyboard>
        </ListBox.Resources>
        <ListBox.ItemContainerStyle>
            <StaticResource ResourceKey="ListBoxItemPivotStyle"/>
        </ListBox.ItemContainerStyle>
    </ListBox>
    <Rectangle Fill="Transparent"
               x:Name="TouchRectangle"
               ManipulationCompleted="Rectangle_ManipulationCompleted_1"
               ManipulationDelta="Rectangle_ManipulationDelta_1"
               ManipulationStarted="Rectangle_ManipulationStarted_1"/>

Ok, another critical section. Make sure you put THIS in the constructor of your page. This is what allows you to animate your scrollviewer offset changes.

Storyboard.SetTargetProperty(ScrollStoryboard.Children[0], new PropertyPath(ScrollViewerEx.HOffsetProperty));
Storyboard.SetTargetProperty(ScrollStoryboard.Children[1], new PropertyPath(ScrollViewerEx.VOffsetProperty));

Get a permanent reference to the scrollviewer inside the ListBox:

private void listBox_Loaded_1(object sender, RoutedEventArgs e)
    {
        scrollviewer = GetVisualChild<ScrollViewer>(listBox);
    }

Finally, handle the manipulation events. The critical one to animate the listbox scroll is the manipulation completed event. I did not use the vertical offsets, just the horizontal ones. The variable vm.Position is a calculated position along the scrollviewer.horizontaloffset. Basically, if you're on the 5th image, then multiply the screen width by four to get the horizontal offset.

private void Rectangle_ManipulationCompleted_1(object sender, ManipulationCompletedEventArgs e)
{
    if (e.FinalVelocities.LinearVelocity.X > 2000)
        {

                if (ScrollStoryboard.GetCurrentState() != ClockState.Stopped)
                    ScrollStoryboard.Stop(); // ensure storyboard stopped after previous run  
                AnimationH.SetValue(DoubleAnimation.FromProperty, scrollviewer.HorizontalOffset);
                AnimationH.SetValue(DoubleAnimation.ToProperty, (double)vm.Position);
                Storyboard.SetTarget(ScrollStoryboard, scrollviewer);
                ScrollStoryboard.Begin();


        }
}

I hope this helps. Like I said, the full implementation I made includes data virtualization in addition to the built-in UI virtualization you get from the ListBox. That and zooming. It's not exactly ready to publish but this will get you started.

查看更多
登录 后发表回答