Click here to Skip to main content
15,887,135 members
Articles / Desktop Programming / Windows Forms
Article

ImageBox Control with Zoom/Pan Capability

Rate me:
Please Sign up or sign in to vote.
4.43/5 (18 votes)
6 Dec 20053 min read 216.4K   11.2K   79   32
How to create a simple control that extends the capability of an ImageBox by adding zoom and pan (scroll).

Image 1

Introduction

This control extends the capability of an ImageBox by including scrollbars to pan the image and a method for zooming the size of the image. My goal here is to show you two things:

  1. How to create your own controls that extend the System.Windows.Forms controls.
  2. How to add zoom/pan capability to an image display in a simple fashion.

Creating the control

Using Microsoft Visual Studio .NET, the easiest way to create a control is to begin by right-clicking on the project and selecting "Add -> Add User Control".

The Zoom control was created by adding a GroupBox, TrackBar, and three Labels for the minimum zoom (25%), center zoom (100%) and maximum zoom (300%). I set the Anchor property of the GroupBox and TrackBar to "Right, Top, Left" so that resizing the window will resize the width of these controls. To keep the 100% Label aligned to the center of the GroupBox, I set the Anchor property to "Top".

The Image control with automatic scroll bars was created by dropping a Panel onto the control and sizing it to fill the remaining section of the control (below the Zoom control) and setting its Anchor property to "Left, Top, Right, Bottom" so that it will resize with the control. Set the AutoScroll property to "true". Finally, I dropped an ImageBox inside the panel with Location = 0,0 and SizeMode=StretchImage.

The properties must be set with AutoScroll=true and SizeMode=StretchImage in order for the zoom and scroll bars to work properly.

C#
// zoom controls
private System.Windows.Forms.GroupBox groupBox1;
private System.Windows.Forms.TrackBar scrollZoom;
private System.Windows.Forms.Label lblMax;
private System.Windows.Forms.Label lblMin;
private System.Windows.Forms.Label lblCenter;    
// image controls
private System.Windows.Forms.Panel imagePanel;
private System.Windows.Forms.PictureBox imgBox;

Developing the code

At this point it becomes very simple. By placing the ImageBox inside a Panel with AutoScroll=true, the Panel will automatically add scrollbars when the ImageBox size exceeds the size of the Panel. So, all you have to do is to add code to get or set the image and a little bit of code to control the zoom.

The image is set by adding a public property. In this case, I chose to make the property available at design time by setting Browsable(true). I also re-center the zoom scroll when a new image is loaded and disable the zoom scroll if the image is null. Finally, I set the size of the ImageBox equal to the size of the Image for a zoom factor of 100%.

