Pan, Zoom & Rotate Images With WPF using C#

Posted on September 24th, 2009, by Joel 6 Comments

WPF is a awesome tool for the person who wants his software to look good. So many effects as well as animations can be applied in WPF. Today I’m going to talk about how to have  UI which will allow a user to rotate zoom and pan an image within a WPF application.

Now lets get started by creating a wpf application, now your newly created application will have a window and a grid inside the window. See the below XAML

<window x:Class="PanandZoom.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="600" Width="800">
    <grid>
    </grid>
</window>

Inside the gird we created lets create two rows, one row will hold the slider for the rotation and the other row will hold the image which we will be playing with add the following snippet inside the <grid></grid> in the XAML.

<grid.RowDefinitions>
            <rowDefinition Height="50"/><!--the slider row-->
            <rowDefinition /><!--the image row-->
        </grid.RowDefinitions>
        <slider Grid.Row="0" Name="rslider" Value="0" Maximum="360" Minimum="0" Width="300" Height="30" TickPlacement="BottomRight" TickFrequency="36">
        </slider>
        <border Grid.Row="1" Name="border" Width="800" Height="500">
            <image Name="image" Source="winvis.jpg" Opacity="1" RenderTransformOrigin="0.5,0.5"  />
        </border>

now lets create the image and the slider which will reside in the different rows we created, make sure that you place the image inside a border to prevent any over flow of the image over other controls also add a high res image file to you project in Visual Studio, Just right click and add existing item. And now you can create an image object and place this image object inside the border object, I will tell you why you will be needing that later, so the final XAML looks like the following:

<window x:Class="PanandZoom.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="600" Width="800">
    <grid>
        <grid.RowDefinitions>
            <rowDefinition Height="50"/><!--the slider row-->
            <rowDefinition /><!--the image row-->
        </grid.RowDefinitions>
        <slider Grid.Row="0" Name="rslider" Value="0" Maximum="360" Minimum="0" Width="300" Height="30" TickPlacement="BottomRight" TickFrequency="36">
        </slider>
        <border Grid.Row="1" Name="border" Width="800" Height="500">
            <image Name="image" Source="winvis.jpg" Opacity="1" RenderTransformOrigin="0.5,0.5"  />
        </border>
    </grid>
</window>

Lets create some public point variables in the code behind page of the application window in which we created all these above XAML objects.

private Point origin;
private Point start;

These points are global and they are used in most of the events as well as methods.

Now lets create a TransformGroup object, to which we will add different types of transformation objects so all transformations can happen simultaneously also lets create these objects on form load or form initialization.

And now lets set the image’s (this is the image we created on XAML side) RenderTransform property to the TransformGroup object instance.
Now lets programmatically wire some events on form initialization.

public Window1()
        {
            InitializeComponent();
            //lets create a transform group which will handle all transformations
            TransformGroup TFG = new TransformGroup();
            ScaleTransform STF = new ScaleTransform();
            TFG.Children.Add(STF);
            TranslateTransform TTF = new TranslateTransform();
            TFG.Children.Add(TTF);
            RotateTransform RTF = new RotateTransform();
            TFG.Children.Add(RTF);
 
            //assign all transformations to this group
            image.RenderTransform = TFG;
 
            //lets add the events
            image.MouseWheel += image_MouseWheel;
            image.MouseLeftButtonDown += image_MouseLeftButtonDown;
            image.MouseLeftButtonUp += image_MouseLeftButtonUp;
            image.MouseMove += image_MouseMove;
            rslider.ValueChanged += rslider_ValueChanged;
 
        }

Now let me describe what happens when each event takes place,

Mouse Button Down – Mouse gets captured

        private void image_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            image.CaptureMouse();
            var TT = (TranslateTransform)((TransformGroup)image.RenderTransform).Children.First(tr => tr is TranslateTransform);
            start = e.GetPosition(border);
            origin = new Point(TT.X, TT.Y);
        }

Mouse Button Up – Mouse gets released

        private void image_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            image.ReleaseMouseCapture();
        }

Mouse Move – If mouse button is down then the image is moved

        private void image_MouseMove(object sender, MouseEventArgs e)
        {
            if (!image.IsMouseCaptured) return;
 
            var TT = (TranslateTransform)((TransformGroup)image.RenderTransform).Children.First(tr => tr is TranslateTransform);
            Vector vMM = start - e.GetPosition(border);
            TT.X = origin.X - vMM.X;
            TT.Y = origin.Y - vMM.Y;
        }

Mouse Wheel – Controls the zoom factor by mouse wheel

        private void image_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            TransformGroup TFG2 = (TransformGroup)image.RenderTransform;
            ScaleTransform STF2 = (ScaleTransform)TFG2.Children[0];
 
            double zoom = e.Delta > 0 ? .2 : -.2;
            STF2.ScaleX += zoom;
            STF2.ScaleY += zoom;
        }

Slider Move – Controls the angle of rotation

        private void rslider_ValueChanged(object sender, RoutedPropertyChangedEventArgs e)
        {
            var RT = (RotateTransform)((TransformGroup)image.RenderTransform).Children.First(tr => tr is RotateTransform);
            RT.Angle = rslider.Value;
            RT.CenterX = 10;
            RT.CenterY = 10;
        }

That’s about it, now you can programmatically handle the moving, rotating and zooming of an image. If you have any questions please feel free to ask in the comments below.

Part of this article is referred from StackOverflow

Related posts:

  1. Sending Mail Using C#
  2. SimplyIcon – Simple way to convert images into icons
  3. Translate Text within Images
  4. How To Enlarge Images Without Loosing Quality or Getting Pixelated
  5. TinEye Reverse Image Search,Upload Images and Search for similar ones
Posted in C#, Coding, WPF | Tags: , , , , ,

6 Responses to “Pan, Zoom & Rotate Images With WPF using C#”

  1. Rod says:

    I am a neophyte WPF programmer, and am having trouble with the syntax (tr => tr is …). Visual Studio 2008 seems to really hate this and I’m not sure what it means. Any clarification as to what I’m missing?

  2. Guest says:

    Basically look at it as the where part of a Linq query. You are sending “tr”(“tr” could be renamed to anything you like it is simply a way to reference the object being tested) which is one of the objects in the collection to test a condition. It iterates over the collection testing your condition and does something if the condition is true. In this case it grabs up the first ojbect that meets the condition.

  3. Roshan says:

    Can you please show how we can save the transformed image to the original… also if there is any way to preserve its native format.
    thank you.

  4. Can you show me how to hook this up with a scrollviewer, so I can use scrollbars to pan?

  5. Michael says:

    Hi Joel,

    your code helped me a lot! But I got the Problem that I can pan the image out of the image-element. Do you have any idea how i could solve this problem? I tried out a lot of things, but it’s my first time programming a gui in WPF…

    Thanks a lot for your help!

    Michael

  6. HoeMeng says:

    if I rotate 45degree follow with panning, happened that it moves toward different direction. Any ideal correct that?
    Thanks.