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.
In the conclusion to our series on building a scrollable and zoomable image viewer, we'll add support for zooming, auto centering, size to fit and some display optimizations and enhancements.
Unlike parts 2 and 3, we're actually adding quite a lot of new functionality, some of it more complicated than others.
First, we're going to remove the ShowGrid
property. This
originally was a simple on/off flag, but we want more control
this time.
We've also got a number of new properties and backing events to add:
AutoCenter
- controls if the image is automatically centered
in the display area if the image isn't scrolled.SizeToFit
- if this property is set, the image will
automatically zoom to the maximum size for displaying the
entire image.GridDisplayMode
- this property, which replaces ShowGrid
will determine how the background grid is to be drawn.InterpolationMode
- determines how the zoomed image will be
rendered.Zoom
- allows you to specify the zoom level.ZoomIncrement
- specifies how much the zoom is increased or
decreased using the scroll wheel.ZoomFactor
- this protected property returns the current
zoom as used internally for scaling.ScaledImageWidth
and ScaledImageHeight
- these protected
properties return the size of the image adjusted for the
current zoom.Usually the properties are simple assignments, which compare the values before assignment and raise an event. The zoom property is slightly different as it will ensure that the new value fits within a given range before setting it.
Using the MinZoom
and MaxZoom
constants we are specifying a
minimum value of 10% and a maximum of 3500%. The values you are
assign are more or less down to your own personal preferences -
I don't have any indications of what a "best" maximum value
would be.
Setting the SizeToFit
property should disable the AutoPan
property and vice versa.
Several parts of the component work from the image size, however
as these now need to account for any zoom level, all such calls
now use the ScaledImageWidth
and ScaledImageHeight
properties.
The AdjustLayout
method which determines the appropriate
course of action when certain properties are changed has been
updated to support the size to fit functionality by calling the
new ZoomToFit
method.
Due to the additional complexity in positioning and sizing, we're also adding functions to return the different regions in use by the control.
GetImageViewPort
- returns a rectangle representing the size
of the drawn image.GetInsideViewPort
- returns a rectangle representing the
client area of the control, offset by the current border
style, and optionally padding.GetSourceImageRegion
- returns a rectangle representing the
area of the source image that will be drawn onto the control.The sample project has been updated to be able to display the
results of the GetImageViewPort
and GetSourceImageRegion
functions.
As with the previous versions, the control is drawn by
overriding OnPaint
, this time we are not using clip regions or
drawing the entire image even if only a portion of it is
visible.
Depending on the value of the GridDisplayMode
property, the
background tile grid will either not be displayed, will be
displayed to fill the client area of the control, or new for
this update, to only fill the area behind the image. The
remainder of the control is filled with the background color.
Previous versions of the control drew the entire image using the
DrawImageUnscaled
method of the Graphics
object. In this
final version, we're going to be a little more intelligent and
only draw the visible area, removing the need for the previous
clip region. The InterpolationMode
is used to determine how
the image is drawn when it is zoomed in or out.
With the control now all set up and fully supporting zoom, it's time to allow the end user to be able to change the zoom.
The first step is to disable the ability to double click the control, by modifying the control styles in the constructor.
We're going to allow the zoom to be changed two ways - by either scrolling the mouse wheel, or left/right clicking the control.
By overriding OnMouseWheel
, we can be notified when the user
spins the wheel, and in which direction. We then adjust the zoom
using the value of the ZoomIncrement
property. If a modifier
key such as Shift or Control is pressed, then we'll modify the
zoom by five times the increment.
Normally, whenever we override a method, we always call it's
base implementation. However, in this case we will not; the
ScrollbableControl
that we inherit from uses the mouse wheel
to scroll the viewport and there doesn't seem to be a way to
disable this undesirable behaviour.
As we also want to allow the user to be able to click the
control with the left mouse button to zoom in, and either the
right mouse button or left button holding a modifier key to zoom
out, we'll also override OnMouseClick
.
Unlike with the mouse wheel and it's fixed increment, we want to use a different approach with clicking. If zooming out and the percentage is more than 100, then the zoom level will be set to the current zoom level + 100, but rounded to the nearest 100, and the same in reverse for zooming in.
If the current zoom is less than 100, then the new value will +- 75% of the current zoom, or reset to 100 if the new value falls between 75 and 125.
This results in a nicer zoom experience then just using a fixed value.
You can download the final sample project from the link below.
One of the really annoying issues with this control that has
plagued me during writing this series is scrolling the
component. During scrolling there is an annoying flicker as the
original contents are moved, then the new contents are drawn. At
present I don't have a solution for this, I've tried overriding
various WM_*
messages but without success. A future update to
this component will either fix this issue, or do it's own
scrollbar support without inheriting from ScrollableControl
,
although I'd like to avoid this latter solution.
If anyone knows of a solution please let us know!
Another enhancement would be intelligent use of the interpolation mode. Currently the control uses a fixed value, but some values are better when zoomed in, and some better when zoomed out. The ability for the control to automatically select the most appropriate mode would be useful.
Like what you're reading? Perhaps you like to buy us a coffee?
# DotNetKicks.com
# DotNetShoutout
# Roberto Beretta
# Richard Moss
# Mark Malburg
# Richard Moss
# Roberto Beretta
# Richard Moss
# Kerwin Lumpkins
# Richard Moss
# Casey
# Richard Moss
# Arsalan
# Cuong Doan
# Bryan Brannon
# Japala
# Jordan
# Richard Moss
# Rafael Vasco
# Richard Moss
# Rafael Vasco
# Henrik Haftmann
# Richard Moss
# DirtyCode
# Richard Moss
# urlreader
# Richard Moss
# MutStarburst
# Richard Moss
# Chris
# Richard Moss
# sudhakar
# Jack Cai
# Richard Moss
# Naeem Shah