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:
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:
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)
{
_iFirstTapX = e.X;
_iFirstTapY = e.Y;
}
}
protected override void OnMouseMove(MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
_pCropLocation.X -= (e.X - _iFirstTapX);
_pCropLocation.Y -= (e.Y - _iFirstTapY);
Replace();
_iFirstTapX = e.X;
_iFirstTapY = e.Y;
this.Invalidate();
}
}
protected override void OnPaint(System.Windows.Forms.PaintEventArgs e)
{
Graphics gxOff;
if (_bOffscreen == null) _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);
}
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()
{
if (_bPicture != null)
{
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