Home > Windows Phone > Smooth flick and zoom with Viewport control for Windows Phone

Smooth flick and zoom with Viewport control for Windows Phone

One of best features of Windows Phone is its fluid way for flick and zoom of images in your photo albums. Yet, most of apps that show images aren’t implemented that way of handling images (e.g. Facebook, Twitter, WhatsApp, etc.) and flicking images is pretty laggy in my opinion (actually it just a pan). However, there is simple solution for such cases and it is called Viewport control.

Essentially, Viewport control for Windows Phone handles pan and flick gestures of its content and provides smooth bounce effect when you flick content (image, canvas, etc.) that is larger than size of screen or viewport itself. You can see that effect in photo album or in Internet Explorer.

Implementation is very simple: just add Viewport control to page, put Image inside it and set Viewport Bound property to size of the image.

<Grid x:Name="LayoutRoot" Background="White">
    <ViewportControl Bounds="0,0,1249,1920">
        <Image Source="/Assets/Saga_14.jpg" />
    </ViewportControl>
</Grid>

And that is it. See how it looks:

Zoom

In order to implement zoom we have to manualy resize image and set view point in ManipulationDelta event of Viewport control. Set XAML code in this way:

<Grid x:Name="LayoutRoot" Background="White">
    <ViewportControl x:Name="viewport" 
        ManipulationDelta="viewport_ManipulationDelta" 
        ManipulationCompleted="viewport_ManipulationCompleted" >
        <Image x:Name="image" Source="/Assets/Saga_14.jpg" 
               CacheMode="BitmapCache" Loaded="image_Loaded" 
               Stretch="None" Source="/Assets/Saga_14.jpg" />
    </ViewportControl>
</Grid>

In ManipulationDelta event we check for Pinch event (e.PinchManipulation != null) and use e.PinchManipulation.CumulativeScale value to calculate new size of image. Also we use e.PinchManipulation.Original.Center value to calculate new point of view for image, sice we want to zoom in and out at that place that pinch actualy occured on screen. To do that we have to do several things:

  • Get offset of image in viewport (or how much is image moved in viewport using transform.Matrix.OffsetX and transform.Matrix.OffsetY values):

     MatrixTransform transform = image.TransformToVisual(viewport) as MatrixTransform;

  • Calculate center of pinch gesture on image (not screen):

     Point pinchCenterOnImage = transform.Transform(e.PinchManipulation.Original.Center);

  • Calculate and set new origin point of viewport:

     Point newOriginPoint = new Point(relativeCenter.X * newWidth – pinchCenterOnImage.X, relativeCenter.Y * newHieght – pinchCenterOnImage.Y);
viewport.SetViewportOrigin(newOriginPoint); 

 

Complete code behind in ManipulationDelta event looks like:

private double m_Zoom = 1;
private double m_Width = 0;
private double m_Height = 0;

private void viewport_ManipulationDelta(object sender, 
System.Windows.Input.ManipulationDeltaEventArgs e)
{
    if (e.PinchManipulation != null)
    {

        double newWidth, newHieght;


        if (m_Width < m_Height)  // box new size between image and viewport
        {
            newHieght = m_Height * m_Zoom * e.PinchManipulation.CumulativeScale;
            newHieght = Math.Max(viewport.ActualHeight, newHieght);
            newHieght = Math.Min(newHieght, m_Height);
            newWidth = newHieght * m_Width / m_Height;
        }
        else
        {
            newWidth = m_Width * m_Zoom * e.PinchManipulation.CumulativeScale;
            newWidth = Math.Max(viewport.ActualWidth, newWidth);
            newWidth = Math.Min(newWidth, m_Width);
            newHieght = newWidth * m_Height / m_Width;
        }


        if (newWidth < m_Width && newHieght < m_Height)
        {
            MatrixTransform transform = image.TransformToVisual(viewport) 
              as MatrixTransform;
            Point pinchCenterOnImage = 
              transform.Transform(e.PinchManipulation.Original.Center);
            Point relativeCenter = 
              new Point(e.PinchManipulation.Original.Center.X / image.Width, 
              e.PinchManipulation.Original.Center.Y / image.Height);
            Point newOriginPoint = new Point(
              relativeCenter.X * newWidth - pinchCenterOnImage.X, 
              relativeCenter.Y * newHieght - pinchCenterOnImage.Y);
            viewport.SetViewportOrigin(newOriginPoint);
        }

        image.Width = newWidth;
        image.Height = newHieght;

        // Set new view port bound
        viewport.Bounds = new Rect(0, 0, newWidth, newHieght);
    }
}

private void viewport_ManipulationCompleted(object sender, 
System.Windows.Input.ManipulationCompletedEventArgs e)
{
    m_Zoom = image.Width / m_Width;
}

 

Here is zoom in action

 

You can download sample code on http://code.msdn.microsoft.com/wpapps/Smooth-flick-and-zoom-with-7760c7f7

Categories: Windows Phone Tags: , , , , ,
  1. April 12, 2014 at 11:00 am

    Nice! Didn’t know ViewPort can handle such a fluid flick effect.
    I’ll be putting this control to my Image apps.

    Thanks!

  2. Ravi
    May 28, 2014 at 7:47 am

    Hello,
    I am using this example in my application. But on a click of a button I will be changing the image source. After the image changes it doesn’t work as it works for first image. I have moved the code from image_Loaded to image_opened to get this to work but still doesn’t work. Can you provide solution for this?

  3. JPHellemons
    June 17, 2014 at 9:35 am

    Panning works, but when I pinch to start zooming. The image disappears..

    • JPHellemons
      June 17, 2014 at 10:59 am

      where do you set to the correct value?
      private double m_Zoom = 1;
      private double m_Width = 0;
      private double m_Height = 0;

  4. June 25, 2014 at 12:17 pm

    Is there any way to make the image centered at the beginning?

  5. Zrn
    December 19, 2014 at 10:02 am

    Is there any way to make the image centered at the beginning?

  1. April 11, 2014 at 8:38 am

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: