Click here to Skip to main content
Click here to Skip to main content

WPF Interactive Image Cropping Control

, 6 Sep 2007
Rate this:
Please Sign up or sign in to vote.
WPF Interactive Image Cropping Control

Contents

Introduction

Recently a friend of mine who has just started a company pointed me at this site which does lots of fancy image editing. Although he doesn't know XAML or Sliverlight he reckoned some of the stuff they were doing was excellent, and asked me to look into doing something similar in WPF. While this article represents only a small portion of what that website can do (namely image cropping) I feel that it outlines some useful techniques and study notes for those of you that may end up trying to do image editing applications in WPF/Silverlight. Although I cannot categorically state that 100% of this article will work with Silverlight as it has really been written in WPF, I am waiting to play with the managed version of Silverlight v1.1. JavaScript leaves me cold (nasty stuff). So after I've had a play with that I should be able to write WPF articles that I know will work with Silverlight. Till then, I'm afraid if you want a Silverlight version, you'll just have to try a code port for yourself.

So what is this article exactly? Well like I said, my friend asked me to create a posh image cropper in WPF. So that's really what it is. It's an image cropping control that may be placed within any other XAML and used to crop images. The cropping of the image is achieved by firstly drawing a shape and then moving the shape around to the desired position before completing and accepting the cropped image.

That's it, in a nutshell. It's a simple image cropper basically written in WPF.

Design Steps

I actually quite liked the image cropping here, so I wanted to create one as similar to that as I could. To this end my core function brief to myself was as follows:

  • Core Function 1 : It should support an owner drawn cropping area which was semi-transparent
  • Core Function 2 : The cropped area should be able to be moved
  • Core Function 3 : The user should be able to resize the crop area
  • Core Function 4 : The user should be able to accept or reject a particular crop operation

Those were the basic steps I wanted to cover. There were however a few more extended functions that I imposed on myself which are as follows:

  • Extended Function 1 : The cropping functionality should be wrapped up into a single re-usable WPF user control
  • Extended Function 2 : The user should be able to resize the image, in order to be able to see the entire image if dealing with a very large image

These were the tasks that I set myself for the purpose of this article. In the next section, I'll explain how I achieved or failed to achieve these tasks.

How It Works

So what I'll do now is explain each of the core/extended functions mentioned earlier.

Core Function 1 : Support a owner drawn cropping area

This was fairly easy to do, I simply subclassed the System.Windows.Controls.Canvas and overrode the mouse events such that when the mouse was moved, a new child UIElement was added to the new subclassed Canvas. Basically every time the user moved the mouse the new System.Windows.Shapes.Rectangle was either added or resized, using the mouse co-ordinates. This is a similar concept to the old tried and tested .NET 2.0 ControlPaint.DrawReversibleFrame() method. By subclassing the System.Windows.Controls.Canvas meant that this System.Windows.Controls.Canvas could be used within any code or XAML file.

A demonstration of this subclassed Canvas in action is shown below:

