?? ch06.htm
字號:
the right as you expect, Y coordinates increase upward rather than downward. Moreover,
the default coordinates for the window are located in the lower-right quadrant of
the Cartesian coordinate system, as shown in Figure 6.5. Figure 6.6 shows the print
preview window when the application uses the MM_LOENGLISH mapping mode. When you
print the document, the rectangle is exactly 2 inches square because a unit is now
1/100 of an inch and the rectangle is 200 units square.</P>
<P><A HREF="javascript:popUp('06uvc05.gif')"><B>FIG. 6.5</B></A><B> </B><I>The MM_LOENGLISH
mapping mode's default coordinates derive from the Cartesian coordinate system.</I></P>
<P><A HREF="javascript:popUp('06uvc06.gif')"><B>FIG. 6.6</B></A><B> </B><I>The rectangle
to be printed matches the rectangle onscreen when you use MM_LOENGLISH as your mapping
mode.</I></P>
<H2><A NAME="Heading3"></A>Printing Multiple Pages</H2>
<P>When your application's document is as simple as Print1's, adding printing and
print previewing capabilities to the application is virtually automatic. This is
because the document is only a single page and requires no pagination. No matter
what you draw in the document window (except bitmaps), MFC handles all the printing
tasks for you. Your view's OnDraw() function is used for drawing onscreen, printing
to the printer, and drawing the print preview screen. Things become more complex,
however, when you have larger documents that require pagination or some other special
handling, such as the printing of headers and footers.</P>
<P>To get an idea of the problems with which you're faced with a more complex document,
modify Print1 so that it prints lots of rectangles--so many that they can't fit on
a single page. This will give you an opportunity to deal with pagination. Just to
make things more interesting, add a member variable to the document class to hold
the number of rectangles to be drawn, and allow the users to increase or decrease
the number of rectangles by left- or right-clicking. Follow these steps:</P>
<DL>
<DD><B>1. </B>Expand CPrint1Doc in ClassView, right-click it, and choose Add Member
Variable from the shortcut menu. The variable type is int, the declaration is m_numRects,
and the access should be public. This variable will hold the number of rectangles
to display.
<P>
<DD><B>2. </B>Double-click the CPrint1Doc constructor and add this line to it:
</DL>
<BLOCKQUOTE>
<PRE>m_numRects = 5;</PRE>
</BLOCKQUOTE>
<PRE></PRE>
<DL>
<DT></DT>
<DD>This line arranges to display five rectangles in a brand new document.
<P>
<DT></DT>
<DD><B>3. </B>Use ClassWizard to catch mouse clicks (WM_LBUTTONDOWN messages) by
adding an OnLButtonDown() function to the view class (see Figure 6.7).
<P>
<DT></DT>
<DD><B>4. </B>Click the Edit Code button to edit the new OnLButtonDown() function.
It should resemble Listing 6.1. Now the number of rectangles to be displayed increases
each time users click the left mouse button.
</DL>
<P><A HREF="javascript:popUp('06uvc07.gif')"><B>FIG. 6.7</B></A><B> </B><I>Use ClassWizard
to add the OnLButtonDown() function.</I></P>
<P>
<H4>Listing 6.1  print1View.cpp --CPrint1View::OnLButtonDown()</H4>
<PRE>void CPrint1View::OnLButtonDown(UINT nFlags, CPoint point)
{
CPrint1Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
pDoc->m_numRects++;
Invalidate();
CView::OnLButtonDown(nFlags, point);
</PRE>
<PRE>}
</PRE>
<DL>
<DT></DT>
<DD><B>5. </B>Use ClassWizard to add the OnRButtonDown() function to the view class,
as shown in Figure 6.8.
<P>
</DL>
<P><A HREF="javascript:popUp('06uvc08.gif')"><B>FIG. 6.8</B></A><B> </B><I>Use ClassWizard
to add the OnRButtonDown() function.</I></P>
<P>
<DL>
<DD><B>6. </B>Click the Edit Code button to edit the new OnRButtonDown() function.
It should resemble Listing 6.2. Now the number of rectangles to be displayed decreases
each time users right-click.
<P>
</DL>
<H4>Listing 6.2  print1View.cpp --CPrint1View::OnRButtonDown()</H4>
<PRE>void CPrint1View::OnRButtonDown(UINT nFlags, CPoint point)
{
CPrint1Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if (pDoc->m_numRects > 0)
{
pDoc->m_numRects--;
Invalidate();
}
CView::OnRButtonDown(nFlags, point);
</PRE>
<PRE>}
</PRE>
<DL>
<DT></DT>
<DD><B>7. </B>Rewrite the view's OnDraw() to draw many rectangles (refer to Listing
6.3). Print1 now draws the selected number of rectangles one below the other, which
may cause the document to span multiple pages. It also displays the number of rectangles
that have been added to the document.
<P>
</DL>
<H4>Listing 6.3  print1View.cpp --CPrint1View::OnDraw()</H4>
<PRE>void CPrint1View::OnDraw(CDC* pDC)
{
CPrint1Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
// TODO: add draw code for native data here
pDC->SetMapMode(MM_LOENGLISH);
char s[10];
wsprintf(s, "%d", pDoc->m_numRects);
pDC->TextOut(300, -100, s);
for (int x=0; x<pDoc->m_numRects; ++x)
{
pDC->Rectangle(20, -(20+x*200),
200, -(200+x*200));
}
</PRE>
<PRE>}
</PRE>
<P>When you run the application now, you see the window shown in Figure 6.9. The
window not only displays the rectangles but also displays the rectangle count so
that you can see how many rectangles you've requested. When you choose File, Print
Preview, you see the print preview window. Click the Two Page button to see the window
shown in Figure 6.10. The five rectangles display properly on the first page, with
the second page blank.</P>
<P><A HREF="javascript:popUp('06uvc09.gif')"><B>FIG. 6.9</B></A><B> </B><I>Print1
now displays multiple rectangles.</I></P>
<P><A HREF="javascript:popUp('06uvc10.gif')"><B>FIG. 6.10</B></A><B> </B><I>Five
rectangles are previewed properly; they will print on a single page.</I></P>
<P>Now, go back to the application's main window and click inside it three times
to add three more rectangles. Right-click to remove one. (The rectangle count displayed
in the window should be seven.) After you add the rectangles, choose File, Print
Preview again to see the two-page print preview window. Figure 6.11 shows what you
see. The program hasn't a clue how to print or preview the additional page. The sixth
rectangle runs off the bottom of the first page, but nothing appears on the second
page.</P>
<P>The first step is to tell MFC how many pages to print (or preview) by calling
the SetMaxPage() function in the view class's OnBeginPrinting() function. AppWizard
gives you a skeleton OnBeginPrinting() that does nothing. Modify it so that it resembles
Listing 6.4.</P>
<P><A HREF="javascript:popUp('06uvc11.gif')"><B>FIG. 6.11</B></A><B> </B><I>Seven
rectangles do not yet appear correctly on multiple pages.</I></P>
<P>
<H4>Listing 6.4  print1View.cpp --CPrint1View::OnBeginPrinting()</H4>
<PRE>void CPrint1View::OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo)
{
CPrint1Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
int pageHeight = pDC->GetDeviceCaps(VERTRES);
int logPixelsY = pDC->GetDeviceCaps(LOGPIXELSY);
int rectHeight = (int)(2.2 * logPixelsY);
int numPages = pDoc->m_numRects * rectHeight / pageHeight + 1;
pInfo->SetMaxPage(numPages);
</PRE>
<PRE>}
</PRE>
<P>OnBeginPrinting() takes two parameters: a pointer to the printer device context
and a pointer to a CPrintInfo object. Because the default version of OnBeginPrinting()
doesn't refer to these two pointers, the parameter names are commented out to avoid
compilation warnings, like this:</P>
<P>
<PRE>void CPrint1View::OnBeginPrinting(CDC* /*pDC*/ , CPrintInfo* /*pInfo*/)
</PRE>
<P>However, to set the page count, you need to access both the CDC and CPrintInfo
objects, so your first task is to uncomment the function's parameters.</P>
<P>Now you need to get some information about the device context (which, in this
case, is a printer device context). Specifically, you need to know the page height
(in single dots) and the number of dots per inch. You obtain the page height with
a call to GetDeviceCaps(), which gives you information about the capabilities of
the device context. You ask for the vertical resolution (the number of printable
dots from the top of the page to the bottom) by passing the constant VERTRES as the
argument. Passing HORZRES gives you the horizontal resolution. There are 29 constants
you can pass to GetDeviceCaps(), such as NUMFONTS for the number of fonts that are
supported and DRIVERVERSION for the driver version number. For a complete list, consult
the online Visual C++ documentation.</P>
<P>Print1 uses the MM_LOENGLISH mapping mode for the device context, which means
that the printer output uses units of 1/100 of an inch. To know how many rectangles
will fit on a page, you have to know the height of a rectangle in dots so that you
can divide dots per page by dots per rectangle to get rectangles per page. (You can
see now why your application must know all about your document to calculate the page
count.) You know that each rectangle is 2 inches high with 20/100 of an inch of space
between each rectangle. The total distance from the start of one rectangle to the
start of the next, then, is 2.2 inches. The call to GetDeviceCaps() with an argument
of LOGPIXELSY gives the dots per inch of this printer; multiplying by 2.2 gives the
dots per rectangle.</P>
<P>You now have all the information to calculate the number of pages needed to fit
the requested number of rectangles. You pass that number to SetMaxPage(), and the
new OnBeginPrinting() function is complete.</P>
<P>Again, build and run the program. Increase the number of rectangles to seven by
clicking twice in the main window. Now choose File, Print Preview and look at the
two-page print preview window (see Figure 6.12). Whoops! You obviously still have
a problem somewhere. Although the application is previewing two pages, as it should
with seven rectangles, it's printing exactly the same thing on both pages. Obviously,
page two should take up where page one left off, rather than redisplay the same data
from the beginning. There's still some work to do.</P>
<P><A HREF="javascript:popUp('06uvc12.gif')"><B>FIG. 6.12</B></A><B> </B><I>The Print1
application still doesn't display multiple pages correctly.</I></P>
<P>
<H2><A NAME="Heading4"></A>Setting the Origin</H2>
<P>To get the second and subsequent pages to print properly, you have to change where
MFC believes the top of the page to be. Currently, MFC just draws the pages exactly
as you told it to do in CPrint1View::OnDraw(), which displays all seven rectangles
from the top of the page to the bottom. To tell MFC where the new top of the page
should be, you first need to override the view class's OnPrepareDC() function.</P>
<P>Bring up ClassWizard and choose the Message Maps tab. Ensure that CPrintView is
selected in the Class Name box, as shown in Figure 6.13. Click CPrintView in the
Object IDs box and OnPrepareDC in the Messages box, and then click Add Function.
Click the Edit Code button to edit the newly added function. Add the code shown in
Listing 6.5.</P>
<P><A HREF="javascript:popUp('06uvc13.gif')"><B>FIG. 6.13</B></A><B> </B><I>Use ClassWizard
to override the OnPrepareDC() function.</I></P>
<P>
<H4>Listing 6.5  print1View.cpp --CPrint1View::OnPrepareDC()</H4>
<PRE>void CPrint1View::OnPrepareDC(CDC* pDC, CPrintInfo* pInfo)
</PRE>
<PRE>{ if (pDC->IsPrinting())
{
int pageHeight = pDC->GetDeviceCaps(VERTRES);
int originY = pageHeight * (pInfo->m_nCurPage - 1);
pDC->SetViewportOrg(0, -originY);
}
CView::OnPrepareDC(pDC, pInfo);
</PRE>
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -