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 this article, I'll describe how to extend the ImageBox control discussed in earlier articles to be able to display PDF files with the help of the GhostScript library and the conversion library described in the previous article.
You can download the source code used in this article from the links below, these are:
ImageBoxcontrol, and the extended
Please note that the native GhostScript DLL is not included in these downloads, you will need to obtain that from the GhostScript project page.
To start extending the
ImageBox, create a new class and
ImageBox control. I also decided to override some
of the default properties, so I added a constructor which sets
the new values.
To ensure correct designer support, override versions of the
properties with new
DefaultValue attributes were added. With
this done, it's time to add the new properties that will support
viewing PDF files. The new properties are:
PdfFileName- the filename of the PDF to view
PdfPassword- specifies the password of the PDF file if one is required to open it (note, I haven't actually tested that this works!)
Settings- uses the
Pdf2ImageSettingsclass discussed earlier to control quality settings for the converted document.
PageCache- an internal dictionary which stores a
Bitmapagainst a page number to cache pages after these have loaded.
With the exception of
PageCache, each of these properties also
has backing event for change notifications, and as
also bind an event detect when the individual setting properties
PdfImageBox doesn't supply a user interface for
navigating to different pages, we want to make it easy for the
hosting application to provide one. To support this, a new
CurrentPage property will be added for allowing the active
page to retrieved or set, and also a number of readonly
CanMove* properties. These properties allow the host to query
which navigation options are applicable in order to present the
Again, to make it easier for the host to connect to the control, we also add some helper navigation methods.
Finally, it can sometimes take a few seconds to convert a page in a PDF file. To allow the host to provide a busy notification, such as setting the wait cursor or displaying a status bar message, we'll add a pair of events which will be called before and after a page is converted.
Each of the property changed handlers in turn call the
method. This method first clears any existing image cache and
then initializes the conversion class based on the current PDF
file name and quality settings. If the specified file is a valid
PDF, the first page is converted, cached, and displayed.
Each time the
CurrentPage property is changed, it calls the
SetPageImage method. This method first checks to ensure the
specified page is present in the cache. If it is not, it will
load the page in. Once the page is in the cache, it is then
displayed in the
ImageBox, and the user can then pan and zoom
as with any other image.
Note that we operate a lock during the execution of this method, to ensure that you can't try and load the same page twice.
With this method in place, the control is complete and ready to be used as a basic PDF viewer. In order to keep the article down to a reasonable size, I've excluded some of the definitions, overloads and helper methods; these can all be found in the sample download below.
The sample project demonstrates all the features described above and provides an example setting up a user interface for navigating a PDF document.
At the moment, the
PdfImageBox control processes on page at a
time and caches the results. This means that navigation through
already viewed pages is fast, but displaying new pages can be
less than ideal. A possible enhancement would be to make the
control multithreaded, and continue to load pages on a
Another issue is that as the control is caching the converted images in memory, it may use a lot of memory in order to display large PDF files. Not quite sure on the best approach to resolve this one, either to "expire" older pages, or to keep only a fixed number in memory. Or even save each page to a temporary disk file.
Finally, I haven't put in any handling at all for if the converter fails to convert a given page... I'll add this to a future update, and hopefully get the code hosted on an SVN server for interested parties.
Like what you're reading? Perhaps you like to buy us a coffee?