And the code that carries out this functionality is pretty simple and is shown below.

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace ImageCropper
{
    /// <span class="code-SummaryComment"><summary>
</span>
    /// Provides a Canvas where a rectangle will be drawn
    /// that matches the selection area that the user drew
    /// on the canvas using the mouse
    /// <span class="code-SummaryComment"></summary>
</span>
    public partial class SelectionCanvas : Canvas
    {
        #region Instance fields
        private Point mouseLeftDownPoint;
        private Style cropperStyle;
        public Shape rubberBand = null;
        public readonly RoutedEvent CropImageEvent;
        #endregion

        #region Events
        /// <span class="code-SummaryComment"><summary>
</span>
        /// Raised when the user has drawn a selection area
        /// <span class="code-SummaryComment"></summary>
</span>
        public event RoutedEventHandler CropImage
        {
            add { AddHandler(this.CropImageEvent, value); }
            remove { RemoveHandler(this.CropImageEvent, value); }
        }
        #endregion

        #region Ctor
        /// <span class="code-SummaryComment"><summary>
</span>
        /// Constructs a new SelectionCanvas, and registers the
        /// CropImage event
        /// <span class="code-SummaryComment"></summary>
</span>
        public SelectionCanvas()
        {
            this.CropImageEvent = EventManager.RegisterRoutedEvent
        ("CropImage", RoutingStrategy.Bubble,
        typeof(RoutedEventHandler), typeof(SelectionCanvas));
        }
        #endregion

        #region Public Properties
        public Style  CropperStyle
        {
            get { return cropperStyle; }
            set { cropperStyle = value; }
        }
        #endregion

        #region Overrides

        /// <span class="code-SummaryComment"><summary>
</span>
        /// Captures the mouse
        /// <span class="code-SummaryComment"></summary>
</span>
        protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
        {
            base.OnMouseLeftButtonDown(e);
            if (!this.IsMouseCaptured)
            {
                mouseLeftDownPoint = e.GetPosition(this);
                this.CaptureMouse();
            }
        }

        /// <span class="code-SummaryComment"><summary>
</span>
        /// Releases the mouse, and raises the CropImage Event
        /// <span class="code-SummaryComment"></summary>
</span>
        protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
        {
            base.OnMouseLeftButtonUp(e);

            if (this.IsMouseCaptured && rubberBand != null)
            {
                this.ReleaseMouseCapture();

                RaiseEvent(new RoutedEventArgs(this.CropImageEvent, this));
            }
        }

        /// <span class="code-SummaryComment"><summary>
</span>
        /// Creates a child control
        /// <span class="code-SummaryComment"><see cref="System.Windows.Shapes.Rectangle">Rectangle</see>
</span>
        /// and adds it to this controls children collection
        /// at the co-ordinates the user
        /// drew with the mouse
        /// <span class="code-SummaryComment"></summary>
</span>
        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);

            if (this.IsMouseCaptured)
            {
                Point currentPoint = e.GetPosition(this);

                if (rubberBand == null)
                {
                    rubberBand = new Rectangle();
                    if (cropperStyle != null)
                        rubberBand.Style = cropperStyle;
                    this.Children.Add(rubberBand);
                }

                double width = Math.Abs(mouseLeftDownPoint.X - currentPoint.X);
                double height =
            Math.Abs(mouseLeftDownPoint.Y - currentPoint.Y);
                double left = Math.Min(mouseLeftDownPoint.X, currentPoint.X);
                double top = Math.Min(mouseLeftDownPoint.Y, currentPoint.Y);

                rubberBand.Width = width;
                rubberBand.Height = height;
                Canvas.SetLeft(rubberBand, left);
                Canvas.SetTop(rubberBand, top);
            }
        }
        #endregion
    }
}

It can be seen that the crop area is actually a Rectangle. I initially had this to be a set color. But Josh Smith suggested that I change this to include a user allowable style dependency property. So I created a CropperStyle dependency property on the main UcImageCropper where both this canvas and the DragCanvas shown below are contained.

Core Function 2 : The cropped area should be able to be moved

Well, this was easy (really easy) as all I do is swap out the current selectionCanvas for a DragCanvas being careful to remove the current crop area (Rectangle) from the current selectionCanvas children collection, and add it to the children of the DragCanvas.

The reason this was so easy is that all the work had been done already by someone else, I simply saw an opportunity of how to use it. The original article is by Josh Smith and the particular article that I used is hosted right here at CodeProject. Its called Dragging Elements in a Canvas. So thanks for that Josh. I hope you like the use of it in this code.

Once the DragCanvas is in place, the user may then drag the crop area to wherever they like. When happy they may use the context menu (right click) to either save the image, or start over.

Core Function 3 : The crop area should be able to be resized

My immediate thought here was to use a System.Windows.Documents.Adorner. For those that have no idea what the heck I'm talking about here, to put it simply, adorners allow you to apply extra functionality to a UIElement such as rotation, sizing etc. There are a number of nice sources about this such as:

  • Adorners Samples MSDN
  • Adorners In WPF, for a nice simply intro

Unfortunately, as Josh Smith's DragCanvas uses the mouse events, and the nice MSDN ResizeAdorner sample also uses the mouse events, it was a bit of a battle to get them to work correctly. To this end I had to ditch the resizing of the crop area. But if anyone wants to give it a go, System.Windows.Documents.Adorners would be the way to go. My idea was simply to use a ResizeAdorner (MSDN) to adorn the current crop rectangle, and that way the user could not only drag (thanks to DragCanvas) but also resize. That was the idea anyway.

Core Function 4 : Accept / Reject crop

To allow the user to preview what the image would look like when it is cropped, there is a small popup which allows the user to either accept or reject the crop. If the user accepts, the cropped image will be used as the new source for the current image. If the user rejects the crop, the existing image will be used without any cropping being performed.

