Managed C++: Rubber-Banding and Cropping Images

My previous article illustrated how to load, display, and dynamically resize images. This article demonstrates how to allow a user to select an area of an image with their mouse (sometimes called rubber-banding) and crop the image to that selected region.

Step-by-Step Instructions

Define the form-level members

First, define the variables that will be used to control how and where the rectangle is drawn. The cropRect defines the exact coordinates of the cropped image (the x and y coordinates as well as the width and height). Use the cropping boolean value to determine when the user has the mouse button down so that the user doesn't draw a rectangle when he or she simply is moving the mouse across the image. Finally, use the cropOriginX and cropOriginY to determine the original starting position (You'll see why these values need to be stored separately from the cropRect member shortly.):

public __gc class Form1 : public System::Windows::Forms::Form
  Rectangle cropRect;
  bool cropping;
  int cropOriginX, cropOriginY;


Implement an event handle for the PictureBox control's MouseDown event

The user pressing the mouse button constitutes the start of a new rectangle. The first thing to verify is that an image file has been loaded. The PictureBox control has a member called Image that represents the image being displayed, so the code can easily check for its existence in a boolean statement. If an image is being displayed, the method first calls the PictureBox::Refresh method to remove the previously drawn rectangle. If you're familiar with the famous MFC Scribble example application, you'll recall that to erase the rectangle, it simply drew the rectangle using the client area background color. However, that obviously won't work here because the program has drawn on an image (instead of a simple blank background). Therefore, the quickest and easiest way to remove the previous rectangle is by refreshing the image.

Once the image has been refreshed, the method needs only to initialize the variables that define where the rectangle is drawn and set a boolean flag to indicate that the user is currently cropping the image:

private: System::Void picImage_MouseDown(System::Object * sender,
                                         MouseEventArgs *  e)
  if (picImage->Image)

    cropOriginX     = e->X;;
    cropRect.X      = e->X;
    cropRect.Height = 0;

    cropOriginY    = e->Y;
    cropRect.Y     = e->Y;
    cropRect.Width = 0;

    cropping = true;

Implement an event handle for the PictureBox control's MouseMove event

As you might imagine, this is where most of the action occurs. The mousemove method first verifies that the user is cropping the image. If so, it refreshes the image (to erase any pre-existing rectangle), calls SetCroppingRectangle (to properly set up the rectangle to be drawn), and then calls the PictureBox::Invalidate method to cause the picture box to be repainted:

private: System::Void picImage_MouseMove(System::Object * sender,
                                         MouseEventArgs *  e)
  if (cropping)

    SetCroppingRectangle(e->X, e->Y);

The SetCroppingRectangle receives the coordinates where the mouse is on the picture box. The first thing the method does is make sure that these coordinates are not outside of the image's dimensions. If either the x or the y coordinate is outside the image's dimensions, the value is modified so that it represents the extreme in that direction. In other words, if an image is 20 (width) x 40 (height) and the user attempts to move the mouse to pixel position 41 on the x axis, then the value representing the x coordinate is set to 40. Likewise, the code ensures that the user can't move to a value below 0. These numbers are then massaged to ensure that the full rectangle can be viewed within the picture box. (I used a hard-coded value of 6, which worked for me when using a pen size of 3. However, you might want to play around with them to ensure perfect accuracy for your application.)

You would think that when the user moves the mouse you need only increment the Width and Height members of your cropRect object. However, when you draw a rectangle in .NET, it is drawn relative to an upper-left hand corner. This causes a problem if the user moves the mouse from right to left and/or from bottom to top. As a result, you need code to handle that situation such that the rectangle properly reflects not the direction the user moves the mouse, but the coordinates that are necessary for drawing the rectangle.

As an example, suppose the user starts moving the mouse from 5,5 and to 15,15. This is easy. The x and y members are each equal to 5 and the width and height are each equal to 10. In this situation, you simply leave the x and y alone and change the width and height to the current position (15,15) minus the starting position (5,5), which gives you a width and height of 10,10. However, if the user moves left and/or up, you need to set the starting position of the rectangle to the point where the user is now and then set the width and height equal to that original starting position (this is why you needed to define the cropOriginX and cropOriginY members) minus the current positions:

private: System::Void SetCroppingRectangle(int moveToX, int moveToY)
  // Account for user cropping outside of image dimensions
  moveToX = Math::Max(0, Math::Min(moveToX,picImage->Image->Width));
  moveToY = Math::Max(0, Math::Min(moveToY,picImage->Image->Height));

  // Make sure that rectangle is viewable within the picturebox
  // (Note: If the form is scrollable and the picturebox is set to
  // autosize, the rectangle can be drawn beyond the user's view.
  if (moveToX == 0) moveToX = 6;
  if (moveToX >= picImage->Image->Width)
    moveToX -= 6;
  if (moveToY == 0) moveToY = 6;
  if (moveToY >= picImage->Image->Height)
    moveToY -= 6;

  if (moveToX >= cropRect.X)    // moving right
    cropRect.Width = moveToX - cropOriginX;
  else                          // moving left
    cropRect.X = moveToX;
    cropRect.Width = cropOriginX - moveToX;

  if (moveToY >= cropRect.Y)    // moving down
    cropRect.Height = moveToY - cropOriginY;
  else                          // moving up
    cropRect.Y = moveToY;
    cropRect.Height = cropOriginY - moveToY;

Implement an event handle for the PictureBox control's MouseUp event

At this point, you have the rectangle to draw. All you need to do is turn off the cropping boolean flag and, in the case of the demo, enable the button that allows the user to invoke the cropping of the image. As you can see, I enable the button based on whether or not a rectangle is present:

private: System::Void picImage_MouseUp(System::Object * sender,
                                       MouseEventArgs *  e)
  cropping = false;
  btnCropImage->Enabled = (cropRect.Width > 0 &&
                           cropRect.Height > 0);

Implement a means of the user invoking the crop function

In the article demo, the user first draws the rectangle and then clicks a button labeled "Crop Image". The method first disables the crop button, and then it adjusts the rectangle to make sure that if the user has moused to the edge of the image, the edge is also cropped.

Once that's done, you're finally finished with the rectangle drawing and can crop the image. Do this by creating a Bitmap object that is the width and height of the user's drawn rectangle. Then, obtain a Graphics object for drawing on the bitmap. Next, use the Graphics::DrawImage method. This method allows you to specify a source image, destination coordinates, and source coordinates. As you can see, the source image is the image in the PictureBox control. The destination coordinates are defined by the cropRect object that has been used to draw the rectangle on the image, and the source coordinates start at 0 and 0 (top left-hand corner of the new image) with a width and height equal to that of the drawn rectangle. The PictureBox control's Image property is set to the newly created image and the rectangle is initialized:

private: System::Void btnCropImage_Click(System::Object * sender,
                                         System::EventArgs * e)
  if (picImage->Image
  && cropRect.Width > 0
  && cropRect.Height > 0)
    btnCropImage->Enabled = false;

    if (cropRect.X <= 6) cropRect.X = 0;
    if (cropRect.X + cropRect.Width >= picImage->Image->Width)
      cropRect.Width += 6;
    if (cropRect.Y <= 6) cropRect.Y = 0;
    if (cropRect.Y + cropRect.Height >=
      cropRect.Height += 6;

    Bitmap* croppedImage = new Bitmap(cropRect.Width,
    Graphics* g = Graphics::FromImage(croppedImage);
                 Rectangle(0, 0,

    picImage->Image = croppedImage;

    cropRect.X      = 0;
    cropRect.Width  = 0;
    cropRect.Y      = 0;
    cropRect.Height = 0;

Implement a Paint method for the PictureBox control

This is done so that if the user switches away from the application and then back again, the rectangle (if drawn) is always present. As you can see, you simply check to see if an image and valid rectangle coordinates are present. If they are, you create a pen and draw the rectangle:

private: System::Void picImage_Paint(System::Object * sender,
                                     PaintEventArgs * e)
  if (picImage-<Image
  && cropRect.Width > 0
  && cropRect.Height > 0)
    Pen* pen = new Pen(Brushes::Red, 3);
    Graphics* g = e->Graphics;
    g->DrawRectangle(pen, cropRect);

About the Author

Tom Archer - MSFT

I am a Program Manager and Content Strategist for the Microsoft MSDN Online team managing the Windows Vista and Visual C++ developer centers. Before being employed at Microsoft, I was awarded MVP status for the Visual C++ product. A 20+ year veteran of programming with various languages - C++, C, Assembler, RPG III/400, PL/I, etc. - I've also written many technical books (Inside C#, Extending MFC Applications with the .NET Framework, Visual C++.NET Bible, etc.) and 100+ online articles.



  • crops the area right of the selected rectangle

    Posted by sanjeet.uchil on 10/17/2008 06:16am

    i dnt knw why this is happening...on clicking crop button....image area right to the selected area gets cropped instead....please help...i have translated ur code into c#.

    • can you share your code?

      Posted by niketrk on 11/21/2008 02:11am

      Hi, Can you share your code here so other can try to help you? otherwise one good ready-made control, I found at . Though it is not free but it has got many features related to crop control. It is in form of activex so it is easy to integrate in application. It is simple but effective. anyway please share your code, so I can try to fix it.

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • The world of data storage is changing around us as organizations continue to seek ways by which they can leverage cloud-based services. However, simply shifting everything to the public cloud is generally neither desirable nor feasible, so organizations are struggling with finding ways that they can leverage the public cloud and its outcomes where it makes sense and leave everything else on-premises. The Gorilla Guide lays out the key questions and considerations you need to think through in building your …

  • Entire organizations suffer when their networks can't keep up and new opportunities are put on hold. Waiting on service providers isn't good business. In these examples, learn how to simplify network management so that your organization can better manage costs, adapt quickly to business demands, and seize market opportunities when they arise.

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date