Using the PrintForm component in Visual Basic (Matt Gertz)

Using the PrintForm component in Visual Basic (Matt Gertz)

  • Comments 22

(This is the fourth and final part of the Paint-by-Numbers series)

Late last year, some clever guys on our Visual Basic team released the PrintForm component on the web.  The idea behind the PrintForm component was to make printing and previewing your form very easy in .NET.  I’m going to leverage this component to enable printing of the Paint-by-Number puzzles created in the application I’ve been building in this series.

First of all, I need to download the component, which is available here on the Microsoft site – it’s about 500KB.  Once downloaded, I’ll install it (you can even do this while Visual Studio is open), and now I can add the PrintForm control in the component to my toolbox.  With my form selected, I right-click on the “Printing” category in the toolbox and select “Choose Items…”  In the resulting dialog, I’ll check the box next to “Print Form” on the .NET tab, and that’ll do it once I press "OK." 

Now, I’ll drag an instance of the PrintForm control to the component tray and rename it to be “VBPBNPrintForm.” We don’t need to change any properties on it otherwise, so on the form, I’ll drop down the File menu item and double-click on “Print Preview” to generate its event handler for that command – now I can start coding!

The PrintForm command really just has one important verb – Print.  It can take several options, but overall it’s just a print job to either a printer, a file, or a preview window.  Which of those it does depends on the value you assign to its PrintAction property.  In this case, I want to just preview the document, and so I’ll need to set it to “PrintToPreview.”  The code looks like this:

    Private Sub PrintPreviewToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _

Handles PrintPreviewToolStripMenuItem.Click

            Me.VBPBNPrintForm.PrintAction = Printing.PrintAction.PrintToPreview

            Me.VBPBNPrintForm.Print(Me, PowerPacks.Printing.PrintForm.PrintOption.ClientAreaOnly)

    End Sub

 

Note that I’ve used an overload of Print which tells the control which form to print (which is my main form in this case), and also indicates that I just want to print the client area and not the window borders and so on.  However, if you run this, you’ll notice that the menu strip is printing in the preview!  This is because it’s in the client area.  Print doesn’t use document technology – it will in fact print anything visible in the client area, which works to our benefit when printing a puzzle solution (View/Show Solution is checked) or just the puzzle (View/Show Solution is unchecked), but not in this case.  So, for the duration of the printing job, I’ll simply hide the Menu, and I’ll wrap that in a try/catch/finally so that the menu will always get turned back on even if an exception is thrown from printing.  (This is hackery, plain and simple, but the benefits are enough to outweigh any sense of guilt I might feel about that.)  Now the code looks like this:

    Private Sub PrintPreviewToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _

Handles PrintPreviewToolStripMenuItem.Click

        Try

            Me.VBPBNMenuStrip.Visible = False

            Me.VBPBNPrintForm.PrintAction = Printing.PrintAction.PrintToPreview

            Me.VBPBNPrintForm.Print(Me, PowerPacks.Printing.PrintForm.PrintOption.ClientAreaOnly)

        Catch ex As Exception

        Finally

            Me.VBPBNMenuStrip.Visible = True

        End Try

    End Sub

 

and that will work nicely.  You can zoom in and out, resize the display, and even print to the default printer from the dialog that pops up. 

Now, for the actual Print command from the file menu, I could write very similar code (the one change is underlined below):

    Private Sub PrintToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles PrintPreviewToolStripMenuItem.Click

        Try

            Me.VBPBNPrintForm.PrinterSettings = Me.VBPBNPrintDialog.PrinterSettings

            Me.VBPBNMenuStrip.Visible = False

            Me.VBPBNPrintForm.PrintAction = Printing.PrintAction.PrintToPrinter

            Me.VBPBNPrintForm.Print(Me, PowerPacks.Printing.PrintForm.PrintOption.ClientAreaOnly)

        Catch ex As Exception

        Finally

            Me.VBPBNMenuStrip.Visible = True

        End Try

    End Sub

 

But that would only allow us to print to the default printer, and I want to be a little more sophisticated.  I’ll drag out a PrintDialog control (which I’ll rename VBPBNPrintDialog) and use it to capture settings before doing the actual printing.  The PrintForm control uses the same PrinterSettings object that the PrintDialog does, which makes things very easy – I just assign one to the other before doing the actual printing.  Then, I check to see if we’re printing to a file or to a printer.  If printing to a printer, then I have everything I need; if a file, I’ll need to pop up a “save file” dialog to get the filename from the user first.  The code now looks like this:

    Private Sub PrintToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _

Handles PrintToolStripMenuItem.Click

        ' Bring up the print dialog:

        Dim res As DialogResult = Me.VBPBNPrintDialog.ShowDialog

        If res = Windows.Forms.DialogResult.OK Then

            Try

                ' Copy the settings from the dialog

                Me.VBPBNPrintForm.PrinterSettings = Me.VBPBNPrintDialog.PrinterSettings

 

                ' Turn off the menu strip

                Me.VBPBNMenuStrip.Visible = False

 

                If Me.VBPBNPrintForm.PrinterSettings.PrintToFile = True Then

                    ' The user wants to print to a file, so bring up a "Save" dialog and get the file name

                    Me.VBPBNSaveFileDialog.Title = My.Resources.TITLE_SavePrintFile

                    Me.VBPBNSaveFileDialog.Filter = My.Resources.FILT_PrintFilter

                    If Me.VBPBNSaveFileDialog.ShowDialog() = Windows.Forms.DialogResult.OK Then

                        ' Go ahead & print to the file

                        Me.VBPBNPrintForm.PrintFileName = Me.VBPBNSaveFileDialog.FileName

                        Me.VBPBNPrintForm.PrintAction = Printing.PrintAction.PrintToFile

                        Me.VBPBNPrintForm.Print(Me, PowerPacks.Printing.PrintForm.PrintOption.ClientAreaOnly)

                    End If

                Else

                    ' Print to whatever printer was selected

                    Me.VBPBNPrintForm.PrintAction = Printing.PrintAction.PrintToPrinter

                    Me.VBPBNPrintForm.Print(Me, PowerPacks.Printing.PrintForm.PrintOption.ClientAreaOnly)

                End If

            Catch ex As Exception

            Finally

                ' Make sure that the menu turns back on!

                Me.VBPBNMenuStrip.Visible = True

            End Try

        End If

    End Sub

 

and we’re done with the Print code.  (The resource FILT_PrintFilter is similar to the other filter I used for saving files in the previous post, but aimed at .EPS files instead of .VBPBN files, and TITLE_SavePrintFile is just a title for the window.)

