Click here to Skip to main content
15,879,239 members
Articles / Multimedia / GDI+
Article

Click to Drag, Zoom, and Copy - PictureBox

Rate me:
Please Sign up or sign in to vote.
4.11/5 (17 votes)
23 Jan 20074 min read 148.3K   8.8K   108   16
A picture box with built-in click to drag, zoom, and copy.

Click and Drag to Zoom

Introduction

This control was originally developed to mimic some existing software that my company was using to view and process medical claims. The original plan was to develop a PictureBox that would allow the users to draw a box on the image and then zoom in on that point. It was immediately apparent that this would be no easy task.

Changes

The PictureBoxEx still uses .NET 1.1 to make it more compatible with current and future projects, but has been completely rewritten.

This control is a completely new control, it is no longer based on a PictureBox but a ScrollableControl. Making this switch was necessary to create a control that was both flexible and fast. When drawing the drag window, the old control was slow to build the image and refresh, whereas the new control is very smooth.

This new version also contains the ability to add annotations to the images. These annotations can be colored, resized to match the image's current zoom, and best of all are XmlSerializable, so they can be saved to file and retrieved later.

Scrolling was also added to this new control. The control now has the ability to be scrolled vertically and horizontally, by both the keys and the mouse. Scrolling the wheel causes the image to be scrolled vertically, but if you hold the control key while scrolling the mouse wheel, the image can now be scrolled horizontally. The arrow keys plus Home, End, PageUp, PageDown can all be used to scroll the image. Because the control is unable to receive and hold direct focus, the focus can shift from control to control on the same form. So to make use of the key scrolling, it is best to make this PictureBoxEx the only control on the form. (If anyone knows how to improve this limitation, I would love to hear about it.)

Also the current zoom of the control can be changed by scrolling the mouse wheel while holding the shift key down.

Features

  1. Click and drag window to zoom or copy
  2. Parentless design (New)
  3. Annotate (Includes custom cursor) (New)
  4. Diagnostics mode (New)
  5. Zoom and scroll with the mouse wheel (New)
  6. Scroll with keys (Works best if the only control on form) (New)

Custom Properties

Most of the controls custom properties are available through the designer.

  • AllowDrag: Indicates whether or not the user should be allowed to drag and copy or zoom
  • CurrentZoom: Controls the PictureBox's current zoom.
  • DefaultZoom: Controls the PictureBox's default zoom.
  • DiagnosticMode: Set to true to display diagnostics information.
  • DoubleClickRestore: Controls the PictureBox's ability to restore the image to the default zoom on double-click.
  • DragOptions: Controls the PictureBox's drag options. (Copy, Zoom, or Prompt)
  • DragWindowColor: Controls the PictureBox's drag window color.
  • DragWindowMinimum: Controls the PictureBox's minimum invokable drag window size.
  • DragWindowPattern: The dash pattern that is used by the control when drawing the drag window.
  • DrawMode: Controls the imaging filter that will be applied to the image if it is resized. (InterpolationMode)
  • MaximumZoom: Controls the maximum allowed zoom.
  • MinimumZoom: Controls the minimum allowed zoom.

The Code

The following code is used by GenerateResizedImage to generate the resized image from the backup which is created when the Image property of the control is set. The creating of the resized image happens regardless of the current zoom, even if it's 1. The reason it's generated anyway is because the control can't work with images of an indexed pixel format. So it's just easier to convert the images straight away.

C#
int resizedWidth  = Convert.ToInt32(backup.Width  * _currentZoom);
int resizedHeight = Convert.ToInt32(backup.Height * _currentZoom);
resized = new Bitmap(resizedWidth, resizedHeight);

// Drag the backup image onto the resized image
using (Graphics g = Graphics.FromImage(resized))
{
    g.InterpolationMode = (_currentZoom < 1F) ? 
               _drawMode : InterpolationMode.Default;

    Rectangle srceRect = 
       new Rectangle(0, 0, backup.Width, backup.Height);
    Rectangle destRect = 
       new Rectangle(0, 0, resized.Width, resized.Height);

    g.DrawImage(backup, destRect, srceRect, GraphicsUnit.Pixel);

    // Add any annotations to the resized image
    DrawAnnotations(g);
}

Determining where the zoom should occur requires a lot of math. The first step it to compensate for the current zoom and the current scroll position. Then, it needs to be determined which part of the drag window is larger in proportion to the size of the control, and enlarge that section to fill the control while centering the remainder.

C#
// The zoom window will never be proportional to the container, so one
//  side will get filled while the other gets centered. The larger % will
//  determine which gets filled, and which gets centered
float xRatio = (float)Width / (float)dragWindowSave.Width;
float yRatio = (float)Height / (float)dragWindowSave.Height;

float largerRatio;
int xAdjust = 0;
int yAdjust = 0;

largerRatio = (xRatio < yRatio) ? xRatio : yRatio;

// The cumulative zoom cannot exceed the maximum zoom;
if ((largerRatio * _currentZoom) > _maximumZoom)
    largerRatio = _maximumZoom / _currentZoom;

if (dragWindowSave.Width * largerRatio > Width)
    largerRatio = (float)Width / (float)dragWindowSave.Width;

if (dragWindowSave.Height * largerRatio > Height)
    largerRatio = (float)Height / (float)dragWindowSave.Height;

yAdjust = Convert.ToInt32(
    ((float)Height - (float)dragWindowSave.Height * largerRatio) / 2F);
xAdjust = Convert.ToInt32(
    ((float)Width - (float)dragWindowSave.Width * largerRatio) / 2F);

int xScrollPos = Math.Max(Convert.ToInt32(((float)-AutoScrollPosition.X + 
                         (float)dragWindowSave.X) * largerRatio) - xAdjust, 0);
int yScrollPos = Math.Max(Convert.ToInt32(((float)-AutoScrollPosition.Y + 
                         (float)dragWindowSave.Y) * largerRatio) - yAdjust, 0);
    
CurrentZoom *= largerRatio;

Using the Control

The control is very much like a PictureBox. All that really needs to be done is to add the control to a form and set its Image property.

  1. Add a reference to the Build DLL.
  2. Add the control to your form.
  3. Set the desired control properties.
  4. Set the control's Image property.

Lessons Learned

This control has an event that fires after the image has been changed. If subscribers of this event take a lot of time or processor power, it will appear to have a negative impact on the performance of this control.

Without a backup image, the zooming process (in and out) will degrade image quality.

Known Issues

  1. Some designer mode instability.
  2. Some issues with the border styles not being drawn correctly.

History

This control was developed for Trusted Plans Service Corp. Tacoma WA.

  • Version 1 - released 3/6/2006.
  • Version 2 - released 12/12/2006.

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


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralLicense Pin
Athmok21-Sep-06 3:39
Athmok21-Sep-06 3:39 

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

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