Extended Function 1 : Wrapped As A Control

Reuse is good. To this end I have wrapped up all this functionality into a single re-usable control called ucImageCropper which can be used in other XAML files.

The source code for the ucImageCropper is as shown below:

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using System.IO;

#region Explanation of why this .NET3.0 app is using .NET2.0 Dlls
//For some very simple .NET niceties like being able to save a bitmap
//to a filename I have had to use the System.Drawing .NET 2.0 DLL
//
//While this looks possible using something like the following :
//
//RenderTargetBitmap rtb = new RenderTargetBitmap((int)img.width,
//(int)img, 0, 0, PixelFormats.Default);
//rtb.Render(this.inkCanv);
//BmpBitmapEncoder encoder = new BmpBitmapEncoder();
//encoder.Frames.Add(BitmapFrame.Create(rtb));
//encoder.Save(file);
//file.Close();
//
//For this to work I would have needed to used a .NET 3.0 CroppedBitmap
//within the RenderTargetBitmap.Render() method. And as CroppedBitmap
//doesn't inherit from Visual this is not possible.
//
//So if anyone knows how to do this better in .NET 3.0 I am all ears
#endregion
using System.Drawing;
using System.Drawing.Drawing2D;

//Josh Smith excellent DragCanvas
using WPF.JoshSmith.Controls;

namespace ImageCropper
{

    /// <span class="code-SummaryComment"><summary>
</span>
    /// Provides a simple Image cropping facility for a WPF image element,
    /// where the cropped area may be picked using a rubber band and moved
    /// by dragging the rubber band around the image. There is also a popup
    /// window from where the user may accept or reject the crop.
    /// <span class="code-SummaryComment"></summary>
</span>
    public partial class UcImageCropper : System.Windows.Controls.UserControl
    {

        #region CropperStyle Dependancy property

        /// <span class="code-SummaryComment"><summary>
</span>
        /// A DP for the Crop Rectangle Style
        /// <span class="code-SummaryComment"></summary>
</span>
        public Style CropperStyle
        {
            get { return (Style)GetValue(CropperStyleProperty); }
            set { SetValue(CropperStyleProperty, value); }
        }

        /// <span class="code-SummaryComment"><summary>
</span>
        /// register the DP
        /// <span class="code-SummaryComment"></summary>
</span>
        public static readonly DependencyProperty CropperStyleProperty =
            DependencyProperty.Register(
            "CropperStyle",
            typeof(Style),
            typeof(UcImageCropper),
            new UIPropertyMetadata(null, new PropertyChangedCallback
                        (OnCropperStyleChanged)));

        /// <span class="code-SummaryComment"><summary>
</span>
        /// The callback that actually changes the Style if one was provided
        /// <span class="code-SummaryComment"></summary>
</span>
        /// <span class="code-SummaryComment"><param name="depObj">UcImageCropper</param>
</span>
        /// <span class="code-SummaryComment"><param name="e">The event args</param>
</span>
        static void OnCropperStyleChanged(DependencyObject depObj,

                    DependencyPropertyChangedEventArgs e)
        {
            Style s = e.NewValue as Style;
            if (s != null)
            {
                UcImageCropper uc = (UcImageCropper)depObj;
                uc.selectCanvForImg.CropperStyle = s;
            }
        }
        #endregion

        #region Instance fields
        private string ImgUrl = "";
        private BitmapImage bmpSource = null;
        private SelectionCanvas selectCanvForImg = null;
        private DragCanvas dragCanvasForImg = null;
        private System.Windows.Controls.Image img = null;
        private Shape rubberBand;
        private double rubberBandLeft;
        private double rubberBandTop;
        private string tempFileName;
        private ContextMenu cmSelectionCanvas;
        private RoutedEventHandler cmSelectionCanvasRoutedEventHandler;
        private ContextMenu cmDragCanvas;
        private RoutedEventHandler cmDragCanvasRoutedEventHandler;
        private string fixedTempName = "temp";
        private long fixedTempIdx = 1;
        private double zoomFactor=1.0;
        #endregion

        #region Ctor
        public UcImageCropper()
        {
            InitializeComponent();

            //this.Unloaded += new RoutedEventHandler
                        (UcImageCropper_Unloaded);
            selectCanvForImg = new SelectionCanvas();
            selectCanvForImg.CropImage +=
            new RoutedEventHandler(selectCanvForImg_CropImage);
            dragCanvasForImg = new DragCanvas();
        }

        #endregion

        #region Public properties
        public string ImageUrl
        {
            get { return this.ImgUrl; }
            set
            {
                zoomFactor = 1.0;
                ImgUrl = value;
                grdCroppedImage.Visibility = Visibility.Hidden;
                createImageSource();
                createSelectionCanvas();
                //apply the default style if the user of this control
                //didn't supply one
                if (CropperStyle == null)
                {
                    Style s = gridMain.TryFindResource("defaultCropperStyle")
                                 as Style;
                    if (s != null)
                    {
                        CropperStyle = s;
                    }
                }

            }
        }
        #endregion

        #region Private methods
        /// <span class="code-SummaryComment"><summary>
</span>
        /// Deletes all occurrences of previous unused temp files from the
        /// current temporary path
        /// <span class="code-SummaryComment"></summary>
</span>
        /// <span class="code-SummaryComment"><param name="tempPath">The temporary file path</param>
</span>
        /// <span class="code-SummaryComment"><param name="fixedTempName">The file name part to search for
</span>
        /// <span class="code-SummaryComment"></param>
</span>
        /// <span class="code-SummaryComment"><param name="CurrentFixedTempIdx">The current temp file suffix
</span>
        /// <span class="code-SummaryComment"></param>
</span>
        public void CleanUp(string tempPath, string fixedTempName,
                        long CurrentFixedTempIdx)
        {
            //clean up the single temporary file created
            try
            {
                string filename = "";
                for (int i = 0; i < CurrentFixedTempIdx; i++)
                {
                    filename = tempPath + fixedTempName + i.ToString()+".jpg";
                    File.Delete(filename);
                }
            }
            catch (Exception)
            {
            }
        }

        /// <span class="code-SummaryComment"><summary>
</span>
        /// Popup form Cancel clicked, so created the SelectionCanvas
        /// to start again
        /// <span class="code-SummaryComment"></summary>
</span>
        private void btnCancel_Click(object sender, RoutedEventArgs e)
        {
            grdCroppedImage.Visibility = Visibility.Hidden;
            createSelectionCanvas();
        }

        /// <span class="code-SummaryComment"><summary>
</span>
        /// Popup form Confirm clicked, so save the file to their
        /// desired location
        /// <span class="code-SummaryComment"></summary>
</span>
        private void btnConfirm_Click(object sender, RoutedEventArgs e)
        {
            ImageUrl = tempFileName;
            grdCroppedImage.Visibility = Visibility.Hidden;
        }

        /// <span class="code-SummaryComment"><summary>
</span>
        /// creates the selection canvas, where user can draw
        /// selection rectangle
        /// <span class="code-SummaryComment"></summary>
</span>
        private void createSelectionCanvas()
        {
            createImageSource();
            selectCanvForImg.Width = bmpSource.Width * zoomFactor;
            selectCanvForImg.Height = bmpSource.Height * zoomFactor;
            selectCanvForImg.Children.Clear();
            selectCanvForImg.rubberBand = null;
            selectCanvForImg.Children.Add(img);
            svForImg.Width = selectCanvForImg.Width;
            svForImg.Height = selectCanvForImg.Height;
            svForImg.Content = selectCanvForImg;
            createSelectionCanvasMenu();
        }

        /// <span class="code-SummaryComment"><summary>
</span>
        /// Creates the selection canvas context menu
        /// <span class="code-SummaryComment"></summary>
</span>
        private void createSelectionCanvasMenu()
        {
            cmSelectionCanvas = new ContextMenu();
            MenuItem miZoom25 = new MenuItem();
            miZoom25.Header = "Zoom 25%";
            miZoom25.Tag = "0.25";
            MenuItem miZoom50 = new MenuItem();
            miZoom50.Header = "Zoom 50%";
            miZoom50.Tag = "0.5";
            MenuItem miZoom100 = new MenuItem();
            miZoom100.Header = "Zoom 100%";
            miZoom100.Tag = "1.0";
            cmSelectionCanvas.Items.Add(miZoom25);
            cmSelectionCanvas.Items.Add(miZoom50);
            cmSelectionCanvas.Items.Add(miZoom100);
            cmSelectionCanvasRoutedEventHandler =
            new RoutedEventHandler(MenuSelectionCanvasOnClick);
            cmSelectionCanvas.AddHandler
             (MenuItem.ClickEvent, cmSelectionCanvasRoutedEventHandler);
            selectCanvForImg.ContextMenu = cmSelectionCanvas;
        }

        /// <span class="code-SummaryComment"><summary>
</span>
        /// Handles the selection canvas context menu. Which will zoom the
        /// current image to either 25,50 or 100%
        /// <span class="code-SummaryComment"></summary>
</span>
        private void MenuSelectionCanvasOnClick(object sender,
                            RoutedEventArgs args)
        {
            MenuItem item = args.Source as MenuItem;
            zoomFactor = double.Parse(item.Tag.ToString());
            img.RenderTransform = new ScaleTransform
                    (zoomFactor, zoomFactor, 0.5, 0.5);
            selectCanvForImg.Width = bmpSource.Width * zoomFactor;
            selectCanvForImg.Height = bmpSource.Height * zoomFactor;
            svForImg.Width = selectCanvForImg.Width;
            svForImg.Height = selectCanvForImg.Height;

        }

        /// <span class="code-SummaryComment"><summary>
</span>
        /// Creates the Image source for the current canvas
        /// <span class="code-SummaryComment"></summary>
</span>
        private void createImageSource()
        {
            bmpSource = new BitmapImage(new Uri(ImgUrl));
            img = new System.Windows.Controls.Image();
            img.Source = bmpSource;
            //if there was a Zoom Factor applied
            img.RenderTransform = new ScaleTransform
                    (zoomFactor, zoomFactor, 0.5, 0.5);
        }

        /// <span class="code-SummaryComment"><summary>
</span>
        /// creates the drag canvas, where user can drag the
        /// selection rectangle
        /// <span class="code-SummaryComment"></summary>
</span>
        private void createDragCanvas()
        {
            dragCanvasForImg.Width = bmpSource.Width * zoomFactor;
            dragCanvasForImg.Height = bmpSource.Height * zoomFactor;
            svForImg.Width = dragCanvasForImg.Width;
            svForImg.Height = dragCanvasForImg.Height;
            createImageSource();
            createDragCanvasMenu();
            selectCanvForImg.Children.Remove(rubberBand);
            dragCanvasForImg.Children.Clear();
            dragCanvasForImg.Children.Add(img);
            dragCanvasForImg.Children.Add(rubberBand);
            svForImg.Content = dragCanvasForImg;
        }

        /// <span class="code-SummaryComment"><summary>
</span>
        /// Creates the drag canvas context menu
        /// <span class="code-SummaryComment"></summary>
</span>
        private void createDragCanvasMenu()
        {
            cmSelectionCanvas.RemoveHandler
              (MenuItem.ClickEvent, cmSelectionCanvasRoutedEventHandler);
            selectCanvForImg.ContextMenu = null;
            cmSelectionCanvas = null;
            cmDragCanvas = new ContextMenu();
            MenuItem miCancel = new MenuItem();
            miCancel.Header = "Cancel";
            MenuItem miSave = new MenuItem();
            miSave.Header = "Save";
            cmDragCanvas.Items.Add(miCancel);
            cmDragCanvas.Items.Add(miSave);
            cmDragCanvasRoutedEventHandler =
        new RoutedEventHandler(MenuDragCanvasOnClick);
            cmDragCanvas.AddHandler
               (MenuItem.ClickEvent, cmDragCanvasRoutedEventHandler);
            dragCanvasForImg.ContextMenu = cmDragCanvas;
        }

        /// <span class="code-SummaryComment"><summary>
</span>
        /// Handles the selection drag context menu.
        /// Which allows user to cancel or save
        /// the current cropped area
        /// <span class="code-SummaryComment"></summary>
</span>
        private void MenuDragCanvasOnClick
            (object sender, RoutedEventArgs args)
        {
            MenuItem item = args.Source as MenuItem;
            switch (item.Header.ToString())
            {
                case "Save":
                    SaveCroppedImage();
                    break;
                case "Cancel":
                    createSelectionCanvas();
                    break;
                default:
                    break;
            }
        }

        /// <span class="code-SummaryComment"><summary>
</span>
        /// Raised by the <span class="code-SummaryComment"><see cref="selectionCanvas">selectionCanvas</see>
</span>
        /// when the new crop shape (rectangle) has been drawn. This event
        /// then replaces the current selectionCanvas with a
                <see cref="DragCanvas">DragCanvas</see>
        /// which can then be used to drag the crop area around
        /// within a Canvas
        /// </summary>
        private void selectCanvForImg_CropImage
                (object sender, RoutedEventArgs e)
        {
            rubberBand = (Shape)selectCanvForImg.Children[1];
            createDragCanvas();
        }

        /// <summary>
        /// User cancelled out of the popup,
        /// so go back to showing original image
        /// </summary>
        private void lblExit_MouseDown
        (object sender, System.Windows.Input.MouseButtonEventArgs e)
        {
            grdCroppedImage.Visibility = Visibility.Hidden;
            createSelectionCanvas();
        }

        /// <summary>
        /// Saves the cropped image area to a temp file,
        /// and shows a confirmation
        /// popup from where the user may accept or reject the cropped image.
        /// If they accept the new cropped image
        /// will be used as the new image source
        /// for the current canvas, if they reject the crop,
        /// the existing image will
        /// be used for the current canvas
        /// </summary>
        private void SaveCroppedImage()
        {
            if (popUpImage.Source!=null)
                popUpImage.Source = null;

            try
            {
                rubberBandLeft = Canvas.GetLeft(rubberBand);
                rubberBandTop = Canvas.GetTop(rubberBand);
                //create a new .NET 2.0 bitmap (which allowing saving)
                //based on the bound bitmap URL
                using (System.Drawing.Bitmap source =
                    new System.Drawing.Bitmap(ImgUrl))
                {
                    //create a new .NET 2.0 bitmap (which allowing saving)
                    //to store cropped image in, should be
                    //same size as rubberBand element which is the size
                    //of the area of the original image we want to keep
                    using (System.Drawing.Bitmap target =
                         new System.Drawing.Bitmap((int)rubberBand.Width,
                         (int)rubberBand.Height))
                    {
                        //create a new destination rectangle
                        System.Drawing.RectangleF recDest =
                             new System.Drawing.RectangleF
                            (0.0f, 0.0f, (float)target.Width,
                            (float)target.Height);
                        //different resolution fix prior to cropping image
                        float hd = 1.0f / (target.HorizontalResolution /
                                            source.HorizontalResolution);
                        float vd = 1.0f / (target.VerticalResolution /
                                              source.VerticalResolution);
                        float hScale = 1.0f / (float)zoomFactor;
                        float vScale = 1.0f / (float)zoomFactor;
                        System.Drawing.RectangleF recSrc =
                            new System.Drawing.RectangleF
                            ((hd * (float)rubberBandLeft) *
                            hScale, (vd * (float)rubberBandTop) *
                            vScale, (hd * (float)rubberBand.Width) *
                            hScale, (vd * (float)rubberBand.Height) *
                            vScale);
                        using (System.Drawing.Graphics gfx =
                            System.Drawing.Graphics.FromImage(target))
                        {
                            gfx.DrawImage(source, recDest, recSrc,
                                System.Drawing.GraphicsUnit.Pixel);
                        }
                        //create a new temporary file, and delete
                        //all old ones prior to this new temp file
                        //This is a hack that I had to put in,
                        //due to GDI+ holding on to previous
                        //file handles used by the Bitmap.Save()
                        //method the last time this method was run.
                        //This is a well known issue see
                        //http://support.microsoft.com/?id=814675 for example
                        tempFileName = System.IO.Path.GetTempPath();
                        if (fixedTempIdx >

Extended Function 2 : Resizing source image

If you have a very large source image, you may want to revise it, so you can use the right click context menu (ONLY available while not in drag mode) which allows for 25, 50 and 100% sizing. Behind the scenes, all that is happening is that a System.Windows.Media.ScaleTransform is being applied. An example of this is as follows:

img.RenderTransform = new ScaleTransform(zoomFactor, zoomFactor, 0.5, 0.5)

How To Use It

Follow these steps:

  • Pick an image using the pick an image (top left) area
  • Scale using right click context menu (optional)
  • Draw a crop area using mouse (left button)
  • Move the crop area
  • Save or cancel using right click context menu
  • Start again

That's it

Although there is not that much code in this article, I had fun doing this one, and hope that it will be useful to someone out there.

So What Do You Think ?

I would just like to ask, if you liked the article please vote for it, and leave some comments, as it lets me know if the article was at the right level or not, and whether it contained what people need to know.

Conclusion

There's not too much to mention here as I think the rest of the article pretty much covers it. I suppose one thing to say would be that although I really love WPF, I found myself still needing to delve into .NET 2.0 to do some pixel level things. There is of course a System.Windows.Media.Imaging.CroppedBitmap within .NET 3.0, but this class did not offer the ability to save the image, so was not quite what I wanted. In terms of image filtering, WPF does actually offer some pixel level function by using the System.Windows.Media.Imaging.FormatConvertedBitmap class. But again not quite what I was after, as I wanted the cropping of my image to be persisted somewhere. So unless the .NET imaging classes were able to save to disk, I had to use the .NET Bitmap or Image classes instead.

History

  • v1.1 - 06/08/07 : Added Dependency Property for ucImageCropper, CropperStyle for the crop rectangle. Josh Smith suggested I do this.
  • v1.0 - 28/07/07 : Initial issue

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Sacha Barber
Software Developer (Senior)
United Kingdom United Kingdom
I currently hold the following qualifications (amongst others, I also studied Music Technology and Electronics, for my sins)
 
- MSc (Passed with distinctions), in Information Technology for E-Commerce
- BSc Hons (1st class) in Computer Science & Artificial Intelligence
 
Both of these at Sussex University UK.
 
Award(s)

I am lucky enough to have won a few awards for Zany Crazy code articles over the years

  • Microsoft C# MVP 2014
  • Codeproject MVP 2014
  • Microsoft C# MVP 2013
  • Codeproject MVP 2013
  • Microsoft C# MVP 2012
  • Codeproject MVP 2012
  • Microsoft C# MVP 2011
  • Codeproject MVP 2011
  • Microsoft C# MVP 2010
  • Codeproject MVP 2010
  • Microsoft C# MVP 2009
  • Codeproject MVP 2009
  • Microsoft C# MVP 2008
  • Codeproject MVP 2008
  • And numerous codeproject awards which you can see over at my blog

Comments and Discussions

 
GeneralMy vote of 5 PinmemberRenatoK7-Oct-14 21:36 
QuestionUcImageCropper_Unloaded Pinmemberkish_1234-Jul-13 0:08 
QuestionPasting Image in WPF PinmemberBob Ranck29-Nov-12 3:23 
AnswerRe: Pasting Image in WPF PinmvpSacha Barber29-Nov-12 3:26 
GeneralMy vote of 5 PinmemberDaveCS23-Jun-12 20:05 
QuestionZoom doesn't work PinmemberMilind R Chavan1-Jun-12 4:51 
GeneralMy vote of 5 Pinmembergyanendra mishra20-Sep-11 1:36 
GeneralMy vote of 2 PinmemberSimplyConfuzed26-May-10 14:17 
QuestionHow to programmatically crop the image PinmemberJohnny7925-Nov-09 10:14 
AnswerRe: How to programmatically crop the image PinmvpSacha Barber25-Nov-09 21:44 
QuestionHow to add rubber band when resize custom window? Pinmembertinatran308-Sep-09 16:35 
AnswerRe: How to add rubber band when resize custom window? PinmvpSacha Barber8-Sep-09 21:31 
GeneralRe: How to add rubber band when resize custom window? Pinmembertinatran309-Sep-09 8:13 
GeneralRe: How to add rubber band when resize custom window? PinmvpSacha Barber9-Sep-09 21:37 
GeneralRe: How to add rubber band when resize custom window? Pinmembertinatran3015-Sep-09 8:09 
GeneralCustom Control hotkey functionality PinmemberMember 367392230-Apr-09 3:25 
QuestionImage with Transparent color ...? PinmemberIamHuM30-Mar-09 17:03 
AnswerRe: Image with Transparent color ...? PinmvpSacha Barber30-Mar-09 21:34 
GeneralRe: Image with Transparent color ...? PinmemberIamHuM30-Mar-09 22:31 
GeneralRe: Image with Transparent color ...? PinmvpSacha Barber30-Mar-09 23:53 
GeneralRe: Image with Transparent color ...? PinmemberIamHuM31-Mar-09 3:12 
GeneralRe: Image with Transparent color ...? PinmvpSacha Barber31-Mar-09 4:26 
Generalcrop bitmap Pinmembersiten030822-Dec-08 11:51 
GeneralGreat job Mr Sacha PinmemberArunKumarShanmugam5-May-08 8:16 
GeneralRe: Great job Mr Sacha PinmvpSacha Barber5-May-08 8:41 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.141015.1 | Last Updated 6 Sep 2007
Article Copyright 2007 by Sacha Barber
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid