Displaying multi-page tiff files using the ImageBox control and C#
A brief article showing how to display individual pages from a multi-page tiff file in the ImageBox control.
Part 4 of this series (by far the most popular article on cyotek.com) was supposed to be the end, but recently I was asked if was possible to select part of an image for saving it to a file. After implementing the new functionality and lacking ideas for a new post on other matters, here we are with a new part!
First thing is to add some new properties, along with backing events. These are:
SelectionMode- Determines if selection is available within the control
SelectionColor- Primary color for drawing the selection region
SelectionRegion- The currently selected region.
LimitSelectionToImage- This property allows you to control if the selection region can be drawn outside the image boundaries.
IsSelecting- This property returns if a selection operation is in progress
SelectionMode property is set, then the
AllowClickZoom properties will both be set to
false to avoid
We also need a couple of new events not directly tried to properties.
Selecting- Occurs when the user starts to draw a selection region and can be used to cancel the action.
Selected- Occurs when the user completes drawing a selection region
These events are called when setting the
Before adding support for defining the selection region, we'll
add the code to draw it - that way we'll know the code to define
the region works! To do this, we'll modify the existing
OnPaint override, and insert a call to a new method named
DrawSelection method itself is very straightforward. First
it fills the region with a translucent variant of the
SelectionColor property, then draws a solid outline around
this. A clip region is also applied to avoid overwriting the
As with most of the methods and properties in the
control, it has been marked as
virtual to allow you to
override it and provide your own drawing implementation if
required, without needing to redraw all of the control.
GetOffsetRectangle method will be described a little
further down this article.
Currently the selection region can only be defined via the
mouse; there is no keyboard support. To do this, we'll do the
usual overriding of
OnMouseUp aren't being used for much in this
case, the former is used to clear an existing selection region,
the later to notify that the selection is no longer being
OnMouseMove calls the
ProcessSelection method which
is where all the action happens.
First, we check to make sure a valid selection mode is set.
Then, if a selection operation hasn't been initiated, we attempt
to set the
IsSelecting property. As noted above, this property
will call the
Selecting event allowing the selection to be
cancelled if required by the implementing application.
If selection was allowed, we construct the co-ordinates for a rectangle, automatically switching values around to ensure that the rectangle will always have a positive width and height. We'll also offset the co-ordinates if the image has been scrolled or if it has been centred (or both!).
As this is the zoomable scrolling image control, we also need
to rescale the rectangle according to the current zoom level.
This ensures the
SelectionRegion property always returns a
rectangle that describes the selection at 100% zoom.
The final step is to constrain the rectangle to the image size
LimitSelectionToImage property is set, before assigning
the final rectangle to the
And that's pretty much all there is to it.
When using the control in our own products, it's very rarely to
display a single image, but rather to display multiple items, be
it sprites in a sprite sheet or tiles in a map. These
implementations therefore often require the ability to get a
single item, for example to display hover effects. This can be
tricky with a control that scrolls, zooms and centres the image.
Rather than repeat
ZoomFactor calculations (and worse
AutoScrollPosition) everywhere, we added a number of helper
GetScaled*. Calling these with
a "normal" value, will return that value repositioned and
rescaled according to the current state of the control. An
example of this is the
DrawSelection method described above
which needs ensure the current selection region is rendered
Versions of these methods exist for the following structures:
These methods can come in extremely useful depending on how you are using the control!
The demonstration program displays two
ImageBox controls, the
first allows you to select part of an image, and the second
displays the cropped selection. I didn't add any sort of crop
functionality to the control itself, but the following snippets
shows how the demonstration program creates the cropped version.
We'll finish off by adding a couple of helper methods that implementers can call:
Currently, if you try and draw the selection bigger than the visible area of the control, it will work, but it will not scroll the control for you. I also was going to add the ability to move or modify the selection but ran out of time for this particular post.
As always, if you have any comments or questions, please contact us!
Like what you're reading? Perhaps you like to buy us a coffee?