As mentioned in the comments below by yfoulon, adding scrollZoom.Focus() should allow the use of mousewheel to zoom the image. (I don't have a mouse so I was unable to test this.)

C#
[Browsable(true),
Description("Image loaded into the box.")]
public Image Image
{
    get
    {
        return imgBox.Image;
    }
    set
    {
        // Set the image value
        imgBox.Image = value;

        // enable the zoom control if this is not a null image
        scrollZoom.Enabled = (value != null);

        if (scrollZoom.Enabled)
        {
            // reset zoom control
            scrollZoom.Value = this.scrollZoom.Maximum/2;

            // Initially, the zoom factor is 100% so set the
            // ImageBox size equal to the Image size.
            imgBox.Size = value.Size;
        }
        else
        {
            // If null image, then reset the imgBox size
            // to the size of the panel so that there are no
            // scroll bars.
            imgBox.Size = imagePanel.Size;
        }
    }
}

The zoom is handled with an EventHandler that calls a method when the user scrolls the zoom TrackBar. The zoom factor is currently a hard-coded array with 11 elements which is the same as the number of positions on the TrackBar (min = 0, center = 5, max = 10). The ImageBox is then resized by multiplying the Image size by the new zoom factor. Because the ImageBox's SizeMode is set to "StretchImage", the Image will be scaled to fit the new size of the ImageBox.

C#
private double[] zoomFactor = 
   {.25, .33, .50, .66, .80, 1, 1.25, 1.5, 2.0, 2.5, 3.0}; 

private void scrollZoom_Scroll(object sender, 
                                System.EventArgs e)
{
    setZoom();
}

private void setZoom()
{
    // The scrollZoom changed so reset the zoom factor
    // based on the scrollZoom TrackBar position.
    double newZoom = zoomFactor[scrollZoom.Value];

    // Set the ImageBox width and height to the new zoom
    // factor by multiplying the Image inside the Imagebox
    // by the new zoom factor.
    imgBox.Width = 
             Convert.ToInt32 ( imgBox.Image.Width * newZoom); 
    imgBox.Height = 
             Convert.ToInt32 ( imgBox.Image.Height * newZoom );
}

Additionally, I also added a KeyDown event handler and some code to allow the user to increase or decrease the zoom factor using the Ctrl+ and Ctrl- keys.

C#
private void ImageBoxPanZoom_KeyDown(object sender, KeyEventArgs e)
{
    // Was the key combination that was pressed Ctrl+ or Ctrl-?
    // If so, then change the zoom level (but only if the zoom
    // is enabled)
    if (scrollZoom.Enabled) 
    {
        // Note: The e.KeyData is the combination of all the
        // keys currently pressed down. To find out if this is
        // the Ctrl key *and* the + key, you "or" the Keys 
        // together. This is a bitwise "or" rather than the 
        // || symbol used for boolean logic.

        if((e.KeyData == (Keys.Oemplus | Keys.Control)) &&
            (scrollZoom.Value != scrollZoom.Maximum))
        {
            scrollZoom.Value++;
            setZoom();
        }
        else if ((e.KeyData == (Keys.OemMinus | Keys.Control)) &&
            (scrollZoom.Value != scrollZoom.Minimum))
        {
            scrollZoom.Value--;
            setZoom();
        }
    }
}

Points of interest

At some point, I would like to revisit this project and add a method of scrolling the image by using the arrow keys and/or the mouse.

History

  • 12/01/05 - Thanks to yfoulon for correcting a mistake I made in the original article. It's even simpler than I thought! :)

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
Web Developer
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

 
QuestionLicense? Pin
ByteBean4-Mar-15 4:43
ByteBean4-Mar-15 4:43 
Generalregarding this article Pin
himanshi tyagi2-Apr-12 23:14
himanshi tyagi2-Apr-12 23:14 
GeneralMy vote of 5 Pin
Manoj Kumar Choubey28-Feb-12 22:42
professionalManoj Kumar Choubey28-Feb-12 22:42 
GeneralScrolling Capabilities Pin
GregWilkerson26-May-09 8:25
GregWilkerson26-May-09 8:25 
Cool | :cool:

First, props for this. This is pretty cool. I did manage to write something to allow scrolling with arrow keys. It's not 100% perfect, but it is pretty close to right. I'm a DBA more than a coder, so if I've done something stupid in the code, let me know.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace Application
{
	/// <summary>
	/// Image Viewer
	/// 
	/// This application displays image files.  
	/// 
	/// This biggest trick involved
	/// with this is the creation of a zero sized text box that is used to
	/// capture keystrokes.  It may be possible to set the focus always to
	/// the form, but this was not tried.  The form is configured so that the
	/// only control able to receive focus is this text box.  In this way, 
	/// keystrokes can be captured within the key down event.
	/// 

	public partial class ImageViewer : Form
	{
		// the 100% zoom values is at array index 7
		private const int FullZoomIndex = 7;

		private int originalImageHeight;
		private int originalImageWidth;
		private int zoomIndex;
		private int hScrollIncrement;
		private int vScrollIncrement;
		private int hScrollPosition;
		private int vScrollPosition;
		private int hCenter;
		private int vCenter;
		private Point imageCenter;
		private string imageFile;

		// define the available zoom levels
		private double[] zoomFactor = { 0.10, 0.15, 0.20, 0.25, 0.33, 0.50, 0.75, 1.00, 1.25, 1.50 };

		// this method seemed to work best when telling the panel to scroll.
		[DllImport("user32.dll")]
		static extern int SendMessage(IntPtr hWnd, int wMsg, int wParam, int lParam);

		public ImageViewer()
		{
			InitializeComponent();
		}

		public ImageViewer(string fileName)
		{
			InitializeComponent();

			// strip out the name of the file for display in the window title.
			string[] s = fileName.Split(new char[] { '\\', '.' });
			imageFile = s[s.Length - 2];

			// Hook into the focus event of the text box used to
			// capture kestrokes.
			KeystrokeCaptureTextBox.GotFocus += new EventHandler(KeystrokeCaptureTextBox_GotFocus);

			// Load the image file
			ImagePictureBox.Load(fileName);

			// save the original size of the image.  These values are used to 
			// calculate the zoomed image size.
			originalImageHeight = ImagePictureBox.Image.Height - 1;
			originalImageWidth = ImagePictureBox.Image.Width - 1;

			// initialize the horizontal and vertical scroll position values.
			hScrollPosition = 0;
			vScrollPosition = 0;

			// set the scroll increment.  In this case 1% of the original image size.
			hScrollIncrement = ImagePictureBox.Image.Width / 100;
			vScrollIncrement = ImagePictureBox.Image.Height / 100;
			ScrollPanel.HorizontalScroll.SmallChange = hScrollIncrement;
			ScrollPanel.VerticalScroll.SmallChange = vScrollIncrement;

			// set the size of the picture box.  Initially this will be at 100% zoom
			ImagePictureBox.Width = originalImageWidth;
			ImagePictureBox.Height = originalImageHeight;

			// calculate the image center.  This is used during the zoom processes.
			// In the case of this code, these values could be constant.  But, in
			// the interest of flexibility, they are calculated.
			imageCenter = CalculateImageCenter();

			// set the initial zoom to 100%
			zoomIndex = FullZoomIndex;

			// set the title text of the image display form.
			SetWindowTitle();
		}

		private Point CalculateImageCenter()
		{
			int x, y;

			x = (int)(ScrollPanel.Width / 2 + 0.5);
			y = (int)(ScrollPanel.Height / 2 + 0.5);

			return new Point(x, y);
		}

		private void SetWindowTitle()
		{
			// show the file name and zoom factor in the window title.
			this.Text = imageFile + " : " + "Zoom " + (zoomFactor[zoomIndex] * 100).ToString() + "%";
		}

		#region Zoom and Scroll

		private void ScrollRight()
		{
			// Tell the panel to scroll right
			SendMessage(
				ScrollPanel.Handle, 
				WindowsMessages.WM_HSCROLL, 
				WindowsMessages.SB_LINERIGHT, 
				0);
		}

		private void ScrollLeft()
		{
			// Tell the panel to scroll left
			SendMessage(
				ScrollPanel.Handle, 
				WindowsMessages.WM_HSCROLL, 
				WindowsMessages.SB_LINELEFT, 
				0); 
		}

		private void ScrollUp()
		{
			// Tell the panel to scroll up
			SendMessage(
				ScrollPanel.Handle,
				WindowsMessages.WM_VSCROLL,
				WindowsMessages.SB_LINEUP,
				0);
		}

		private void ScrollDown()
		{
			// Tell the panel to scroll down
			SendMessage(
				ScrollPanel.Handle,
				WindowsMessages.WM_VSCROLL,
				WindowsMessages.SB_LINEDOWN,
				0);
		}

		private void Zoom(string InOut)
		{
			// The center position calculation (not scroll bar position) is as follows:
			// newCenterX = newWidth * oldCenterX / oldWidth
			// newCenterY = newHeight * oldCenterY / oldHeight
			// these values need to have the scroll panel height and width subtracted from them

			// Calculate the center of the displayed image.
			Point p = CalculateImageCenter();

			// Get the coordinates of the center of the image.  
			// This is the old position.
			int oldCenterX = hCenter;
			int oldCenterY = vCenter;

			// get the current image size.
			int oldImageWidth = ImagePictureBox.Width;
			int oldImageHeight = ImagePictureBox.Height;

			// move the the previous zoom factor, if not zoomed all the way in.
			if (InOut == "In") 
			{
				if (zoomIndex < zoomFactor.Length - 1) zoomIndex++;
			}

			// move the the previous zoom factor, if not zoomed all the way out.
			if (InOut == "Out") 
			{
				if (zoomIndex > 0) zoomIndex--;
			}

			// calculate the new image size in accordance with the zoom factor.
			int newImageWidth = (int)(originalImageWidth * zoomFactor[zoomIndex] + 0.5);
			int newImageHeight = (int)(originalImageHeight * zoomFactor[zoomIndex] + 0.5);

			// set the picture box dimensions 
			// The picture box is set to stretch the image to fit the size of the box.
			// Altering those dimensions has the effect of zooming.
			ImagePictureBox.Width = newImageWidth;
			ImagePictureBox.Height = newImageHeight;

			// calculate and save the new center position
			hCenter = (int)(newImageWidth * oldCenterX / oldImageWidth + 0.5);
			vCenter = (int)(newImageHeight * oldCenterY / oldImageHeight + 0.5);
	
			// reset the scroll position and adjust for center
			int scrollX = hCenter - p.X;
			int scrollY = vCenter - p.Y;

			// A negative scroll position generates an error.  
			// So, set it to zero if it is negative.
			if (scrollX < 0) scrollX = 0;
			if (scrollY < 0) scrollY = 0;

			hScrollPosition = scrollX;
			vScrollPosition = scrollY;

			ScrollPanel.HorizontalScroll.Value = scrollX;
			ScrollPanel.VerticalScroll.Value = scrollY;

			SetWindowTitle();

		}

		#endregion

		#region Event Handlers

		private void KeystrokeCaptureTextBox_KeyDown(object sender, KeyEventArgs e)
		{
			if (e.KeyCode == Keys.Left) ScrollLeft();
			if (e.KeyCode == Keys.Right) ScrollRight();
			if (e.KeyCode == Keys.Up) ScrollUp();
			if (e.KeyCode == Keys.Down) ScrollDown();

			if (e.KeyCode == Keys.Add) Zoom("In");
			if (e.KeyCode == Keys.Subtract) Zoom("Out");

			if (e.KeyCode == Keys.Left | e.KeyCode == Keys.Right | 
				e.KeyCode == Keys.Up | e.KeyCode == Keys.Down |
				e.KeyCode == Keys.Add | e.KeyCode == Keys.Subtract)
			{
				e.Handled = true;
			}
		}

		private void ScrollPanel_Scroll(object sender, ScrollEventArgs e)
		{
			int oldValue = e.OldValue;
			int newValue = e.NewValue;
			ScrollEventType set = e.Type;
			ScrollOrientation so = e.ScrollOrientation;

			Point p = CalculateImageCenter();

			if (e.ScrollOrientation == ScrollOrientation.HorizontalScroll)
			{
				hScrollPosition = e.NewValue;
				hCenter = e.NewValue + p.X;
			}

			if (e.ScrollOrientation == ScrollOrientation.VerticalScroll)
			{
				vScrollPosition = e.NewValue;
				vCenter = e.NewValue + p.Y;
			}
		}

		private void KeystrokeCaptureTextBox_GotFocus(object sender, EventArgs e)
		{
			ScrollPanel.HorizontalScroll.Value = hScrollPosition;
			ScrollPanel.VerticalScroll.Value = vScrollPosition;
		}

		#endregion
	}
}

GeneralRe: Scrolling Capabilities Pin
BillWoodruff6-Jul-09 7:30
professionalBillWoodruff6-Jul-09 7:30 
GeneralUm Pin
konikula30-Nov-08 17:33
konikula30-Nov-08 17:33 
GeneralAbsolutely awesome. Pin
David Catriel15-Aug-07 9:18
David Catriel15-Aug-07 9:18 
GeneralRe: Absolutely awesome. Pin
gabkorta17-Jul-08 2:39
gabkorta17-Jul-08 2:39 
QuestionDoes this fix everything? Pin
smithsmithsmith2-Jan-07 13:36
smithsmithsmith2-Jan-07 13:36 
QuestionMore scrolling issues Pin
Eric Weaver2-Oct-06 9:18
Eric Weaver2-Oct-06 9:18 
Questiondisable scrolling with MouseWheel? Pin
Smeld18-Sep-06 20:21
Smeld18-Sep-06 20:21 
QuestionAutomatic scroll events Pin
jethro_tull7-Aug-06 22:48
jethro_tull7-Aug-06 22:48 
QuestionPoints of interest Pin
rheia8419-Jun-06 2:07
rheia8419-Jun-06 2:07 
QuestionMouseClick Event not working? Pin
mhavoc8-Jan-06 19:11
mhavoc8-Jan-06 19:11 
AnswerRe: MouseClick Event not working? Pin
mhavoc8-Jan-06 20:21
mhavoc8-Jan-06 20:21 
GeneralLinks Pin
fwsouthern6-Dec-05 18:53
fwsouthern6-Dec-05 18:53 
GeneralRe: Links Pin
smallwisdom5-Jan-06 8:34
smallwisdom5-Jan-06 8:34 
Questionautofocus, dock to fill and scroll Pin
yfoulon30-Nov-05 23:29
yfoulon30-Nov-05 23:29 
AnswerRe: autofocus, dock to fill and scroll Pin
smallwisdom1-Dec-05 13:46
smallwisdom1-Dec-05 13:46 
GeneralFocus on trackball Pin
yfoulon30-Nov-05 22:15
yfoulon30-Nov-05 22:15 
GeneralRe: Focus on trackball Pin
smallwisdom1-Dec-05 13:22
smallwisdom1-Dec-05 13:22 
GeneralNot bad Pin
Tim McCurdy29-Nov-05 2:56
Tim McCurdy29-Nov-05 2:56 
Generaldownload links broken Pin
sprague29523-Nov-05 4:29
sprague29523-Nov-05 4:29 
GeneralRe: download links broken Pin
sprague29523-Nov-05 6:51
sprague29523-Nov-05 6:51 
QuestionWhere's the image ? Pin
saegezauberer22-Nov-05 21:21
saegezauberer22-Nov-05 21:21 

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.