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

WPF Interactive Image Cropping Control

By , 6 Sep 2007
 

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
{
    /// <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:

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
{

    /// <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 >

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

About the Author

Sacha Barber
Software Developer (Senior)
United Kingdom United Kingdom
Member
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 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

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionPasting Image in WPFmemberBob Ranck29 Nov '12 - 3:23 
Very Nice Work in this Cropping Control- Add another 5.
 
Sacha, have you looked into How to paste an image from the clipboard in WPF?
AnswerRe: Pasting Image in WPFmvpSacha Barber29 Nov '12 - 3:26 
Thanks.
 
You could try this guys paste image code : http://www.thomaslevesque.com/2009/02/05/wpf-paste-an-image-from-the-clipboard/
Sacha Barber
  • Microsoft Visual C# MVP 2008-2012
  • Codeproject MVP 2008-2012
Open Source Projects
Cinch SL/WPF MVVM

Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue
 
My Blog : sachabarber.net

GeneralMy vote of 5memberDaveCS23 Jun '12 - 20:05 
When I saw the presentation and detail of the article I was sure it was one of yours without seeing the author.
 
Your contributions are of great help and I am absolutely positive that not enough people thank you.
 
Thanks again for the great article
QuestionZoom doesn't workmemberMilind R Chavan1 Jun '12 - 4:51 
Hi Sacha,
 
Really great work, Thank you for posting such nice article.
 
I do have one question. When I select a image and trying to zoom in and out the image doesn't appear in it proper form/ doesn't retain it original form.
 
Thanks again.
GeneralMy vote of 5membergyanendra mishra20 Sep '11 - 1:36 
really great article.I have used your code in my scanning application but it is showing error when i wanna to call it again.any help.....its urgent.
GeneralMy vote of 2memberSimplyConfuzed26 May '10 - 14:17 
Lots of extra layers of unneeded ui in this code. Could be done with a simple canvas containing an image and a draggable rectangle. Then use CroppedBitmap, which is very saveable, btw. See http://drwpf.com/blog/2007/09/08/image-manipulation-using-wpf-imaging-classes/
QuestionHow to programmatically crop the imagememberJohnny7925 Nov '09 - 10:14 
Hi, I am facing a problem where the image is larger than the view control and I need to crop out the center of the image. How can that be done programmatically?
 
Thanks to any responses.
AnswerRe: How to programmatically crop the imagemvpSacha Barber25 Nov '09 - 21:44 
You can use a CroppedBitMap class in code, I do that over at my other article WPF : A Simple Color Picker With Preview[^]
 
Sacha Barber
  • Microsoft Visual C# MVP 2008/2009
  • Codeproject MVP 2008/2009
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue
 
My Blog : sachabarber.net

QuestionHow to add rubber band when resize custom window?membertinatran308 Sep '09 - 16:35 
Does anyone know how to draw a rubber band when resize custom window?
 
1. Able to resize my custom window via mouse drag(WindowStyle=None, AllowTransparent=True)
2. When resize, no need to preview because it's choppy because I have to recalculate height and width of the tile image assets used to fill the custom window.
 
3.Instead, I'd like to draw a rubber band with the new size that I want to resize to while mousedown and mousemove. Once mouseup, then I can shall refresh the custom window with the new size. This means I only refresh/recalculate my custom window once and avoid choppy.
 
My question is how to draw the rubber band that goes beyond the size of the window. Am I on the right track? Should I use DirectX instead?
AnswerRe: How to add rubber band when resize custom window?mvpSacha Barber8 Sep '09 - 21:31 
You can use an Adorner/AdornerLayer for this sort of thing
 
Sacha Barber
  • Microsoft Visual C# MVP 2008/2009
  • Codeproject MVP 2008/2009
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue
 
My Blog : sachabarber.net

GeneralRe: How to add rubber band when resize custom window?membertinatran309 Sep '09 - 8:13 
Hi Sacha,
 
Please give me a little more details on the approach. I have also tried WPF Diagram Designer: Part 1[^] but couldn't get what I need.
 
Thanks.
GeneralRe: How to add rubber band when resize custom window?mvpSacha Barber9 Sep '09 - 21:37 
i dont have time search google for adorners anf wpf and take it from there
 
Sacha Barber
  • Microsoft Visual C# MVP 2008/2009
  • Codeproject MVP 2008/2009
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue
 
My Blog : sachabarber.net

GeneralRe: How to add rubber band when resize custom window?membertinatran3015 Sep '09 - 8:09 
OK. Thanks Sacha.
GeneralCustom Control hotkey functionalitymemberMember 367392230 Apr '09 - 3:25 
Hi,
I have a requirement very similar to Word 2007 toolbar. In Word 2007, if user presses ALT key, then it show hotkey in the form of overlay. I have created custom control & want the same feature. I don't have any idea on that. Kindly provide me the suggestions or sample. That will really help.
 
Thnx,
Ritesh
QuestionImage with Transparent color ...?memberIamHuM30 Mar '09 - 17:03 
Hi...
 
First of all very good article...!!!
 
In my application i am using image controls inside grid. I want to set tranparent color property for that image. How i can do it...? In C# 2.0 i used to set TransparentColor property, what is its equivalent here in WPF...?
 

Once again thanks for the great article.
 
IamHuM
AnswerRe: Image with Transparent color ...?mvpSacha Barber30 Mar '09 - 21:34 
as far as I know you cant do that in XAML, you would have to get to the actual bits. ImageSource has the bits, which you can frig on a bit by bit basis. Bit XAML will not allow this
 
Sacha Barber
  • Microsoft Visual C# MVP 2008/2009
  • Codeproject MVP 2008/2009
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue
 
My Blog : sachabarber.net

GeneralRe: Image with Transparent color ...?memberIamHuM30 Mar '09 - 22:31 
hi Sacha,
 
Thanks for the reply. I am new in WPF can you provide me some sample code for this. I want to make white as transparent color.
 
Thanks.
IamHuM
GeneralRe: Image with Transparent color ...?mvpSacha Barber30 Mar '09 - 23:53 
Have a look at
http://jmorrill.hjtcentral.com/Home/tabid/428/EntryId/16/WPF-Hackery-Part-II.aspx
and
http://www.codeproject.com/KB/WPF/ImageCropper.aspx?msg=2986338#xx2986338xx
 
Sacha Barber
  • Microsoft Visual C# MVP 2008/2009
  • Codeproject MVP 2008/2009
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue
 
My Blog : sachabarber.net

GeneralRe: Image with Transparent color ...?memberIamHuM31 Mar '09 - 3:12 
hi,
 
Thanks, I wil check both links. One more doubt i have is in your FishEyePanel article if instead of image how i can use a button control and how i can give same background image to that button and handle the button click events for the same.
 
Thanks,
IamHuM
GeneralRe: Image with Transparent color ...?mvpSacha Barber31 Mar '09 - 4:26 
Thats Fisheye panel is not my article, you need to ask Paul Tallett, its his article.
 
Sacha Barber
  • Microsoft Visual C# MVP 2008/2009
  • Codeproject MVP 2008/2009
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue
 
My Blog : sachabarber.net

Generalcrop bitmapmembersiten030822 Dec '08 - 11:51 
Can anyone show/tell me how to crop bitmapimage?
GeneralGreat job Mr SachamemberArunKumarShanmugam5 May '08 - 8:16 
Great job man.. its really awesome.. really helpful..
GeneralRe: Great job Mr SachamvpSacha Barber5 May '08 - 8:41 
Thanks man
 
Sacha Barber
  • Microsoft Visual C# MVP 2008
  • Codeproject MVP 2008
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue
 
My Blog : sachabarber.net

GeneralRe: Great job Mr SachamemberArunKumarShanmugam5 May '08 - 9:42 
jus visited your blog... that was really cool. gud job man Smile | :)
QuestionVery cool... how would you constrain the crop dimensions?memberAshaman1 May '08 - 2:30 
I need to provide a way for some of my users to crop employee pics.
 
They must be 125 pixels wide by 160 pixels high.
 
How would I allow the user to draw as big a crop area as they want, but keep the rectangle constrained to that ratio?
 
Thanks so much.
AnswerRe: Very cool... how would you constrain the crop dimensions?mvpSacha Barber5 May '08 - 8:42 
Just use the mouse moves and dont allow them to process past a certain point
 
Sacha Barber
  • Microsoft Visual C# MVP 2008
  • Codeproject MVP 2008
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue
 
My Blog : sachabarber.net

GeneralRe: Very cool... how would you constrain the crop dimensions?memberAshaman7 May '08 - 2:51 
LOL. Well, you certainly answered my question.
 
I guess I should stop being lazy and figure it out for myself.
GeneralRe: Very cool... how would you constrain the crop dimensions?mvpSacha Barber7 May '08 - 5:21 
LOL, im sure youll figure it out
 
Sacha Barber
  • Microsoft Visual C# MVP 2008
  • Codeproject MVP 2008
Your best friend is you.
I'm my best friend too. We share the same views, and hardly ever argue
 
My Blog : sachabarber.net

General1.1 or 2.0memberMember 462883225 Mar '08 - 21:31 
nice work
how to make it working in .Net 1.1 or 2.0 .. even if started from scratch.???
thanx
GeneralRe: 1.1 or 2.0mvpSacha Barber25 Mar '08 - 23:42 
In .NET 1.1 or 2.0 you would go about it totally differently try this
 
http://www.codeproject.com/KB/miscctrl/TdhMarchingAnts.aspx
 
Sacha Barber
  • Microsoft Visual C# MVP 2008
  • Codeproject MVP 2008
Your best friend is you.
I'm my best friend too. We share the same view, and never argue
 
My Blog : sachabarber.net

Generalnot working...memberAlexandros547 Feb '08 - 20:06 
I try to open the project with VS2005 but a message pops up saying it cannot open it. Do you know why this might happen? I want to ask if there is any C++ cropping software because I am building a MFC image processing program in C++ and I don't know if your source code will work with mine. Thanx! Keep up the good work!
GeneralRe: not working...mvpSacha Barber7 Feb '08 - 20:41 
This will not work with C++/MFC full stop. WPF is not supported by C++.
 
Sacha Barber
  • Microsoft Visual C# MVP 2008
  • Codeproject MVP 2008
Your best friend is you.
I'm my best friend too. We share the same view, and never argue
 
My Blog : sachabarber.net

NewsYou are the manmemberKarl Shifflett14 Dec '07 - 14:24 
Sacha,
 
Super nice work. I need this functionality for a game I'm working on to allow the players to import their own pictures and customize the deck of cards with their faces!
 
Did you get the image business sorted out? The code from Mole's image processing is all .Net with calls to the Windows API. Very clean stuff.
 
Bes to you mate! Cool | :cool:
Cheers, Karl
 
Just a grain of sand on the worlds beaches.

GeneralAbout GDI+ problemmembercivanovici24 Sep '07 - 4:07 
I have the same problem when I tried to overide the picture, but this article help me:
 
http://blogs.msdn.com/llobo/archive/2007/03/08/bitmapsource-bitmap-interop.aspx

GeneralRe: About GDI+ problemmemberSacha Barber24 Sep '07 - 4:49 
great,
 
now this forum has some really cool image manipulation stuff for WPF.
 
Excellent
 
Sacha Barber
A Modern Geek - I cook, I clean, I drink, I Program. Modern or what?
 
My Blog : sachabarber.net

GeneralSaving a CroppedBitmap using encodersmemberAndyb197920 Sep '07 - 11:01 
Hi there, good article! Just a quick point about saving a CroppedBitmap without interop to .NET2
 
CroppedBitmap inherits BitmapSource , so it can be saved using encoders. e.g.
 
CroppedBitmap cb = new CroppedBitmap();
PngBitmapEncoder e = new PngBitmapEncoder(); // Also jpeg, bmp, tif encoders available
e.Frames.Add(BitmapFrame.Create(cb)); // pass BitmapSource to BitmapFrames.Create
e.Save(new FileStream(@"C:\test.png", FileMode.Create));

 
As for loading bitmapiamges, if you set the URl, .NET3 will lock the file until the BitmapImage gets disposed (which can be anytime)! This method helps unlock the file immediately by caching the bitmap to memory
 

BitmapImage bb = new BitmapImage();
bb.BeginInit();
bb.CacheOption = BitmapCacheOption.OnLoad; // Means the Bitmap is cached and file lock is released
bb.UriSource = new Uri(@"C:\myImage.jpg");
bb.EndInit();

 
Hope this is helpful Big Grin | :-D

GeneralRe: Saving a CroppedBitmap using encodersmemberSacha Barber20 Sep '07 - 21:08 
Andy
 
Excellent this is really really useful actually. Thanks for sharing it.
 
I will try and refactor code with this at some point and see if it helps me. Though im sure it will.
 
Thanks again
 

 
Sacha Barber
A Modern Geek - I cook, I clean, I drink, I Program. Modern or what?
 
My Blog : sachabarber.net

GeneralRe: Saving a CroppedBitmap using encodersmemberAndyb197920 Sep '07 - 22:01 
No Problems, we're all on a steep learning curve with this new undocumented API! Laugh | :laugh: In fact, it was your example I read first when I got started out, so happy to help Big Grin | :-D
 
Im also working on some image manipulation software, but for a commercial project, so cant share code. However, you may be interested to know about the following points:
 
BitmapSource is the object you want to be working with to manipulate bitmaps (Inherits ImageSource). The methods CopyPixels and Create also allow you to get an array of bytes for the target image, allowing almost direct manipulation of the bitmap and creation of a new bitmap (similar to GDI Bitmap.LockBits, but involves a copy operation in both directions). An example of how to get at the bytes of a bitmap can be found at xamlxaml.com[^].
 
Drawing on a bitmap (like GDI+ ImageGraphics.DrawRectangle) is possible using RenderTargetBitmaps (Inherits BitmapSource). Note that drawing to RenderTargetBitmaps is performed on the CPU side, but when you set a RTB to the source of an Image, it gets downloaded to the video card and rendered on hardware. An example of drawing on RTBs can be found in Chapter 31 of the Source[^] for Charles Petzold's book Applications = Code + Markup. RTBs are useful for intermediate operations that you dont want to display, such as blending two images in a custom manner.
 
Direct access to Bitmap data is also possible, through an extremely inventive (and no doubt dangerous!) hack found here[^]. This exposes a pointer to the unmanaged bitmap within a BitmapSource and saves you a copy operation in both directions if you want to do high-performance manipulation.
 
Resizing, Rotating or Transforming an image in memory is also possible outside of the render pipeline using encoders and decoders. Follow this example here[^].
 
Finally, to load an image directly to BitmapSource (without the intermediate BitmapImage), you can use decoders (similar in operation to encoders). These allow you to load an image from Stream, and also solves the problem of the .NET runtime locking the file (as Streams can be closed).
 
Hope this helps, thanks for your article and keep up the good work! Big Grin | :-D
GeneralRe: Saving a CroppedBitmap using encodersmemberSacha Barber20 Sep '07 - 22:35 
Again
 
Thanks for this, im sure this will be helpful to lots of folks.
 
I had read about RenderTargetBitmaps before (in that book also) and had seen and used BitmapSource.
 
Sadly all my XML/WPF articles are for fun, so they are limited in terms of how much time I give them all, perhaps if they were for commercial product and I was getting paid, I would spend more time on it.
 
But your posts here are 1st rate, and for that I (and im sure others will too) thanks a lot.
 
Im glad this article was well received by someone who obviously knows whats what.
 
Great
 
Sacha Barber
A Modern Geek - I cook, I clean, I drink, I Program. Modern or what?
 
My Blog : sachabarber.net

GeneralPublish in Worldofasp.netmemberHandy01212 Sep '07 - 2:23 
Hi,
 
Can I publish your article at my site http://www.worldofasp.net
I will put your copyright
GeneralRe: Publish in Worldofasp.netmemberSacha Barber12 Sep '07 - 2:27 
Yes you can.
 
I guess it will be under the .NET 3.0 category, you have there.
 
As its not ASP, as im sure you know.
 
Sacha Barber
A Modern Geek - I cook, I clean, I drink, I Program. Modern or what?
 
My Blog : sachabarber.net

QuestionOpen error!memberSergiu10 Sep '07 - 23:46 
I use VS 2005 and I can't open this projectFrown | :(
Error message: this project type is not supported by this installation.
Please help!
AnswerRe: Open error!memberSacha Barber11 Sep '07 - 6:11 
Its .NET 3.0. So unless you have WCF/WPF extensions for Visual Studio installed it wont work.
 
You must have .net 3.0 framework installed also
 
.NET 3.0 framework
 
The Visual Studio 2005 extensions
 
Then it will work
 
Sacha Barber
A Modern Geek - I cook, I clean, I drink, I Program. Modern or what?
 
My Blog : sachabarber.net

GeneralAmazingmemberandalmeida8 Sep '07 - 2:32 
Pretty cool, do you have more work on image processing?
 
Anderson J. Almeida
Systems Analyst
SimSysBr

GeneralRe: AmazingmemberSacha Barber8 Sep '07 - 20:49 
Thanks.
 
andalmeida wrote:
do you have more work on image processing?

 
This chap is the best in the business
 
http://www.codeproject.com/cs/media/Image_Processing_Lab.asp
 

I used this library for my Msc, its very cool and very easy to extend and use.
 

 
Sacha Barber
A Modern Geek - I cook, I clean, I drink, I Program. Modern or what?
 
My Blog : sachabarber.net

Generalgood jobmemberDr.Luiji5 Sep '07 - 0:37 
As usual, you have written a good article with a good documentation.
Your series is enlarged with (agiain) an excellent article,
Good job Sacha.
 

 

Dr.Luiji
 
Trust and you'll be trusted.
Cryptography API: The Next Generation (CNG) - How to crypt documents with C++ programming, here.

GeneralRe: good jobmemberSacha Barber5 Sep '07 - 1:14 
Thanks Dr Luiji.
 
Actually got one small change for this one to do tonight. But after that, many more to come.
 
Thanks for your support.
 
Smile | :)
 

 
Sacha Barber
A Modern Geek - I cook, I clean, I drink, I Program. Modern or what?
 
