(Lviv community of .NET developers)

WPF Interactive Image Cropping Control

September 12, 2007 00:25 by realnero

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.


Advertisement
Public Folder Watcher (PFW) is an add-on for Microsoft Outlook enabling support of notifications about changes in Exchange Public Folders and providing a status of unread email messages.

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.

Collapse
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{    /// <summary>    /// Provides a Canvas where a rectangle will be drawn    /// that matches the selection area that the user drew    /// on the canvas using the mouse    /// </summary>    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        /// <summary>        /// Raised when the user has drawn a selection area        /// </summary>        public event RoutedEventHandler CropImage        {            add { AddHandler(this.CropImageEvent, value); }            remove { RemoveHandler(this.CropImageEvent, value); }        }        #endregion        #region Ctor        /// <summary>        /// Constructs a new SelectionCanvas, and registers the        /// CropImage event        /// </summary>        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        /// <summary>        /// Captures the mouse        /// </summary>        protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)        {            base.OnMouseLeftButtonDown(e);            if (!this.IsMouseCaptured)            {                mouseLeftDownPoint = e.GetPosition(this);                this.CaptureMouse();            }        }        /// <summary>        /// Releases the mouse, and raises the CropImage Event        /// </summary>        protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)        {            base.OnMouseLeftButtonUp(e);            if (this.IsMouseCaptured && rubberBand != null)            {                this.ReleaseMouseCapture();                RaiseEvent(new RoutedEventArgs(this.CropImageEvent, this));            }        }        /// <summary>        /// Creates a child control        /// <see cref="System.Windows.Shapes.Rectangle">Rectangle</see>        /// and adds it to this controls children collection        /// at the co-ordinates the user        /// drew with the mouse        /// </summary>        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:

Collapse
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#endregionusing System.Drawing;using System.Drawing.Drawing2D;//Josh Smith excellent DragCanvasusing WPF.JoshSmith.Controls;namespace ImageCropper{    /// <summary>    /// 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.    /// </summary>    public partial class UcImageCropper : System.Windows.Controls.UserControl    {        #region CropperStyle Dependancy property        /// <summary>        /// A DP for the Crop Rectangle Style        /// </summary>        public Style CropperStyle        {            get { return (Style)GetValue(CropperStyleProperty); }            set { SetValue(CropperStyleProperty, value); }        }        /// <summary>        /// register the DP        /// </summary>        public static readonly DependencyProperty CropperStyleProperty =            DependencyProperty.Register(            "CropperStyle",            typeof(Style),            typeof(UcImageCropper),            new UIPropertyMetadata(null, new PropertyChangedCallback                        (OnCropperStyleChanged)));        /// <summary>        /// The callback that actually changes the Style if one was provided        /// </summary>        /// <param name="depObj">UcImageCropper</param>        /// <param name="e">The event args</param>        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        /// <summary>        /// Deletes all occurrences of previous unused temp files from the        /// current temporary path        /// </summary>        /// <param name="tempPath">The temporary file path</param>        /// <param name="fixedTempName">The file name part to search for        /// </param>        /// <param name="CurrentFixedTempIdx">The current temp file suffix        /// </param>        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)            {            }        }        /// <summary>        /// Popup form Cancel clicked, so created the SelectionCanvas        /// to start again        /// </summary>        private void btnCancel_Click(object sender, RoutedEventArgs e)        {            grdCroppedImage.Visibility = Visibility.Hidden;            createSelectionCanvas();        }        /// <summary>        /// Popup form Confirm clicked, so save the file to their        /// desired location        /// </summary>        private void btnConfirm_Click(object sender, RoutedEventArgs e)        {            ImageUrl = tempFileName;            grdCroppedImage.Visibility = Visibility.Hidden;        }        /// <summary>        /// creates the selection canvas, where user can draw        /// selection rectangle        /// </summary>        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();        }        /// <summary>        /// Creates the selection canvas context menu        /// </summary>        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;        }        /// <summary>        /// Handles the selection canvas context menu. Which will zoom the        /// current image to either 25,50 or 100%        /// </summary>        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;        }        /// <summary>        /// Creates the Image source for the current canvas        /// </summary>        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);        }        /// <summary>        /// creates the drag canvas, where user can drag the        /// selection rectangle        /// </summary>        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;        }        /// <summary>        /// Creates the drag canvas context menu        /// </summary>        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;        }        /// <summary>        /// Handles the selection drag context menu.        /// Which allows user to cancel or save        /// the current cropped area        /// </summary>        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;            }        }        /// <summary>        /// Raised by the <see cref="selectionCanvas">selectionCanvas</see>        /// 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 > 2)                            fixedTempIdx = 0;                        else                            ++fixedTempIdx;                        //do the clean                        CleanUp(tempFileName, fixedTempName, fixedTempIdx);                        //Due to the so problem above, which believe you                        //I have tried and tried to resolve                        //I have tried the following to fix this,                        //incase anyone wants to try it                        //1. Tried reading the image as a stream of bytes                        //into a new bitmap image                        //2. I have tried to use the WPF BitmapImage.Create()                        //3. I have used the trick where you use a 2nd Bitmap                        //(GDI+) to be the newly saved image                        //                        //None of these worked so I was forced into using                        //a few temp files, and pointing the                        //cropped image to the last one, and making sure                        //all others were deleted.                        //Not ideal, so if anyone can fix it please                        //this I would love to know. So let me know                        tempFileName = tempFileName + fixedTempName +                            fixedTempIdx.ToString() + ".jpg";                        target.Save(tempFileName,                            System.Drawing.Imaging.ImageFormat.Jpeg);                        //rewire up context menu                        cmDragCanvas.RemoveHandler(MenuItem.ClickEvent,                                cmDragCanvasRoutedEventHandler);                        dragCanvasForImg.ContextMenu = null;                        cmDragCanvas = null;                        //create popup BitmapImage                        BitmapImage bmpPopup = new BitmapImage                        (new Uri(tempFileName));                        popUpImage.Source = bmpPopup;                        grdCroppedImage.Visibility = Visibility.Visible;                    }                }            }            catch (Exception ex)            {                MessageBox.Show(ex.Message);            }        }        #endregion    }}

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.

 

From http://www.codeproject.com/WPF/ImageCropper.asp

ImageCropper.zip (832,95 kb)


Currently rated 5.0 by 1 people

  • Currently 5/5 Stars.
  • 1
  • 2
  • 3
  • 4
  • 5

Categories: WinFx
Actions: Permalink | Comments (69) | RSSRSS comment feed

Comments

October 27. 2009 23:43

Nice resource. rss feed added

easy cash loans

November 20. 2009 03:07

Nice post and very helpfull

cash loan

December 2. 2009 17:35

Thank you for your help!

fast payday loans

December 6. 2009 23:25

Hmmm interesting stuff

payday loans

December 29. 2009 23:25

Just wanted to say thanks for this.

TN Payday Loans

January 15. 2010 11:01

Discipline is the bridge between goals and accomplishment.

online payday loans

January 17. 2010 20:29

A business that makes nothing but money is a poor kind of business.

cash advance

January 24. 2010 23:06

I really apreciate yourposting it's realy informated for me hope you always update more often and share to us what you know. Thanks

cash advance

February 9. 2010 00:10

I don't like your template but your posts are quite good so I will check back!

easy payday loans

February 19. 2010 04:19

I really apreciate yourposting it's realy informated for me hope you always update more often and share to us what you know. Thanks

auto insurance

February 26. 2010 02:15

In searching for sites related to web hosting and specifically comparison hosting linux plan web, your site came up.You are a very smart person!

pay day loans

February 27. 2010 15:49

I just expect to have understood this the way it was destined

cash til payday loan

February 27. 2010 20:38

Hi Webmaster, commenters and everybody else !!! The blog was absolutely fantastic! Lots of great information and inspiration, both of which we all need!Keep 'em coming... you all do such a great job at such Concepts... can't tell you how much I, for one appreciate all you do!

cash advance

March 1. 2010 16:13

This article gives the light in which we can observe the reality. this is very nice one and gives indepth information. thanks for this nice article!

Acne products

March 6. 2010 17:01

my God, i thought you were going to chip in with some decisive insght at the end there, not leave it with ‘we leave it to you to decide’. Big thanks for the useful info i found on WPF Interactive Image Cropping Control.

cash payday loan

March 7. 2010 14:46

You made some good points there. I searched this topic and found out that most people will agree with your blog.

Herbal colon cleanse

March 8. 2010 02:28

Super-Duper site! I am loving it!! Will come back again - taking you feeds also. Big thanks for the useful info i found on WPF Interactive Image Cropping Control.

teeth whitening products

March 10. 2010 12:16

Hello, I found your blog in a new directory of blogs. I dont know how your blog came up, must have been a typo, Your blog looks good. Have a nice day. Big thanks for the useful info i found on WPF Interactive Image Cropping Control.

payday cash loans

March 14. 2010 13:31

Great blog, keeping me from working but who's complaining?

Home Steam Cleaners

March 15. 2010 14:06

This is exactly what i was looking for. thank you for the informative post and keep up the good work!

Stretch mark removal

March 22. 2010 17:26

Found your site on del.icio.us today and really liked it.. i bookmarked it and will be back to check it out some more later .. Big thanks for the useful info i found on WPF Interactive Image Cropping Control.

nail fungus treatments

March 24. 2010 17:28

I usually dont post in Blogs but your blog forced me to, amazing work.. beautiful

car loan with bad credit

April 1. 2010 23:30

Resources like the one you mentioned here will be very useful to me! I will post a link to this page on my blog. I am sure my visitors will find that very useful. Big thanks for the useful info i found on WPF Interactive Image Cropping Control.

genital wart removal

April 2. 2010 15:21

I just stumbled on your blog when i was lookng on google. I have to say that the information here was the most complet that I found anywhere. I am definitly bookmarkin this to come bck and read later

Mens Kegel exercises

April 6. 2010 08:40

Very interesting post - Might be old new, but it was new to me.

credit card debt management

April 9. 2010 02:44

Be glad of life because it gives you the chance to love, to work, to play, and to look up at the stars.

digestit

April 12. 2010 10:00

Of course, what a great site and advisory posts, Can I add backlink - import your rss feed? Big thanks for the useful info i found on WPF Interactive Image Cropping Control.

us

April 17. 2010 03:18

Success is simple. Do what's right, the right way, at the right time.

cash advance payday loans

April 21. 2010 23:28

Kinda got hooked on this blog and so i will be bookmarking this article for easy access

Leah Fulk

April 22. 2010 03:43

He knows not his own strength that hath not met adversity.

faxless cash advance

April 23. 2010 16:39

Interesting post :)
Sensible to determine more people writing concerning development

