Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

TouchPictureBox, Slide Pictures with Fingers!

0.00/5 (No votes)
14 Oct 2008 1  
Leave traditional scrollbars for picture viewing on small screens.
Demo Project, main form Demo Project, TouchPictureBox form

Introduction

The idea came while developing a smart device app where man should view pictures, larger than the screen, where I found that scrollbars weren't so easy to use, even by these times where "touch" experience is such a fashion! So I decided to build a new control, so man could navigate through the picture using his finger. Afterwards, I added zoom capabilities, so man could easily see the whole picture and then navigate through it.

Using the Code

The source code project will build a DLL file containing the control. You will then be able to add it to the Visual Studio Toolbox, and then use it like a normal PictureBox. If you open the solution included in the zip file, build it and start the project, you'll get a sample application that you can upload to your Pocket PC device, to test it live! Use the "Live Demo" menu at the bottom to test the control using an embedded picture, which is a standard Windows Vista Wallpaper, or you could browse your device for other pictures, and then tap the "Show" button to test the control using your own pictures!

Control Properties

  • CropLocation: The Point of the upper left corner of the part of the picture that is shown. This property lets you get the actual position, or set it for example at design time for a default start, or in the code if you want to force scrolling. The point will be updated while scrolling. To understand it, just see the following picture:

    CropLocation

  • Picture: The Bitmap of the picture being shown. Just use it like the traditional Image property, at design or run time!
  • StopEdges: A boolean value determining if the scrolling must stop when an edge of the picture is reached. These pictures illustrate the behaviour:

    StopEdges = true StopEdges = false

  • Zoom: An Int32 value determining the actual zooming value, in percentage. "100" will mean 100%, or if you prefer, "real size"!
  • ZoomMin: An Int32 value determining the minimum zoom value allowed.
  • ZoomMax: An Int32 value determining the maximum zoom value allowed.
  • ZoomStep: An Int32 value determining the zoom step. When you will call ZoomIn() or ZoomOut() methods at run time, the Zoom value will be updated ZoomStep by ZoomStep. For example, a value of 25 will get zoom values of 25, 50, 75, 100, 125, 150, etc.

Let's Get Inside the Code

Now, we'll have a look at the most important code part: the three events that do the real thing. These are OnMouseDown, OnMouseMove and OnPaint. In fact, we want to get the moment the user taps on the screen and keep the coordinates. Then, when the user moves while maintaining stylus or finger pressure, we want to scroll the picture.

We had a first solution: keep the first tap coordinates in mind, then always calculate the new picture coordinates using this memory and the actual MouseMove coordinates. But that's a bit complicated. So the solution I used will calculate the new picture coordinates on MouseMove, and then keep the new mouse (stylus or finger) coordinates as the "first" ones.

protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs e)
{
    if (e.Button == MouseButtons.Left)
    {
	// Left button clicked, save click position
	_iFirstTapX = e.X;
	_iFirstTapY = e.Y;
    }
}

protected override void OnMouseMove(MouseEventArgs e)
{
    if (e.Button == MouseButtons.Left)
    {
	// Displace the CropLocation by the move size
	_pCropLocation.X -= (e.X - _iFirstTapX);
	_pCropLocation.Y -= (e.Y - _iFirstTapY);

	// Replace picture to avoid going further than the edges
	Replace();

	// Picture has moved, save the new click position for next move
	_iFirstTapX = e.X;
	_iFirstTapY = e.Y;
	this.Invalidate();
    }
}

protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
    Graphics gxOff;	         // Offscreen graphics
            
    if (_bOffscreen == null) // Bitmap for doublebuffering
	_bOffscreen = new Bitmap(ClientSize.Width, ClientSize.Height);

    gxOff = Graphics.FromImage(_bOffscreen);
    gxOff.Clear(this.BackColor);
  
    if (_bPicture != null)
    {
	Rectangle rDst = new Rectangle
			(0, 0, ClientSize.Width, ClientSize.Height);
	Rectangle rSrc = new Rectangle(_pCropLocation.X, _pCropLocation.Y, 
			ClientSize.Width, ClientSize.Height);
	gxOff.DrawImage(_bZoomedPicture, rDst, rSrc, GraphicsUnit.Pixel);
    }

    // Draw from the memory bitmap
    e.Graphics.DrawImage(_bOffscreen, 0, 0);

    base.OnPaint(e);
}

_iFirstTapX and _iFirstTapY will keep the first tap coordinates. _pCropLocation is a Point defining the upper left coordinates of the part of the picture that should be displayed. I call it a crop of the whole picture.

Then you'll see a call to the Replace() method. This will check if the displacement goes further than the edges of the picture, and will put it back if the StopEdges property is set to true.

this.Invalidate() is always called when the control needs to be repainted. So here we come to the OnPaint event. As you can see, it will use doublebuffering to prevent flickering. A temporary bitmap of the size of the control is created, then we look if a picture has been set. If true, we get the rectangle part of the original picture that needs to be displayed, and draw it to the temporary bitmap.

But wait a minute! What is this _bZoomedPicture ? Well, to allow easy sliding when in zoomed mode, the control creates the zoomed picture in memory, which will be the source picture for drawing. So when you change the Picture property, the Zoom property, or use the ZoomIn() or ZoomOut() methods, we will call a CreateZoomedPicture() to calculate this new version of the picture. Here is the code:

private void CreateZoomedPicture()
{
    // Draw zoomed version of the picture. 
    // In fact this version will be used for control drawing.
    if (_bPicture != null)
    {
	// If zoom value is 100, just take original picture !
	if (_iZoom == 100)
	    _bZoomedPicture = _bPicture;
         else
         {
	    Graphics gx;
	    _bZoomedPicture = new Bitmap((int)((double)_bPicture.Width * _dZoom), 
				(int)((double)_bPicture.Height * _dZoom));

	    gx = Graphics.FromImage(_bZoomedPicture);
	    gx.Clear(this.BackColor);

	    Rectangle rDst = new Rectangle
			(0, 0, _bZoomedPicture.Width, _bZoomedPicture.Height);
	    Rectangle rSrc = new Rectangle
			(0, 0, _bPicture.Width, _bPicture.Height);
	    gx.DrawImage(_bPicture, rDst, rSrc, GraphicsUnit.Pixel);
         }
    }
}

Here we just take benefit of the .NET CompactFramework functionalities: draw the source bitmap in a larger or smaller rectangle. The picture will be resampled without having to worry about how to do that!

Use of this Class

You can see in the demo project that you can make a Windows Form filled with this control, and two menus Zoom In and Zoom Out, defined for the two soft buttons of the device, and then do full screen navigation. Each button code will just do a simple call to touchPictureBox1.ZoomIn() and touchPictureBox1.ZoomOut()! But you can also use it as a small control in a Form, for small navigation.

Included you will have a Visual Studio 2008 project, but it should be easy to get it work in Visual Studio 2005 as it uses the .NET CF 2.0. You will also get a small icon for a good representation in the ToolBox, and the DesignTimeAttributes.xmta file to have good properties behaviour and descriptions!

This class will run on Windows Mobile 5 and Windows Mobile 6, but you should easily use it and compile it for desktop applications, so users will slide using their mouse, or on a Tablet PC, using their stylus!!!

History

  • 15th October 2008: First version

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