This article talks about how to take a screenshot of a Windows form and then print it.
As I am working on an application, I end up with several smaller test applications to learn about different ways to do something. After creating them, they are a great source to go back to if I don’t have to do a certain thing for a while so I can remember how to do it. Always comment code well.
In my current main project, it inputs 4 dimensions and 2 options, does over 100 calculations and then outputs 8 final dimensions.
I wanted to print the form and output for the end user, and since I’ve haven’t worked much with printing, or screenshots, I had a lot to learn.
I wasn’t sure how to print a form so I added all of the standard controls and looked to see what it would do for me. That didn’t turn out very well, you can’t just add the controls and hope to fumble your way through to get it to work. So I downloaded and installed the Visual Basic Power pack 3. After some reading, trial, and a lot of error, I decided I didn’t want to use the Power pack because it had to be installed on the end user system in order for the printing to work. So I went back to the internet, and started by searching for how to take a screenshot. I found an interesting C# version that took a screenshot of the entire screen then saved it to a file (located here). After converting it to VB.NET, I was ready to see what it could do.
It worked great for taking a screen of the entire screen, but I wanted just the form.
C# and Converted VB.NET Code
private void btnCapture_Click(object sender, EventArgs e)
Graphics graph = null;
Bitmap bmp = new Bitmap(Screen.PrimaryScreen.Bounds.Width,
graph = Graphics.FromImage(bmp);
graph.CopyFromScreen(0,0, 0, 0, bmp.Size);
Private Sub btnCapture_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnCapture.Click
Dim graph As Graphics = Nothing
Dim bmp As New Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height)
graph = Graphics.FromImage(bmp)
graph.CopyFromScreen(0,0, 0, 0, bmp.Size
Catch ex As Exception
(Note: The C# code errored out when trying to use an online code converter.)
Now that I had working code to create the screenshot with, it was time to start tweaking the code to get it to just take a screenshot of just the form.
Rather than taking the screenshot, then finding and opening up the image each time I saved the file, I added the ability for Microsoft Paint to open and view the image after each screenshot was taken, it will overwrite the file each time so only 1 file is left, unless you add code to delete the file. After the screenshot was saved, I could go back and change the numbers for the size, and location of what was captured. After getting the result you want, you can just comment out the code or remove it all together.
(Note 2: The screenshot is saved to the debug or release folder that the program is run from, using the code above “
SaveImage(bmp)”. I had to change the output to the “c:\Filename.png” because it would not find the file with the number of characters it took for the path length to where the projects were saved, and starting Microsoft Paint from code to open the file.)
Final Code for Screen Capture Test
Private Sub GetScreenshot_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles GetScreenshot.Click
Dim graph As Graphics = Nothing Try
Dim frmleft As System.Drawing.Point = Me.Bounds.Location
Dim bmp As New Bitmap(Me.Bounds.Width + 8, Me.Bounds.Height + 8)
graph = Graphics.FromImage(bmp)
Dim screeny As Integer = frmleft.Y
graph.CopyFromScreen(screenx - 5, screeny - 5, 0, 0, bmp.Size)
filepath = "C:\temp.png"
Process.Start("mspaint.exe ", filepath)
Catch ex As Exception
End Try End Sub
The code above is just the button click event to get the capture and then start Microsoft Paint with the saved screenshot.
As you can see from all of the comments, we first set the variable graph = to nothing.
Next, we want to get the location of the upper left hand corner of the form. We then set the variable bmp as a new
Bitmap type and get the size of what we want to capture. I added 8 to the width and height to get more of the right and bottom of the border to display. It may be different for different border types.
We set the variable graph = to a graphics object created from the image bmp. Next we get the X,Y coordinates of the upper left hand corner of our form from the “
frmleft” variable we got first.
We then do “
graph.CopyFromScreen(screenx – 5, screeny – 5, 0, 0, bmp.Size)”. Here I subtracted 5 from each of the input X,Y coordinated to capture more of the left and top of the form.
This saves the image to the variable starting at the input X,Y and the output X,Y and then Width and height of the capture represented as the variable “
bmp.Size” .The output X, Y coordinates will almost always be “0” Zero.
Next, we save the file to the local system so Microsoft Paint can open it for viewing the result. As of this writing, I have not tested for ways of bypassing saving the file to disk yet. We then open the file for viewing with Microsoft Paint. Finally we
Dispose, or release the graphic objects.
The next trick is to figure out how to output this to a printer.
After several hours and many failed attempts trying any code I could find, I finally discovered that text and images are handled a little differently.
Final Code for Outputting to a Print Preview Control
Private Sub btnPrintPreview_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles btnPrintPreview.Click
Public Sub New()
Private Sub Print_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
AddHandler PrintDocument2.PrintPage, AddressOf Me.printDocument2_PrintPage
Me.PrintPreviewDialog1.Document = Me.PrintDocument2
Private Sub printDocument2_PrintPage(ByVal sender As Object, _
ByVal e As System.Drawing.Printing.PrintPageEventArgs)
Dim prnborderX As Single
prnborderX = PrintDocument2.DefaultPageSettings.PrintableArea.X + 1
Dim prnborderY As Single
prnborderY = PrintDocument2.DefaultPageSettings.PrintableArea.Y + 10
Dim pagewidth As Single
pagewidth = PrintDocument2.DefaultPageSettings.PrintableArea.Width
Dim pageHeight As Single
pageHeight = PrintDocument2.DefaultPageSettings.PrintableArea.Height
Dim br As SolidBrush = New SolidBrush(Color.Black)
Dim p As Pen = New Pen(Color.Black)
Dim g As Pen = New Pen(Color.CornflowerBlue)
Dim PrntDoc2 As String
PrntDoc2 = "Paper Size = " & PrintDocument2.DefaultPageSettings.PaperSize.ToString
prndoc2default = "Printable Area = " & PrintDocument2.DefaultPageSettings.PrintableArea.ToString
e.Graphics.DrawString(PrntDoc2, f, Brushes.RosyBrown, 50, 70)
e.Graphics.DrawString(prndoc2default, f, Brushes.Black, 50, 90)
e.Graphics.DrawRectangle(p, 50, 150, 300, 150)
e.Graphics.DrawRectangle(g, prnborderX, prnborderY, pagewidth - 50, pageHeight - 25)
Dim drawImg As New Bitmap("C:\temp.png")
Dim mgHeight As Single = drawImg.Height
Dim mgWidth As Single = drawImg.Width
Dim sourceRectangle As New Rectangle(0, 0, mgWidth, mgHeight)
Dim destRetangle1 As New Rectangle(40, 350, pagewidth - 75, 600)
e.Graphics.DrawImage(drawImg, destRetangle1, sourceRectangle, GraphicsUnit.Pixel)
Dim PictureSize As String
PictureSize = ("Image Width = " & mgWidth.ToString & _
" Image Height = " & mgHeight.ToString)
e.Graphics.DrawString(PictureSize, f, Brushes.Blue, 50, 110)
The first thing we need to do is add a standard Print Preview Control and a Print Document control to the form. Just using these, you don’t need the power pack. (As far as I know of anyway.)
Next a Button or use a menu item for opening the Print Preview window with the click event.
Next, we add a Component initializer for the code in the
Next we need the handlers for the Print Document Control and the “Address Of “ to the sub that will do the work of what will be “Painted” to the Print Preview control.
PrintDocument2 is the name of the Print Document control that was added they need to match.
printDocument2_PrintPage is the name of the sub that will do the work.
If the Component initializer and the “
Print_Load” sub are not present, then the Print preview control will not display anything.
Next, we move onto the
printDocument2_PrintPage(ByVal sender As Object, ByVal e As System.Drawing.Printing.PrintPageEventArgs).
printDocument2_PrintPage is a variable name but the rest needs to be the same.
Next, I need to understand the terms and what they are used for.
Font, the font to use, set what font name you want here. Just make sure it is common font or it may fail on other systems.
Solid Brush, solid brush will be used with text for setting the color, or filling in areas.
Pen, the pen is used for setting the color of lines or shapes.
e.Graphics.DrawString this is event driven, so this tells the
DrawString event to draw the (
string that is referenced, the font to use, the color of the brush to use, the X,Y coordinates to start painting the text at from the top left hand corner of the page).
This test program:
- Outputs an Outline Box just inside of the printable area, to help see where the printable area is as laid out on the print preview control.
- Prints the paper size of the default printer as a
- Prints the Printable Area of the default printer, (this may be different for each individual printer).
- Draws a box (left over from some test code).
- Then paints the image scaled to the width of the printable area. I used the printable area outline box to help me center the image by adjusting the parameters.
When this page was printed, the outline box was centered on the paper, not offset to the left like the preview shows.
Let's see what the output looks like:
At this point after all that I have learned, rather than outputting a screenshot to the print preview control, I can just pass the text needed to be displayed from either the output text boxes or globally declared variables for the items I need displayed, for a more printer friendly version.
This test application does not have error handling for when the image file does not exist. So the screenshot button will need to be hit at least once.
This test program does not have any code for using the landscape view. But another sub form in this project has a button for opening the “print setup dialog” but no code for saving a selection of landscape or portrait.
On another sub form in this project is a button to list all of the default values for the default printer. All sub forms can be opened from a button starting from the main form.
The source and binary are available from my SkyDrive folder.
Due to Microsoft Closing down their offering of Microsoft Office Live Small Business service and forcing their current customers to either move to the new Office 365 program and completely rebuild their web sites again or find a new web host and rebuild there site. Effective April 30, 2012.
After that, my website may be gone or moved to a new host. I also may not be able to keep the SkyDrive location my articles refer to after that since it is tied to my website.
After all that I have learned over that last few days, once you learn what is required to do the job, it is not difficult to implement the screen shot or the printing. I see no need at the moment to use a third party control that may require it to be installed, and possibly break you application because of it. Just some number changes can move your image or text anywhere you want. A person could even make a few snippets with the replacements set for quicker use later.
I hope someone learned as much from this adventure as I did.
My first experience with computers was when my mom gave a Timex Sinclair 1000 to me for Christmas some time in the late 70's (I still have it)There I learned to copy code from magazines to save to cassette tapes for playing games.
Since then I have dabbled in:
Basic,Qbasic,ruby,python,Java Script, HTML, CSS, C#, C++, Perl, and a few other I can't think of off hand.
Now I Mainly work with VB Script and VB.Net
I Prefer to build programs that make use of the GUI so I don't have to remember all of the syntax for console apps. I realy don't care much for HTML because of the way you build and then run to see if it looks right. Also the new WPF is to much like HTML so I steer clear of it for now.
Most of what I build is for getting information from a system to use in system repair.I make heavy use of the WMI classes. Why reinvent something.