This article demonstrates one method of capturing Web type content (in this example I capture XML) to a single image file. My original problem that I'd been trying to overcome was to somehow show a directory structure in a document, and at that time I didn't just want to take a screen shot of Windows Explorer. I stumbled across a small sample application which I recorded as a .NET console application that enabled me to iterate through the directories recursively and list files in those directories, and capture the result to an XML file. Once I'd done that, I wanted to take the view of the XML file (with some nodes expanded and others collapsed) as seen when you view an XML file in Internet Explorer. The problem I encountered was that my view was longer than my monitor, so my screen shot had to be manually composed of several pieces. I did try to use the
DrawBitmapmethod to capture the output, but this proved problematic and very often left me with completely blank files.
My chosen solution was to determine the screen coordinates of an embedded
WebBrowserand then take a series of snapshots which I joined into a single file. This solution also caters to instances where the length of the Web content isn't an exact multiple of the size of the browser. To make things a little more interesting, I did the production of the XML as a
BackgroundWorkertask, and displayed progress in the status bar. The asynchronous task can be cancelled prior to completion.
Using the Code
As mentioned above, my example generates an XML file which is then displayed in an embedded
WebBrowsercontrol. If you want to display other Web content, then you should use a URL in the
WebBrowser.Navigatemethod as below:
The topic of this example is saving image so I'll go through the functionality around that in a small section taken from the demo application. The first step is to declare a load of
intvariables to hold the coordinates. My
WebBrowseris in a
Formso the real screen
Ylocation of the top left pixel is calculated by adding up all of their respective
Ylocations. Note: I added
Yto overcome the forms frame - is there a programmatic way around this?
The next pair of
intvariables hold the size of the
WebBrowser.Document(which may be greater than the size of the screen). And then we have some variables for the visible size, and finally a pair for offsets.
int realBrowserPositionX =
this.Location.X + panel1.Location.X + webBrowser1.Location.X + 5;
int realBrowserPositionY =
this.Location.Y + panel1.Location.Y + webBrowser1.Location.Y + 30;
int browserFullWidth = webBrowser1.Document.Body.ScrollRectangle.Width;
int browserFullHeight = webBrowser1.Document.Body.ScrollRectangle.Height;
int browserWindowWidth = webBrowser1.Width;
int browserWindowHeight = webBrowser1.Height;
int browserOffsetX = 0;
int browserOffsetY = 0;
I then instantiate a
GDI Bitmapthe size of the full image and then a
Graphicsdrawing surface from the
Bitmap. These will be used to build the full image.
Bitmap fullImage = new Bitmap(browserFullWidth, browserFullHeight);
Graphics fullImageGraphics = Graphics.FromImage(fullImage);
WebBrowseris scrolled so that the top of the image is in view, and then I create some variables to hold the calculated size of the image (in cases it doesn't fill the
WebBrowser). I then instantiate another
GDI Bitmapthe size of the image segment and then a
Graphicsdrawing surface from the new
Bitmap. I use the
Graphics.CopyFromScreenmethod to grab the displayed segment of the image, and then the
Graphics.DrawImagemethod to add it to the full-size image. After that I scroll the
WebBrowserby the size of the window.
I repeat this process in a loop until I have gathered all of the image.
webBrowser1.Document.Body.ScrollTop = 0;
int actualImageSegmentHeight = Math.Min(browserWindowHeight,
browserFullHeight - browserOffsetY);
int actualBrowserWindowOffsetY = Math.Min(browserOffsetY,
browserWindowHeight - actualImageSegmentHeight);
Bitmap sectionOfImage =
new Bitmap(browserWindowWidth, actualImageSegmentHeight);
Graphics sectionOfImageGraphics = Graphics.FromImage(sectionOfImage);
realBrowserPositionY + actualBrowserWindowOffsetY,
0, 0, new Size(browserWindowWidth, actualImageSegmentHeight),
fullImageGraphics.DrawImage(sectionOfImage, browserOffsetX, browserOffsetY,
browserOffsetY += browserWindowHeight;
webBrowser1.Document.Body.ScrollTop += browserWindowHeight;
while (browserOffsetY < browserFullHeight);
I hope that the example is easy to follow. I had considered splitting it into a number of separate files, but thought that this single module was not excessively large.
Using the Demo Application
Click on the top
ComboBoxand select "Browse.." and then browse to a directory with some sub-directories in it. Then click on the Start button. The resulting XML file is displayed in the
WebBrowserwindow, and you can interact with it in the usual manner (click on a "-" to close an
XmlElementand on a "+" to open). Click on the Save to Image button to save the view in the
WebBrowserto a file.
Points of Interest
Whilst tinkering with this application, I decided to have a go at using
BackgroundWorkerto run my task in the background. Starting and prematurely stopping the task is easy enough as shown in the example below:
In order for your application to be able to stop on demand, you need to check if the
BackgroundWorkertask should stop as soon as it has been started, and at regular intervals throughout the task like below (this should be done in each of your time consuming functions):
e.Cancel = true;
Reporting progress from the
BackgroundWorkertask is achieved by calling the
worker.ReportProgress(int progress) at some suitable point.
- 22nd November, 2006: Initial post