pictures of acne

April 27. 2010 08:38

Valuable information and excellent design you got here! I would like to thank you for sharing your thoughts and time into the stuff you post!! Thumbs up!

payday loans

May 3. 2010 14:53

As a Newbie, I am always searching online for articles that can help me. Thank you

easy personal loans

May 5. 2010 07:16

you must be aware that you have a really nice blog

easy reverse cell phone lookup service

May 9. 2010 08:12

I don't like your template but your posts are quite good so I will check back!

paydayloans

May 13. 2010 06:32

Valuable information and excellent design you got here! I would like to thank you for sharing your thoughts and time into the stuff you post!! Thumbs up!

faxless cash advances

May 18. 2010 00:42

great layout... i really like your site

vigrx plus

May 20. 2010 19:29

great layout... i really like your site

vigrx plus

May 23. 2010 21:25

I usually dont post in Blogs but your blog forced me to, amazing work.. beautiful

fast cash personal loans

May 27. 2010 08:04

a good article couldn't be written without a good topic, i think you should wrtie some new things on your blogs. things like,<A

title="ghd hair straightners" href="http://www.bestghdstraighteners.com/"; ><B>ghd hair straightners</B></A> as well as&nbsp;

ghd straightener

June 2. 2010 14:29

nice software for image editing... maybe sometimes i will to download that application... and trying.

