Preview and print from the Windows Form Application using .NET Printing Namespace
Release Date: 12/13/2004
| Update Date: 12/13/2004
Alex Calvo
This article is assumed to be familiar with C # and Windows Forms
Download this article: PrintingInnet.exe (134KB)
Summary
Print is part of each of the complete, Windows-based applications. Providing robust printing in these applications is usually identified as a boring bad thing. Now, using .NET Framework, printing from a Windows Form means that the document-centric approach must be used to generate a more neat and easier management code. Although System.Windows.Forms namespace provides seamless integration with all standard print dialogs (eg, "Print Preview", "Page Setup", "Print"), but system.drawing.printing namespace provides a lot of Class for expansion and customization. This article will discuss these classes and how they provide access to printing functions. This article also interprets other useful techniques (for example, printing in background to allow users to continue to complete other tasks).
This page
Using the PrintDocument class to achieve derived PrintDocument class with GDI printing the printController class using the print dialog box for background printing summary
From the development perspective, Microsoft.NET has changed almost all squares. Some of these changes (for example, web forms and ado.net) have requested major changes in the way of completing the task, while others have more slowed in nature, only in the prior art (eg SYSTEM). Some improvements were made on the basis of XML. For traditional developers using Visual Basic and Visual C , printing from a Windows Form indicates a major change. However, this change is undoubtedly better for a large number of cases where .NET Framework is used.
The days of using the Visual Basic Print object and its Printers collection are not returned. In the .NET Framework, there is no monocular print object, and you will never set the currentx and currenty attributes or send commands such as Enddoc and NewPage. If you use .NET from Visual C , you might aware that printing may be a boring task. For example, it requires you to use the Win32 API to carefully track the printing process to make sure the page is properly printed. This is not to say that you don't have to do things like this. Just say that through .NET, you will eventually get a more tidy and easier maintenance print logic. You can fully encapsulate the print code through the .NET Framework class. Because you can derive the code from a base class, you will get all kinds of additional features for free. Hook to the Print Preview dialog, for .NET is a small dish, this is just an example.
Most code examples in this article are extracted from sample print applications that can be downloaded from the top of this page. The example Windows Form Application shown in Figure 1 demonstrates many new features and functions that can be used when printing in .NET. It allows you to select any text document and send it to the Print Preview dialog or a specific printer. For demonstration purposes, I will provide you with an option for your choice to display watermark on each page.
Figure 1 Windows Form Application
When printing with the Print Preview dialog, you can enable or disable the elimination serration function - this is a built-in function, which makes the text and graphics rendered on the screen with a smoother look. However, remember that this is at the expense of the reduction in output speed. In addition, the Print Preview dialog automatically uses any font smoothing display provided by Windows, thereby reducing the need to use to eliminate serrated functions. When the output is sent to the printer, the sample print application also allows you to select a variety of other options. You can then decide to display the Status dialog, display the movie printer in the status bar, or print in the background thread. Let us experience a Windows form print by first analyzing the rapid and messy way to send the output to the printer. Then, I will strictly examine the correct way to print through the Windows Form - use the derived PrintDocument class.
Use the PrintDocument class
Printing from a Windows Form is a document-centered event drive process. Most of your energy will spend on using a universal PrintDocument object or implement derived PrintDocument class. Inheriting from the PrintDocument class class is a better way - I will soon explain. However, sometimes instances using the PrintDocument base class may be faster, simpler.
Printing with the PrintDocument base class requires the PrintPage event of this class to a certain handler method (static or instance) that is entrusted by the PrintPageEventHandler commission. This event will be excited when the code calls the Print method on the PrintDocument object instance. To actually draw a page, you can use the Graphics property of the PrintPageEventArgs object. An instance of the PrintPageEventargs class is passed to the PrintPage event handler as a parameter. The Graphics property of the PrintPageEventArgs object discloses a GDI object that encapsulates the drawing surface you use to draw a page. (This article will discuss some basic GDI commands later.) To print a page, you need to notify the Basic Print Controller You have more pages to print. This task can be done using the HASMOREPAGES attribute of the PrintPageEventArgs object. Setting the HasMorePages property to make sure that the PrintPage event handler is called again.
In addition, event handlers can be set for other common print events (eg, BeginPrint, EndPrint, and QuerypageSettings). BeGinprint is a nice choice for initializing any object (for example, fonts) that PRINTPAGE routines may depend on. The QueryPageSettings event is just excited before each PrintPage event. It allows you to use different page settings to print each page (you can get a different page settings by modifying the querypagesettingsingseventargs.pagesettings attribute). To modify the page settings of the entire document, you can use the defaultpagesettings attribute of the PrintDocument class.
The following example illustrates how to start print jobs using the PrintDocument base class:
PRINTDocument PrintDoc = New PrintDocument ();
PrintDoc.printpage = new printpageeventhandler (this.printdoc_printpage);
PRINTDOC.PRINT ();
// The PrintPage Event Is Raised for Each Page To Be Printed.private Void PrintDoc_printpage (Object Sender, PrintPageEventArgs E)
{
// Todo: Print Your Page Using The E.Graphics GDI Object
// Notify The PrintController WHether The ARE ANY More Pages
E.hasmorepages = false;
}
As you can see, there are many disadvantages. The biggest disadvantage is that you must maintain a conscious object between the continuous calls for the PrintPage event handler. For example, if you want to print a text document, you will need to maintain an open StreamReader object. You can initialize StreamReader during the BeginPrint event, and then close it during the EndPrint event. However, no matter how you split StreamReader variables, it is necessary to limit its scope to the outside of the PrintPage event handler together with other variables. When this happens, your print code will be disclosed and vulnerable, and it may make the rest of the code confusing.
Back to top
Realize the derived PrintDocument class
When printing from a Windows form, a better way is to implement a class that is inherited from a general PRINTDocument class. This allows you to get a reward of the package. There is no need to implement event handler for beginPrint, EndPrint, and PrintPage events, but to rewrite the foundation of the PrintDocument base class onbeginprint, oneundPrint, and onprintpage methods. Now, any status-aware objects used by the OnPrintPage method can remain in a private class field. This completely eliminates the potential code problem I just mentioned. In addition, you can now add custom properties, methods, events, and constructor to derived PrintDocument classes.
Example printing application uses the TEXTPRINTDocument type to derive the PrintDocument class, as shown in Figure 2. The TextPrintDocument class discloses an overload constructor using a file name as a parameter. Alternatively, you can use the custom filetoprint property setting and read the file name. This attribute will cause an exception when set to a file that does not exist. This class also discloses a common Boolean field called Watermark, which is used to enable or disable page background graphics. (Page background graphics as embedded resource named Watermark.gif in the program set.) Finally, the derived TextPrintDocument class discloses the Font property to specify which font is to use the correct font to render the page.
The internal mechanism of the TextPrintDocument class can be found in the onPrintPage method. Here, you can draw a page using the GDI draw surface provided by the PrintPageEventArgs Graphics property. In addition, the PrintPageEventArgs object contains the following properties: Cancel, HasmRepages, MarginBounds, PageBounds, and Pagesettings. You can cancel the print job through Cancel. The Marginbounds property returns a Rectangle object that represents the part of the inner page of the margin. You can use this rectangle to determine where you start and stop printing on each page. On the other hand, PageBounds represents the entire area of the page (including margins).
Back to top
With GDI , the details of printing GDI itself require an article to fully introduce, so I will only discuss GDI only from the TEXTPRINTDocument class how to present the angle of each page from the TEXTPRINTDOCUMENT class. Then, I will discuss some of the GDI calls you are likely during typical printing.
First, onPrintPage methods determine the height of the current font using the GetHeight method of the Font class. The GetHeight method can determine the current page per inch (DPI) by using a Graphics object as a parameter. Once the height of the current font is determined, the number of rows per page is calculated using the current marginbounds.height. Next, read the text file (one line) and print it with the DrawString method. If the page is reached before the end of the file is reached, the HASMOREPAGES property is set to true.
As you can see, just use DrawString to complete basic printing. However, GDI provides you with more than 15 ways of drawing, and each method can perform a lot of overload. You can print with vector graphics (eg, Drawbezier, Drawellipse), and raster graphics (for example, DrawImage, Drawicon). Note how the onprintpage method uses DrawImage to display watermarks. You can also use Many advanced features of GDI , such as clip and conversion. Try to complete the task with the Visual Basic Print object!
Back to top
PrintController class
Earlier, I mentioned the example print application (as shown in Figure 1) allows you to display an optional state dialog and / or an animation status bar icon (that is used to spit out of the page while printing). Both functions are implemented using derived print controller classes. PrintController is an abstract class that implements three different specific classes in the .NET Framework: StandardPrintController, PrintControllerWithStatusDialog, and PreviewPrintController.
The print controller is responsible for printing the print document. The PrintDocument class is disclosed to its base print controller. Call the printing document The print method triggers the onStartPrint, OneendPrint, onStartPage, and OneundPage method of the underlying print controller. Figure 3 shows the event sequence that occurs between the print document and the print controller. OnStartPage is a unique way to return values. The type of return value is Graphics, and as you may have already guessed, it passes the GDI drawing surface of the print document via the PrintPageEventArgs parameter.
Figure 3 Printing Process
The default print controller is PrintControllerWithStatusDialog. Therefore, if you want to close the Print Status dialog, you will need to use StandardPrintController. PreviewPrintController is used by PrintPreviewDialog and PrintPreviewControl class. PrintControllerWithStatusDialog is located in the System.Windows.Forms namespace, while StandardPrintController and PreviewPrintController are located under System.drawing.printing Namespace. PrintControllerWithStatusDialog provides an overloaded constructor that uses another print controller as a parameter. This allows you to combine PrintControllerWithStatusDialog to combine any other features you might add to your print controller. When running an example printing an application, try the PrintControllerWithStatusDialog and PrintControllerWithstatusbar checkbox to see how this is working. The following fragments illustrate how the code works: CustomPrintDocument PrintDoc = New
CustomPrintDocument ();
CustomPrintController PrintctL = New
CustomPrintController ();
PrintDoc.printController = New
PrintControllerwithStatusDialog
PrintCTL, "DILOG CAPTION");
PRINTDOC.PRINT ();
Example printing application uses a custom print controller for a PrintControllerWithStatusBar type (see Figure 4). PrintControllerWithStatusbar discloses a StatusBarPanel property that determines which status bar panel should display an animated printer icon. I use a SYSTEM.TIMERS.TIMER type timer to complete the actual animation. The System.Timers.Timer class is running well in multi-threaded applications (which belongs to printing in the background).
Back to top
Use print dialog
The advantage of printing in .NET is that the print document is perfect with the print dialog box. There are three different print dialogs in the System.Windows.Forms namespace: PrintDialog, PrintPreviewDialog, and PagesetupDialog. In addition, there is a PrintPreviewControl class that does not contain a surround dialog, providing greater UI design flexibility. For simple purposes, sample print applications use the PrintPreviewDialog class (see Figure 5).
Figure 5 Print preview
The PrintDialog class can be used to display a standard print dialog box in Windows, and enable the user to select the printer, specify the number to print and determine the number of copies. Use it as simple as shown below:
CustomPrintDocument PrintDoc = New CustomPrinTdocument ();
PrintDialog dlgprint = new printDialog ();
DLGPrint.document = printdoc;
IF (DLGPrint.ShowDialog () == DialogResult.ok)
{
PRINTDOC.PRINT ();
}
Using the PrintPreviewDialog class is even easier. I found that the Print Preview dialog is extremely useful during the development process. When commissioning the print document, it can save a lot of paper. Here is its working principle: CustomPrintDocument PrintDoc = New CustomPrintDocument ();
PRINTPREVIEWDIALOG DLGPRINTPREVIEW = New PrintPreviewDialog ();
// set Any Optional Properties of DlgPrintpreview Here ...
DLGPRINTPREVIEW.DOCUMENT = printdoc;
Dlgprintpreview.showdialog ();
After creating an instance of the PrintPreviewDialog class, you need to set its Document property to any instance of the class derived from the PrintDocument. The ShowDialog method of PrintPreviewClass will automatically process the presentation of the print preview of the document (see Figure 5). As you know, it is not more simple than this.
The PagesetupDialog class works in a similar manner. However, in addition to supporting the Document property, you can also choose to set the PageSettings or PrinterSettings property to the PageSettings or PrinterSettings class instance. The PageSettings class defines settings for the actual print page, such as Margins, and Papersize, and the PrinterSettings class specifies how to print documents, such as fromPage, Topage, and Printername. With the PrinterSettings class, you can also use the Installedprinters Static Method (which returns the PrinterSettings.StringCollection object) to get a list of available printers. . According to the return result from the Page Setup dialog, Document, PageSettings or PrinterSettings objects will be modified accordingly.
Back to top
Background printing
Thread processing is difficult to deal with, and I will never intend to deny this situation, that is, there is a problem that there is a problem when writing multithreaded applications. Despite this, the fact is that the use of the background thread can indeed improve the user's printing experience. It allows users to continue using the rest of the application while printing jobs in the background. To test background printing, it is best to use large documents and make sure the printer is suspended. Why waste paper? You can also reduce the print speed using the static SLEEP method of the Thread class. By the sample printing the application, you can try it when the output is sent to the printer. It is meaningless to use the background printing with the Print Preview dialog box. In short, the following is its way of working:
Private void cmdbackgroundprint_click (Object Sender, System.EventArgs E)
{
Thread T = New Thread (New ThreadStart (PrintinBackground);
T.isbackground = true;
T.Start ();
}
Private void printinbackground ()
{
PRINTDOC.PRINT ();
}
You can also use the commission to complete the same work. Initially, when I write the sample printing the application, I used the Thread class. However, because I want to get notifications when printing is completed, I decided to change and use the commission. By using the entrusted BeginInvoke method, you can specify a callback function so that the code can be notified when the asynchronous operation has been completed. I need this to do this so that the Print button is re-enabled after the print is complete (see Figure 6). Figure 6 contains most of the code used to drive the sample print application. In particular, it contains a delegate called PrintinBackgroundDelegate. If the Background thread check box is selected, the BeginInvoke method of the delegate will be called inside the Click event handler of the Print button. Otherwise, call the print method of the print document directly from the UI primary thread.
There will be most of the defects you may encounter, will involve the printed document class that may depend on status awareness. For example, as an example in the previous example, what happens if the user clicks CMDBACKGROUNDPRINT? The results are also unpredictable in optimal conditions. Because the PrintDocument.PrintPage events usually depend on the class field with status awareness, things are really cumbersome in similar situations. One simple way to deal with these kinds is to simply disable some UI controls to prevent the user from re-issue the same command. At the same time, they can continue to use the rest of the application. This is the method used by the sample print application. Before processed the background print, I suggest you read the article on Ian Griffiths on page 68 of this issue.
Back to top
summary
Although this article only discusses the NET's Native Windows Form Print, but .NET also provides other printer output methods, for example, Crystal Report. In some particular circumstances, the reporting tool may be more appropriate than this machine. However, if you don't want to pay the overhead of ready-made products, and your application needs to customize or dedicated print function (just like many of the capable Windows-based applications), then you must print method.
Related articles, see:
Safe, Simple Multithreading in Windows Forms
http://samples.gotdotnet.com/quickstart/winforms
Alex Calvo is a Microsoft .NET MCSD and MCAD. He is one of the joint founders of Developer Box Llc (http://www.develop. "The company is a consultation company in Connecticut, Connecticut, USA. Net, Visual Basic, COM and SQL Server consulting firm. You can contact him through acalvo@hotmail.com.
Go to the original English page