But, hold on – the preview can also use the PrinterSettings object.  For example, if the user chose “Print,” clicked "Apply" after changing some settings, but then chose Cancel, the settings would persist for the next call to Print, so we’d expect those to apply to the preview as well (otherwise, it wouldn’t be much of a preview, since there's no other way to do settings in a preview). So, we’ll add one more line to the Print Preview handler (underlined):

    Private Sub PrintPreviewToolStripMenuItem_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles PrintPreviewToolStripMenuItem.Click

        Try

            Me.VBPBNPrintForm.PrinterSettings = Me.VBPBNPrintDialog.PrinterSettings

            Me.VBPBNMenuStrip.Visible = False

            Me.VBPBNPrintForm.PrintAction = Printing.PrintAction.PrintToPreview

            Me.VBPBNPrintForm.Print(Me, PowerPacks.Printing.PrintForm.PrintOption.ClientAreaOnly)

        Catch ex As Exception

        Finally

            Me.VBPBNMenuStrip.Visible = True

        End Try

    End Sub

 

And that’s it! 

PrintForm is a great alternative to fiddling about with print documents if you don’t need to do a lot of reformatting for the printout you generate.  (In this case, all I had to do was hide a menu strip temporarily, which is much easier that defining a print document , etc.) The final application is attached to this post.  I hope you’ve enjoyed this series – it was a lot of fun to write, and already I’m thinking about the improvements that could be made, such as arbitrarily-sized grids, select/copy/paste of arbitrary bitmaps into and within the puzzle, creating a “puzzle viewer” to solve the puzzle on the PC rather than on paper, adding authoring information to the saved puzzle, supporting the red/black Paint-by-Number puzzles that have been so popular recently, and so on – it’s hard to stop! 

I’ll be around for another week or so to answer any comments on these posts, but after that I’ll be on a month-long vacation (ahhhhhh…. J), after which I’ll be digging out of e-mail for days, so I’m going to be silent for a bit.  I hope everyone enjoys their summer (or winter, depending on where you are), and I’ll probably post again at the end of August.  I hope to come up with a lot of blog application ideas as my family and I drive around the east coast of the U.S.  ‘Til then…

--Matt--*

Attachment: VB Paint-by-Numbers.zip
Leave a Comment
  • Please add 8 and 2 and type the answer here:
  • Post
  • OK here's the deal. If you are teaching Visual Basic, programming in Visual Basic for fun or profit,

  • I've searched through the documentation and haven't found a simple solution to this so I was wondering if anyone could point me in the right direction.

    I like the simple functionality of this component, but I would like to have the default printing mode be in Landscape mode instead of the Regular mode.  Is there anyway to do this?

    Thanks for the help,

    Dave

  • Is there a way to set preview's zoom? It is very small

  • Hi, windowscoder,

    It's been a few months since I've looked at this code area.  I'll take another peek tomorrow morning (busy day today!) and will get back to you tomorrow.

    --Matt--*

  • I had a quick break in the action, so I took a look.  There is a Zoom dropdown next to the printer button (as you probably know), but there does not appear to be a way to preset it via automation -- it requires user-action to change the setting.

    I also noticed that there was another comment posted by Dave over a year ago that I regrettably missed seeing.  Unfortunately, because this doesn't use the document technology, you have no way to access a PageSettings object to preset the Landscape property.  (And since Landscape would be false by default, the PrinterSettings's LandscapeAngle Property is of no use.)  So my apologies to Dave for both the tardiness of this reply as well as for the answer itself.

    --Matt--*

  • I need to PrintForm a Windows form to three different network printers using Visual Studio 2008. Can you help?

    Thank you.

  • Hi, Steve,

     I think you mean being able to print to three different network printers simultaneously, yes, without the dialog?  That's not something I've done before.  I suspect that you could leverage the System.Drawing.Printing namespace to do this.  It's relatively complicated (which is why the PowerPack created this wrapper that I discussed in the blog) -- see http://msdn.microsoft.com/en-us/library/system.drawing.printing.aspx for a start.

    --Matt--*

  • Im new to .Net so apologies if my question sounds silly. Can i use this to print a webform or is only for desktop applications. Are there any equivalent for printing from the web bypassing the print dialog, so you hit a button and it prints ?

  • It's not a silly question at all!  This is a WinForm component meant for desktop applications.  Code-wise, bypassing the dialog for desktop printing would be pretty simple to implement -- simply don't call the Me.VBPBNPrintDialog.ShowDialog line, and instead populate PrinterSettings with whatever you want the defaults to be, rather than asking the user for them, before calling PrintAction & Print.

    I'm not knowledgeable about web printing, alas -- generally, I just use the print button supplied by Internet Explorer to print out a screen from a web app.

    Hope this helps!

     --Matt--*

  • Thanks VB team for the clarification. Our ASP.Net Web application provides option to print labels for items and the users print like > 100 labels / day. So it is not a good thing for the user to click the Print option from the browser, which throws a print dialog and you click print again for every label/label batch. We currently have a VB6.0 desktop application which prints directly to the printer without going through the print dialog and we're scratching our heads to implement a similar one for our web app. Although you said you are not knowledgeable about web printing ..appreciate any ideas/suggestions. Thanks in advance.

  • I mean it -- I'm really not knowledgeable about web apps. :-)  (I'm speaking for me personally, not the VB team.  I'm not actually on the VB team anymore, although they graciously allow me a soapbox here.)  The #1 place to get help on web applications is http://www.asp.net, which is run by the web guys here.

    --Matt--*

  • Hi, Good article. But i feel a problem with the control. Whenever i am putting an image over a group box and try to print the client area then i can see only the group box and the picture is not there. Should i have to set the z-index property. I already used Send to Back function and it is working fine in the program but not working when i am trying to print. A solution will be appreciated greatly. Thank you in advance.

  • Hi, Minhajul,

     As noted above, I'm not on the VB team anymore, but if you file a bug at http://connect.microsoft.com against the VB.NET product, then the bug will enter their database and someone can follow up with you on your specific printing issue.

    --Matt--*

  • I searched the web for two days looking for a solution to setting landscape and margins with PrintForm.

    Pieced together what I found and I just came up with this solution:

                   Dim psd As New PageSetupDialog

                   psd.PrinterSettings = New System.Drawing.Printing.PrinterSettings

                   psd.PageSettings = New System.Drawing.Printing.PageSettings

                   If psd.ShowDialog = Windows.Forms.DialogResult.OK Then

                       If psd.PrinterSettings.IsValid Then

                           GenericPrintForm.PrinterSettings = psd.PrinterSettings

                           GenericPrintForm.PrinterSettings.DefaultPageSettings.Margins = New System.Drawing.Printing.Margins(psd.PageSettings.Margins.Left, psd.PageSettings.Margins.Right, psd.PageSettings.Margins.Top, psd.PageSettings.Margins.Bottom)

                       End If

                   End If

                   GenericPrintForm.PrintAction = Printing.PrintAction.PrintToPreview

                   GenericPrintForm.Print(CType(Me.ActiveMdiChild, Scorecard), PowerPacks.Printing.PrintForm.PrintOption.Scrollable)

                   psd.Dispose()

  • Thanks for sharing, Dave!

    --Matt--*

Page 1 of 2 (22 items) 12