Frequency Doubler

June 8. 2010 08:59

I'm so happy that I find this blog, I get any info what I want on this subject!

vibram five fingers

June 12. 2010 11:40

I have read your article, I think this is very good! Simple language, concise blog! Another kind of style! I like!http://www.nikeairjordan.cc

Jordans 2

June 14. 2010 10:20

I think, that's more comfortable to purchase the essay thesis at comparison essay service, just because it will save free time.

thomas sabo charm

June 19. 2010 13:52

Nice information, I enjoy to write opinions since the comments brings writers to remain more engaged and for the chance to probably study from each other.

Jordan retro 10

June 24. 2010 01:09

Thanks for sharing your article. As a sports fan I adore all kinds of sports equipments. I've got <a href="http://www.hijordan.us/";>michael jordan shoes</a>、 <a href="http://www.vibramfivefingerssale.us/";>vibram five fingers</a> and<a href="http://www.p90xworkoutdvdset.com/";> p90x sale</a>. I think <a href="http://www.cheap-ed-hardy.com/";>ed hardy</a> are the coolest t-shirts in the world!

p90x sale

June 26. 2010 10:22

I have been hunting for answers to a bunch of questions I have on a related subject but I have not been able to unearth precisely what I'm searching for. I suppose the search will go on.

Lowest home insurance rates

July 1. 2010 21:52

I'm very thankful to the author for posting such an amazing development post. Continuing to the post, This is a fantastic survey, very nice write up,Thanks you for sharing.

Rerto Jordans

