[Previous] [Next]

A Bare-Bones Printing Application

The EZPrint application shown in Figure 13-2 demonstrates the minimum amount of work a document/view application must do to support printing and print previewing.

Click to view at full size.

Figure 13-2. The EZPrint application displaying a print preview.

An EZPrint "document" contains a blue circle 10 centimeters (1,000 units in the MM_LOMETRIC mapping mode) in diameter with a yellow interior. The application's File menu contains just four items: Print, Print Preview, Print Setup, and Exit. The Print and Print Preview commands are mapped to CView::OnFilePrint and CView::OnFilePrintPreview in CEZPrintView's message map, and the Print Setup command is mapped to CWinApp::OnFilePrintSetup in CEZPrintApp's message map. AppWizard performed all the message mapping. The Print command displays a Print dialog box in which the user can specify printing options such as the desired printer, the print range, and the number of copies. Print Preview puts the application in print preview mode. Print Setup displays a Print Setup dialog box. You can use the Print Setup dialog box to choose a printer, select a paper size, and specify the page orientation—portrait or landscape.

I used AppWizard to create the EZPrint project. In the Step 4 dialog box (shown in Figure 13-3), I checked the Printing And Print Preview box to add printing support. Checking this box prompts AppWizard to make three modifications to the code that it generates:

AppWizard's OnPreparePrinting function includes a call to DoPreparePrinting. Its OnBeginPrinting and OnEndPrinting functions do nothing, so you can delete them if you don't use them. I left them in, but EZPrint would work just as well without them. All of EZPrint's printing code is found in the view class, whose source code is reproduced in Figure 13-4.

Click to view at full size.

Figure 13-3. Using AppWizard to add printing and print previewing support.

There's not a lot to say about EZPrint's printing and print previewing capabilities other than that MFC does the bulk of the work. CEZPrintView::OnDraw renders all the output, regardless of whether that output is destined for the screen, a printer, or a print preview window. So that the circle will have the same proportions regardless of where it is drawn, OnDraw does all of its drawing using the MM_LOMETRIC mapping mode. That's important, because pixel-per-inch values for screens and printers are rarely the same. If you drew to the screen and the printer in the MM_TEXT mapping mode, the circle would be a lot smaller on a 600 dpi printer than it would be on the screen. To get WYSIWYG results, you'd have to scale the circle's height and width manually during printing and print previewing using ratios derived from pixel-per-inch counts for the screen and printer. Using a mapping mode in which logical units scale to physical distances rather than pixel counts allows the GDI to do the scaling and ensures that OnDraw can produce consistent results no matter where the output is rendered.

Figure 13-4. The EZPrint application.


// EZPrintView.h : interface of the CEZPrintView class

#if !defined(
#define AFX_EZPRINTVIEW_H__3A83FDED_A3E6_11D2_8E53_006008A82731__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

class CEZPrintView : public CView
protected: // create from serialization only

// Attributes
    CEZPrintDoc* GetDocument();

// Operations

// Overrides
    // ClassWizard generated virtual function overrides
    virtual void OnDraw(CDC* pDC);  // overridden to draw this view

    virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
    virtual BOOL OnPreparePrinting(CPrintInfo* pInfo);
    virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo);
    virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo);

// Implementation
    virtual ~CEZPrintView();
#ifdef _DEBUG
    virtual void AssertValid() const;
    virtual void Dump(CDumpContext& dc) const;


// Generated message map functions
       // NOTE - the ClassWizard will add and remove member functions here.
       //    DO NOT EDIT what you see in these blocks of generated code !

#ifndef _DEBUG  // debug version in EZPrintView.cpp
inline CEZPrintDoc* CEZPrintView::GetDocument()
    { return (CEZPrintDoc*)m_pDocument; }


// Microsoft Visual C++ will insert additional declarations 
// immediately before the previous line.

// !defined(
//     AFX_EZPRINTVIEW_H__3A83FDED_A3E6_11D2_8E53_006008A82731__INCLUDED_)


// EZPrintView.cpp : implementation of the CEZPrintView class
#include "stdafx.h"
#include "EZPrint.h"

#include "EZPrintDoc.h"
#include "EZPrintView.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;

// CEZPrintView


        // NOTE - the ClassWizard will add and remove mapping macros here.
        //    DO NOT EDIT what you see in these blocks of generated code!
    // Standard printing commands

// CEZPrintView construction/destruction



BOOL CEZPrintView::PreCreateWindow(CREATESTRUCT& cs)
    return CView::PreCreateWindow(cs);

// CEZPrintView drawing
void CEZPrintView::OnDraw(CDC* pDC)
    CPen pen (PS_SOLID, 50, RGB (0, 0, 255));
    CBrush brush (RGB (255, 255, 0));

    pDC->SetMapMode (MM_LOMETRIC);
    CPen* pOldPen = pDC->SelectObject (&pen);
    CBrush* pOldBrush = pDC->SelectObject (&brush);

    pDC->Ellipse (100, -100, 1100, -1100);

    pDC->SelectObject (pOldBrush);
    pDC->SelectObject (pOldPen);

// CEZPrintView printing

BOOL CEZPrintView::OnPreparePrinting(CPrintInfo* pInfo)
    return DoPreparePrinting(pInfo);

void CEZPrintView::OnBeginPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
    // TODO: add extra initialization before printing

void CEZPrintView::OnEndPrinting(CDC* /*pDC*/, CPrintInfo* /*pInfo*/)
    // TODO: add cleanup after printing

// CEZPrintView diagnostics

#ifdef _DEBUG
void CEZPrintView::AssertValid() const

void CEZPrintView::Dump(CDumpContext& dc) const

CEZPrintDoc* CEZPrintView::GetDocument() // non-debug version is inline
    return (CEZPrintDoc*)m_pDocument;
#endif //_DEBUG

// CEZPrintView message handlers

Black-and-White Print Previews

MFC's print preview support isn't perfect. EZPrint's preview page shows the circle in full-blown color even if the only printer attached to your PC is a black-and-white model. (Naturally, the circle will be printed in color if you print it on a color printer.) You can add a nice touch to your print preview code by doing your rendering in shades of gray if both the following conditions are true when OnPrint or OnDraw is called:

You can convert RGB color values into shades of gray with this formula:

r/g/b = (red * 0.30) + (green * 0.59) + (blue * 0.11)

The following statement creates a gray brush that simulates on the screen how yellow (RGB (255, 255, 0)) will look on a monochrome output device:

CBrush brush (RGB (227, 227, 227));

I got the value 227 by plugging the color components 255, 255, and 0 into the color conversion formula.

To see a simple demonstration of black-and-white print previewing, replace the lines

CPen pen (PS_SOLID, 50, RGB (0, 0, 255));
CBrush brush (RGB (255, 255, 0));

in EZPrint's CPrintView::OnDraw function with these:

BOOL bMono = (pDC->GetDeviceCaps (NUMCOLORS) == 2) &&
    (pDC->m_hDC != pDC->m_hAttribDC); // True only for preview mode.
CPen pen (PS_SOLID, 50, bMono ? RGB (28, 28, 28) : RGB (0, 0, 255));
CBrush brush (bMono ? RGB (227, 227, 227) : RGB (255, 255, 0));

Print previews will now be rendered in shades of gray when the default printer is a black-and-white model. Comparing m_hDC to m_hAttribDC is a sneaky way to detect print preview mode when CPrintInfo information isn't available.

The CHM file was converted to HTML by chm2web software.