My Blog : sachabarber.net

GeneralRe: good jobmemberDr.Luiji5 Sep '07 - 2:18 
np
Sacha Barber wrote:
Actually got one small change for this one to do tonight. But after that, many more to come.

A little improvement can be a desktop cropper.
Sometime you may need to print a portion of your current desktop. To do this you need 4 steps: 1 the screenshot (print or alt-print button), 2 paste it into a program (like Paint), 3 select the portion of the image you need, 4 save it.
You can skip the first 2 steps, if your program runs in bg, with a shortcut you can skip the first 2 steps.
 

Dr.Luiji
 
Trust and you'll be trusted.
Cryptography API: The Next Generation (CNG) - How to crypt documents with C++ programming, here.

GeneralRe: good jobmemberSacha Barber5 Sep '07 - 3:50 
Dr.Luiji wrote:
A little improvement can be a desktop cropper.
Sometime you may need to print a portion of your current desktop. To do this you need 4 steps: 1 the screenshot (print or alt-print button), 2 paste it into a program (like Paint), 3 select the portion of the image you need, 4 save it.
You can skip the first 2 steps, if your program runs in bg, with a shortcut you can skip the first 2 steps.

 
Ah thats different tool. This is purely for XAML/WPF. Think of it as part of a XAML/WPF image editing suite.
 
Smile | :) Smile | :) Smile | :)
 
Sacha Barber
A Modern Geek - I cook, I clean, I drink, I Program. Modern or what?
 
My Blog : sachabarber.net

GeneralRe: good jobmemberDr.Luiji5 Sep '07 - 20:51 
Sacha Barber wrote:
Ah thats different tool. This is purely for XAML/WPF. Think of it as part of a XAML/WPF image editing suite.

 
Yeah, a possible future tool Rose | [Rose]
I'll wait your new articles.
bye.
 

Dr.Luiji
 
Trust and you'll be trusted.
Cryptography API: The Next Generation (CNG) - How to crypt documents with C++ programming, here.

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

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130516.1 | Last Updated 6 Sep 2007
Article Copyright 2007 by Sacha Barber
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid