Pan, Zoom & Rotate Images With WPF using C#

24 September, 2009
Share/Bookmark

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

Categories : C#, Coding, WPF Tags : , , , , ,

About Joel

Its All About Me

Other posts by Joel

Comments

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?

ReplyReply
Commented by Rod
on October 8, 2009

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.

ReplyReply
Commented by Guest
on December 24, 2009

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.

ReplyReply
Commented by Roshan
on January 8, 2010

RSS feed for comments on this post. TrackBack URI

Leave a comment

(required)

(required)