July 2. 2010 23:08

I'm reading this post from my I-Phone and it looks great! I like your writing style and you make some excellent points about development.

burty

July 5. 2010 17:52

Trubloods.com could be a net site dedicated to be able to giving you all the goodness and also night with the strike display True Blood. True Blood is actually named because the manufactured blood the japoneses have produced and also today vampires regarding the actual underworld arise from the night to be able to try since well as coexist along with human beings. An individual follow Sookie Stackhouse because your woman offers with the particular disarray that will be out there in the world. There's ambiance, comedy, motion, as well as several a lot more as you observe pumpkin heads, human beings and also supernaturals collide within the town regarding Bon Temperatures, Louisiana. Observe free of charge of impose streams at trubloods.com!

japanese

July 6. 2010 05:33

The Big 10 Football Nation Forum can be a web site where it is possible to examine just about something concerning the NCAA Big 10 Football Conference. You could also discuss about all sorts of other things like other NCAA Football Conferences, other sports activities, every day chit chat, and some other topics. There can also be a special VIP Section in which you could acquire, sell, trade, or have sports activities bets with other members. http://www.big10footballnation.net/forums/forum.php

big 10 football nation forum

July 6. 2010 06:18

Trubloods.com is any net site specialized in bringing you all the amazing benefits since well as night along with the strike show True Blood. True Blood is known as as the actual synthetic bloodstream that the actual western possess created and also these days vampires associated with the actual underworld emerge by means of the particular darkness in order to try because well since exist together with persons. You stick to Sookie Stackhouse since your woman deals applying the actual chaos which is out there on earth. There is certainly ambiance, humor, actions, and numerous more when you watch pumpkin heads, humans and supernaturals clash inside the community regarding Bon Temperatures, Louisiana. Watch no cost of impose channels at trubloods.com!

supernatural

July 7. 2010 07:40

I totally agree.

Peer

July 7. 2010 07:41

One more good post, continually awesome to go through a fantastic post over a subject you seriously care about.

Peek

July 7. 2010 18:43

Couldn't agree more.

PeeQ

July 7. 2010 18:44

One more outstanding post, usually awesome to go through a superb post over a topic you seriously care about.

Mira Jones

July 8. 2010 13:28

Everything in this blog is completely true! In addition you've created an excellent blog post once again! Your writing style about development is impeccable, I really enjoy the articles. I check your site regularly and enjoy the fact its popular and has frequent users, please post about development more often.

rebecca

July 8. 2010 23:44

What a super development blog!

Ryan

July 9. 2010 21:41

This is twice now that i’ve landed on your site in the last 2 days while searching yahoo for absolutely unrelated things. Kinda funny. Keep up the good work!

oil painting

July 12. 2010 06:57

fact its popular and has frequent users, please post about development more often.

payday loans

July 13. 2010 19:40

I propose not to hold off until you get enough cash to buy different goods! You should take the <a href="http://bestfinance-blog.com/topics/business-loans";>business loans</a> or car loan and feel yourself comfortable

SnyderErma

July 15. 2010 00:11

I don't like your template but your posts are quite good so I will check back!

vibram fivefingers

July 16. 2010 20:31

Soon just after looking close to for a legitimate hotmail password hack computer software, I determined this tool. Be cautious with it while, I am not confident if in anyway legal to benefit from it to hack someones hotmail or yahoo account password, its only legal to set it to make use of all on your own if you forget the password. http://www.hotmailpasswordhack.net

hotmail password hack

July 19. 2010 07:27

This is a really good read for me, Must admit that you are one of the best bloggers I ever saw.Thanks for posting this informative article.

AC Millan Football Shirts

July 24. 2010 03:06

Thank you for sharing this article. So excellent.Welcome to our website,we can exchange our idea.Thank you!

Nike Shox Deliver

July 29. 2010 03:51

Thanks a good deal! I truly enjoyed reading this.Looking through these posts and the information you've provided I can appreciate that I still have a lot of things to learn. I will keep reading and keep re-visiting.
http://www.guccionlineoutlet.com/

Gucci handbags

July 29. 2010 03:55

this site is really great.. nice layout

vigrx

July 29. 2010 10:02

Thanks so much for posting these, they are fantastic! So bummed I missed what looks like a truly unique evening.
http://www.nikeairjordan.cc/air-jordan-6-vi-20/

jordans 6

July 29. 2010 10:16

Your blog impress me with your article. Thanks for sharing. Welcome to our website to see all kinds of shoes. http://www.sneakers4sales.com/nike-shox-men-88/

Nike Shox

Add comment


(Will show your Gravatar icon)