Working with the PrintDocument Object

   

Working with the PrintDocument Object

The System::Drawing::Printing::PrintDocument class is the basis for all document printing within the .NET Framework. It manages the document page printing, settings for the document (such as margins), and the Graphics object for the printing device. However, to use the class, you have to do more than just call the function. After the function is called, your application must then handle various events that are fired by using delegate functions within your class.

Providing Simple Printing

As you start working with printing code, you might see some similarities with using GDI or GDI+. The reason for this is that the hardware that each of these methods use is also similar. GDI+ uses the monitor to render graphics, whereas printing uses a printer which prints onto a piece of paper. If you had to display a rectangle on the screen, why should the method be any different for displaying a rectangle on the printed page? With printing, of course, there are a few extra things that you need to work with, such as paper sizes, color depth, and margins.

Underneath the hood is an object called the device context. There is a certain device context for a video card and a certain device context for a printer. Once your application obtains access to that device context, you can call the same GDI+ functions regardless of whether you're using a printer or a display adapter.

Initially providing the printing functionality to simply send the contents of the MDIChildForm to the printer is done by creating a PrintDocument object and adding the printing functionality to the PrintForm() method.

The PrintDocument object provides events for when printing begins and ends as well as for when a page is printed. At a minimum, an application handles the PrintPage event to provide the basic printing content of the report. For this application, you're going to create a PrintDocument object as a member variable in your class. Add a variable named m_pPrintDocument to the protected section of your class declaration. The best place to create this object and hook up the event handler is within the InitForm function. Add the following code to your InitForm function:

 m_pPrintDocument = new PrintDocument; m_pPrintDocument->add_PrintPage(new PrintPageEventHandler(this,OnPrintPage)); 

The PrintForm() method receives the MDIChildForm that is currently active. In order to print the contents of the form, a method needs to be defined to retrieve the text contents. This is easily done by adding the following public method declaration and implementation to the MDIChildForm class:

 String* GetPrintText() { return( m_pRichTextBox->get_Text() ); } 

The PrintForm() method calls the PrintDocument object's Print() method to start the printing, as shown in Listing 10.5. You'll notice that the Print method is inside a try block and is followed immediately by a catch block with an error message. If you don't have a printer installed or you have a printer installed but it is currently unavailable, you will get an exception and your program will cease to function. By adding the try/catch block, you can avoid the application fault and continue. Error handling like this will be covered in Hour 13, "Working with .NET Error Handling and Diagnostics." The printing process is then started, and the OnPrintPage() method is called to handle the PrintPage event raised from the PrintDocument object.

