| Tenouk C & C++ | MFC Home | Printing & Print Preview 1 | Splitter Windows & Multiple Views 1 | Download | Site Index |





Module 13a:

Printing and Print Preview 2





Program examples compiled using Visual C++ 6.0 (MFC 6.0) compiler on Windows XP Pro machine with Service Pack 2. Topics and sub topics for this Tutorial are listed below:

  1. The MYMFC19 Example: A WYSIWYG Print Program...a continuation

  2. Reading the Printer Rectangle

  3. Template Collection Classes Revisited: The CArray Class

  4. The MYMFC20 Example: A Multi page Print Program



Remember "logical twips" from Module 4? We're going to use logical twips now to enlarge type on the display while keeping the printed text the same size. This requires some extra work because the CScrollView class wasn't designed for non-standard mapping modes. You will be changing the view's base class from CScrollView to CLogScrollView, which is a class that I created by copying and modifying the MFC code in ViewScrl.cpp. The files LogScrollView.h and LogScrollView.cpp links are given at the end of this Module.


Insert the CScrollView class into the project. Copy the files LogScrollView.h and LogScrollView.cpp from the given links to the project directory mfcproject\mymfc19 if you have not done so already. Choose Add To Project from the Project menu, and then choose Files from the submenu. Select the two new files and click OK to insert them into the project.


Adding the already available files into the project.


Figure 15: Adding the already available files into the project.


Selecting files to be included in the project.


Figure 16: Selecting files to be included in the project.


Edit the StringView.h header file. Add the following line at the top of the file:


#include "LogScrollView.h"


Visual C++ code segment


Listing 10.


Then change the line:


class CStringView : public CScrollView




class CStringView : public CLogScrollView


Visual C++ code segment


Listing 11.


Edit the StringView.cpp file. Globally replace all occurrences of CScrollView with CLogScrollView. You can use the Edit Replace menu for this task.


Using find and replace to replace all occurrences of CScrollView with CLogScrollView.


Figure 17: Using find and replace to replace all occurrences of CScrollView with CLogScrollView.


Then edit the OnInitialUpdate() function. Here is the edited code, which is much shorter:


void CStringView::OnInitialUpdate()



    CSize sizeTotal(m_rectPrint.Width(), -m_rectPrint.Height());




Visual C++ code segment


Listing 12.


Build and test the application again. Now the window text is larger.


Modified text size of the MYMFC19 printing program output.


Figure 18: Modified text size of the MYMFC19 printing program output.


Reading the Printer Rectangle


The MYMFC19 program prints in a fixed-size rectangle that's appropriate for a laser printer set to portrait mode with 8.5-by-11-inch (letter-size) paper. But what if you load European-size paper or you switch to landscape mode? The program should be able to adjust accordingly.

It's relatively easy to read the printer rectangle. Remember the CPrintInfo pointer that's passed to OnPrint()? That structure has a data member m_rectDraw that contains the rectangle in logical coordinates. Your overridden OnPrint() function simply stuffs the rectangle in a view data member, and OnDraw() uses it. There's only one problem: you can't get the rectangle until you start printing, so the constructor still needs to set a default value for OnDraw() to use before printing begins.

If you want the MYMFC19 program to read the printer rectangle and adjust the size of the scroll view, use ClassWizard to override OnPrint() and then code the function as follows:


void CStringView::OnPrint(CDC* pDC, CPrintInfo* pInfo)


    m_rectPrint = pInfo->m_rectDraw;

    SetLogScrollSizes(CSize(m_rectPrint.Width(), -m_rectPrint.Height()));

    CLogScrollView::OnPrint(pDC, pInfo);



Template Collection Classes Revisited: The CArray Class


In MYMFC16 in Module 10, you saw the MFC library CTypedPtrList template collection class, which was used to store a list of pointers to CStudent objects. Another collection class, CArray, is appropriate for the next example, MYMFC20. This class is different from CTypedPtrList in two ways. First, it's an array, with elements accessible by index, just like CStringArray in MYMFC19. Second, the array holds actual objects, not pointers to objects. In MYMFC20, the elements are CRect objects. The elements' class does not have to be derived from CObject, and indeed, CRect is not. As in MYMFC16, a typedef makes the template collection easier to use. We use the statement:


typedef CArray<CRect, CRect&> CRectArray;


to define an array class that holds CRect objects and whose functions take CRect reference parameters. It's cheaper to pass a 32-bit pointer than to copy a 128bit object. To use the template array, you declare an instance of CRectArray and then you call CArray member functions such as SetSize(). You can also use the CArray subscript operator to get and set elements. The template classes CArray, CList, and CMap are easy to use if the element class is sufficiently simple. The CRect class fits that description because it contains no pointer data members. Each template class uses a global function, SerializeElements(), to serialize all the elements in the collection. The default SerializeElements() function does a bitwise copy of each element to and from the archive. If your element class contains pointers or is otherwise complex, you'll need to write your own SerializeElements() function. If you wrote this function for the rectangle array (not required), your code would look like this:


void AFXAPI SerializeElements(CArchive& ar, CRect* pNewRects, int nCount)


    for (int i = 0; i < nCount; i++, pNewRects++)


        if (ar.IsStoring()) {

            ar << *pNewRects;


        else {

            ar >> *pNewRects;





When the compiler sees this function, it uses the function to replace the SerializeElements() function inside the template. This only works, however, if the compiler sees the SerializeElements() prototype before it sees the template class declaration. The template classes depend on two other global functions, ConstructElements() and DestructElements(). Starting with Visual C++ version 4.0, these functions call the element class constructor and destructor for each object. Therefore, there's no real need to replace them.


The MYMFC20 Project Example: A Multipage Print Program


In this example, the document contains an array of 50 CRect objects that define circles. The circles are randomly positioned in a 6-by-6-inch area and have random diameters of as much as 0.5 inch. The circles, when drawn on the display, look like two-dimensional simulations of soap bubbles. Instead of drawing the circles on the printer, the application prints the corresponding CRect coordinates in numeric form, 12 to a page, with headers and footers.


Run AppWizard to generate\mfcproject \mymfc20. Select Single Document, and accept the defaults for all the other settings. The options and the default class names are shown here.


MYMFC20 project summary.


Figure 19: MYMFC20 project summary.


Edit the StdAfx.h header file. You'll need to bring in the declarations for the MFC template collection classes. Add the following statement:


#include <afxtempl.h>








Visual C++ code segment


Listing 13.


Edit the mymfc20Doc.h header file. In the MYMFC19 example, the document data consists of strings stored in a CStringArray collection. Because we're using a template collection for ellipse rectangles, we'll need a typedef statement outside the class declaration, as shown here:


typedef CArray<CRect, CRect&> CRectArray;


Visual C++ code segment


Listing 14.


Next add the following public data members to the mymfc20Doc.h header file:



    enum { nLinesPerPage = 12 };

    enum { nMaxEllipses = 50 };

    CRectArray m_ellipseArray;


Visual C++ code segment


Listing 15.


The two enumerations are object-oriented replacements for #defines.


Edit the mymfc20Doc.cpp implementation file. The overridden OnNew() Document function initializes the ellipse array with some random values, and the Serialize() function reads and writes the whole array. AppWizard generated the skeletons for both functions. You don't need a DeleteContents() function because the CArray subscript operator writes a new CRect object on top of any existing one. Add the following code:


BOOL CMymfc20Doc::OnNewDocument()


    if (!CDocument::OnNewDocument())

        return FALSE;


    int n1, n2, n3;

    // Make 50 random circles

    srand((unsigned) time(NULL));



    for (int i = 0; i < nMaxEllipses; i++)


        n1 = rand() * 600 / RAND_MAX;

        n2 = rand() * 600 / RAND_MAX;

        n3 = rand() * 50  / RAND_MAX;

        m_ellipseArray[i] = CRect(n1, -n2, n1 + n3, -(n2 + n3));



    return TRUE;



Visual C++ code segment


Listing 16.


void CMymfc20Doc::Serialize(CArchive& ar)





Visual C++ code segment


Listing 17.


Edit the mymfc20View.h header file. Use ClassView to add the member variable and two function prototypes listed below. ClassView will also generate skeletons for the functions in mymfc20View.cpp.



    int m_nPage;



    void PrintPageHeader(CDC *pDC);

    void PrintPageFooter(CDC *pDC);


Adding a member variable using ClassView.


Figure 20: Adding a member variable using ClassView.


Adding a function prototype using ClassView.


Figure 21: Adding a function prototype using ClassView.


The m_nPage data member holds the document's current page number for printing. The private functions are for the header and footer subroutines. Edit the OnDraw() function in mymfc20View.cpp. The overridden OnDraw() function simply draws the bubbles in the view window. Add the code shown here:


void CMymfc20View::OnDraw(CDC* pDC)


    int i, j;


    CMymfc20Doc* pDoc = GetDocument();


    j = pDoc->m_ellipseArray.GetUpperBound();


    for (i = 0; i < j; i++)






Visual C++ code segment


Listing 18.


Insert the OnPrepareDC() function in mymfc20View.cpp. The view class is not a scrolling view, so the mapping mode must be set in this function. Use ClassWizard to override the OnPrepareDC() function:


Overriding the OnPrepareDC() function.


Figure 22: Overriding the OnPrepareDC() function.


And then add the following code:


void CMymfc20View::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo)





Visual C++ code segment


Listing 19.


Insert the OnPrint() function in mymfc20View.cpp. The CView default OnPrint() function calls OnDraw(). In this example, we want the printed output to be entirely different from the displayed output, so the OnPrint() function must take care of the print output without calling OnDraw(). OnPrint() first sets the mapping mode to MM_TWIPS, and then it creates a fixed-pitch font. After printing the numeric contents of 12 m_ellipseArray elements, OnPrint() deselects the font. You could have created the font once in OnBeginPrinting(), but you wouldn't have noticed the increased efficiency. Use ClassWizard to override the OnPrint() function, and then add the following code:


Inserting the OnPrint() function in mymfc20View.cpp.


Figure 23: Inserting the OnPrint() function in mymfc20View.cpp.


void CMymfc20View::OnPrint(CDC* pDC, CPrintInfo* pInfo)


    int        i, nStart, nEnd, nHeight;

    CString    str;

    CPoint     point(720, -1440);

    CFont      font;




    CMymfc20Doc* pDoc = GetDocument();

    // for PrintPageFooter's benefit

    m_nPage = pInfo->m_nCurPage;

    nStart = (m_nPage - 1) * CMymfc20Doc::nLinesPerPage;

    nEnd = nStart + CMymfc20Doc::nLinesPerPage;


    // 14-point fixed-pitch font

    font.CreateFont(-280, 0, 0, 0, 400, FALSE, FALSE,

                    0, ANSI_CHARSET, OUT_DEFAULT_PRECIS,


                    DEFAULT_PITCH | FF_MODERN, "Courier New");

                    // Courier New is a TrueType font

    CFont* pOldFont = (CFont*) (pDC->SelectObject(&font));



    nHeight = tm.tmHeight + tm.tmExternalLeading;

    for (i = nStart; i < nEnd; i++) {

        if (i > pDoc->m_ellipseArray.GetUpperBound()) {



        str.Format("%6d %6d %6d %6d %6d", i + 1,





        point.y -= nHeight;

        pDC->TextOut(point.x, point.y, str);






Visual C++ code segment


Listing 20.


Edit the OnPreparePrinting() function in mymfc20View.cpp. The OnPreparePrinting() function (whose skeleton is generated by AppWizard) computes the number of pages in the document and then communicates that value to the application framework through the SetMaxPage() function. Add the following code:


BOOL CMymfc20View::OnPreparePrinting(CPrintInfo* pInfo)


    CMymfc20Doc* pDoc = GetDocument();

    pInfo->SetMaxPage(pDoc->m_ellipseArray.GetUpperBound() / CMymfc20Doc::nLinesPerPage + 1);

    return DoPreparePrinting(pInfo);



Visual C++ code segment


Listing 21.


Insert the page header and footer functions in mymfc20View.cpp. These private functions, called from OnPrint(), print the page headers and the page footers. The page footer includes the page number, stored by OnPrint() in the view class data member m_nPage. The CDC::GetTextExtent function provides the width of the page number so that it can be right-justified. Add the code shown here:


void CMymfc20View::PrintPageHeader(CDC* pDC)


    CString str;


    CPoint point(0, 0);

    pDC->TextOut(point.x, point.y, "Bubble Report");

    point += CSize(720, -720);

    str.Format("%6.6s %6.6s %6.6s %6.6s %6.6s", "Index", "Left", "Top", "Right", "Bottom");

    pDC->TextOut(point.x, point.y, str);










Visual C++ code segment


Listing 22.


void CMymfc20View::PrintPageFooter(CDC* pDC)


    CString str;


    CPoint point(0, -14400); // Move 10 inches down

    CMymfc20Doc* pDoc = GetDocument();

    str.Format("Document %s", (LPCSTR) pDoc->GetTitle());

    pDC->TextOut(point.x, point.y, str);

    str.Format("Page %d", m_nPage);

    CSize size = pDC->GetTextExtent(str);

    point.x += 11520 - size.cx;

    pDC->TextOut(point.x, point.y, str); // right-justified



Visual C++ code segment


Listing 23.


Build and test the application. For one set of random numbers, the bubble view window looks something like this.


MYMFC20 program output in action.


Figure 24: MYMFC20 program output in action.


Each time you choose New from the File menu, you should see a different picture. In Print Preview, the first page of the output should look like this.


MYMFC20 Print Preview page.


Figure 25: MYMFC20 Print Preview page.


With the Print dialog, you can specify any range of pages to print.


MYMFC20 with Page Range setting for printing.


Figure 26: MYMFC20 with Page Range setting for printing.


MYMFC20 with multiple pages of the Print Preview.


Figure 27: MYMFC20 with multiple pages of the Print Preview.


The LogScrollView.h and LogScrollView.cpp files download.








Further reading and digging:

  1. MSDN MFC 7.0 class library online documentation.

  2. MSDN MFC 9.0 class library online documentation - latest version.

  3. Porting & Migrating your older programs.

  4. MSDN Library

  5. DCOM at MSDN.

  6. COM+ at MSDN.

  7. COM at MSDN.

  8. Windows data type.

  9. Win32 programming Tutorial.

  10. The best of C/C++, MFC, Windows and other related books.

  11. Unicode and Multi-byte character set: Story and program examples.



| Tenouk C & C++ | MFC Home | Printing & Print Preview 1 | Splitter Windows & Multiple Views 1 | Download | Site Index |