Listing 10.5 The PrintForm() Method Implementation
 1: void MDIWindowFrame::PrintForm( MDIChildForm* pChildForm )  2: {  3:    // Make sure that there was a form provided  4:    if ( !pChildForm )  5:       return;  6:  7:    // Get the text stream to print  8:    m_sPrintText = pChildForm->GetPrintText();  9:    m_nPrintPos  = 0; 10: 11:    // Initialize the font to use for printing 12:    m_pPrintFont = new Drawing::Font( "Arial", 10 ); 13: 14:    // Cause the document to be printed 15:    try 16:    { 17:        m_pPrintDocument->Print(); 18:    } 19:    catch(Exception* /* e */ ) 20:    { 21:        MessageBox(NULL, "Printing Failed", "", MB_OK ); 22:    } 23: } 

The m_sPrintText member stores the contents of the window that is being printed. The m_nPrintPos member stores the position within the string that is currently being printed. Finally, the m_pPrintFont member is the font used for all the printing. These members are declared as protected members of the MDIWindowFrame class with the following statements added to the class declaration:

 String*         m_sPrintText; Drawing::Font*  m_pPrintFont; int             m_nPrintPos; 

The OnPrintPage() method is defined in Listing 10.6 and shows how to process the text contained in the m_sPrintText member for multiple lines. The code doesn't account for a single line being too long, but that could be added with word-wrapping logic.

Listing 10.6 The OnPrintPage() Method Implementation
 1: void MDIWindowFrame::OnPrintPage(  2:    Object* pSender, PrintPageEventArgs* pArgs )  3: {  4:    Graphics* gr = pArgs->get_Graphics();  5:  6:    int nPos;  7:    int nYPos  = pArgs->MarginBounds.get_Top();  8:    int nCount = 0;  9:    int lpp = pArgs->MarginBounds.get_Height() 10:        / (int)m_pPrintFont->GetHeight(gr); 11: 12:    // While there are more lines to print and the end of the page 13:    // has not been reached, keep printing. 14:    while( m_nPrintPos < m_sPrintText->get_Length() && nCount < lpp ) 15:    { 16:       // Find the first line break 17:       nPos = m_sPrintText->IndexOf( "\n", m_nPrintPos ); 18: 19:       // If no line break, set to end of line 20:       if ( nPos < 0 ) nPos = m_sPrintText->get_Length()-1; 21: 22:       // If anything needs to be printed, do it 23:       if ( nPos > m_nPrintPos ) 24:          gr->DrawString( m_sPrintText->Substring 25:                           (m_nPrintPos, nPos-m_nPrintPos), 26:                            m_pPrintFont, Brushes::Black, 27:                           (float)pArgs->MarginBounds.get_Left(), 28:                           (float)nYPos, new StringFormat ); 29: 30:       // Increment the print position and line number 31:       m_nPrintPos = nPos+1; 32:       nCount++; 33:       nYPos += (int)m_pPrintFont->GetHeight(gr); 34:    } 35: 36:    // If more lines are available to print, indicate that another page is needed 37:    if ( m_nPrintPos < m_sPrintText->get_Length() ) 38:       pArgs->HasMorePages = true; 39:    else 40:    { 41:       pArgs->HasMorePages = false; 42:       m_nPrintPos = 0; 43:    } 44: } 

Compiling and running the application at this point provides basic printing functionality without the page setup, print preview, or printer selection. The output is simply sent to the default system printer.

Selecting the Printer

Allowing the user to select which printer to use when printing is done by using the PrintDialog class, which displays the common dialog shown in Figure 10.1. By tying the PrintDocument object to the PrintDialog object, any settings the user makes in the Print dialog are reflected in the PrintDocument object. Listing 10.7 shows the modifications required in the PrintForm() method to display and use the PrintDialog object.

Figure 10.1. The Print common dialog.

graphics/10fig01.jpg

Listing 10.7 The PrintForm() Modifications to Use the PrintDialog Object to Allow the User to Select a Printer
 1: void MDIWindowFrame::PrintForm( MDIChildForm* pChildForm )  2: {  3:    // Make sure that there was a form provided  4:    if ( !pChildForm )  5:       return;  6:  7:    // Get the text stream to print  8:    m_sPrintText = pChildForm->GetPrintText();  9:    m_nPrintPos  = 0; 10: 11:    // Initialize the font to use for printing 12:    m_pPrintFont = new Drawing::Font( "Arial", 10 ); 13: 14:    PrintDialog* pPrintDlg = new PrintDialog; 15: 16:    // Assign the PrintDocument object to the PrintDialog 17:    pPrintDlg->Document = m_pPrintDocument; 18: 19:    // If the user selected OK, print 20:    if ( pPrintDlg->ShowDialog() == DialogResult::OK ) 21:    { 22:        try 23:        { 24:            m_pPrintDocument->Print(); 25:            } 26:            catch(Exception* /* e */ ) 27:            { 28:                MessageBox(NULL, "Printing Failed", "", MB_OK ); 29:            } 30:    } 31: } 

Changing the Page Setup

Displaying a Page Setup dialog, as shown in Figure 10.2, allows the user to change the page setup for printing. The user can change the page orientation and margins along with the paper size. On line 17 of Listing 10.7, you see that the PrintDialog has a property named Document that is a PrintDocument object. This is set to the PrintDocument object that you are using for your application. By doing this, any changes that a user makes within the Printer Setup dialog will change the various properties within your PrintDocument object.

Figure 10.2. The Page Setup common dialog.

graphics/10fig02.jpg

Displaying the Page Setup dialog is done with a PageSetupDialog object. The settings are stored in a PageSettings object that is then passed to the PrintDocument object before printing so it can adjust the pages accordingly. Because the user selections for the page settings should be for the life of the program, the PageSettings object is declared as protected in the class definition of the MDIWindowFrame class with the following statement:

 PageSettings*   m_pPageSettings; 

Add a statement to the InitForm() method to initialize the m_pPageSettings member to 0. Doing so provides a method to determine whether the user has set the page settings or the default page settings should be used. When the user first displays the Page Setup dialog, the m_pPageSettings member is initialized and used.

Listing 10.9 shows the implementation of the OnPageSetup() method, which is called when the user selects the Page Setup menu item.

Listing 10.9 The OnFilePageSetup() Method Implementation
 1: void MDIWindowFrame::OnFilePageSetup( Object* pSender, EventArgs* pArgs )  2: {  3:    PageSetupDialog* pPageSetupDlg = new PageSetupDialog;  4:  5:    // If the user hasn't set pages setting yet, initialize the object  6:    if ( !m_pPageSettings )  7:       m_pPageSettings = new PageSettings;  8:  9:    pPageSetupDlg->PageSettings = m_pPageSettings; 10:    pPageSetupDlg->ShowDialog(); 11: } 

Before you display the Printer Setup dialog when you want to print, you need to first initialize its properties with those found in the Page Setup dialog. The PrintDocument class has a property named DefaultPageSettings that you set from your m_pPageSettings variable. Therefore, before the setup dialog is shown, the properties that the user had chosen earlier get reflected into the Printer Setup dialog. Add the following code to the PrintForm method immediately preceding the code for launching the Printer Setup dialog box:

 // Assign the page settings if present if ( m_pPageSettings )     m_pPrintDocument->DefaultPageSettings = m_pPageSettings; 

Providing a Print Preview

The Print Preview option is the final option missing from this application. The .NET Framework provides virtually all the functionality for the print preview in the PrintPreviewDialog object. As was mentioned earlier when the similarities were discussed with display adapters and printers, you'll notice these similarities when working with the Print Preview methods and with the methods used to print. Rather than print to a physical page, the Print Preview window simply routes the drawing calls to the display adapter instead. In other words, the underlying Graphics object contained by the PrintDocument object is shared between the print previewer and the physical printer. Listing 10.10 shows the OnFilePrintPreview() method implementation.

Listing 10.10 The OnFilePrintPreview() Method Implementation to Provide a Print Preview Dialog
 1: void MDIWindowFrame::OnFilePrintPreview( Object* pSender, EventArgs* pArgs )  2: {  3:    MDIChildForm* pChildForm =  4:    dynamic_cast<MDIChildForm*>(get_ActiveMdiChild());  5:  6:    // If now child window is active, nothing to print.  7:    if ( !pChildForm )  8:       return;  9: 10:    // Perform the same initialization as PrintForm() 11:    m_sPrintText = pChildForm->GetPrintText(); 12:    m_nPrintPos  = 0; 13:    m_pPrintFont = new Drawing::Font( "Arial", 10 ); 14: 15:    if ( m_pPageSettings ) 16:       m_pPrintDocument->DefaultPageSettings = m_pPageSettings; 17: 18:    PrintPreviewDialog* pPrintPreviewDlg = new PrintPreviewDialog; 19: 20:    pPrintPreviewDlg->Document = m_pPrintDocument; 21:    pPrintPreviewDlg->ShowDialog(); 22: } 

The functionality prior to displaying the Print Preview dialog is basically the same code as the PrintForm() method. All the same setup has to occur for the print preview to work.

Compiling and running the application now provides the Print Preview dialog shown in Figure 10.3 when the user selects the Print Preview menu option.

Figure 10.3. The Print Preview dialog.

graphics/10fig03.gif


   
Top


Sams Teach Yourself Visual C++. NET in 24 Hours
Sams Teach Yourself Visual C++.NET in 24 Hours
ISBN: 0672323230
EAN: 2147483647
Year: 2002
Pages: 237

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net