📄 ch04.htm
字号:
};
#ifndef _DEBUG // debug version in App1View.cpp
inline CApp1Doc* CApp1View::GetDocument()
{ return (CApp1Doc*)m_pDocument; }
#endif
///////////////////////////////////////////////////////////////////////////
//{{AFX_INSERT_LOCATION}}
// Microsoft Visual C++ will insert additional declarations
// immediately before the previous line.
#endif // !defined(AFX_APP1VIEW_H__43BB481F_64AE_11D0_9AF3
</PRE>
<PRE>[ccc] _0080C81A397C__INCLUDED_)
</PRE>
<P>Near the top of the listing, you can see the class's public attributes, where
it declares the GetDocument() function as returning a pointer to a CApp1Doc object.
Anywhere in the view class that you need to access the document's data, you can call
GetDocument() to obtain a pointer to the document. For example, to add a CPoint object
to the aforementioned array of CPoint objects stored as the document's data, you
might use the following line:</P>
<P>
<PRE>GetDocument()->m_points[x] = point;
</PRE>
<P>You also can do this a little differently, of course, by storing the pointer returned
by GetDocument() in a local pointer variable and then using that pointer variable
to access the document's data, like this:</P>
<P>
<PRE>pDoc = GetDocument();
pDoc->m_points[x] = point;
</PRE>
<P>The second version is more convenient when you need to use the document pointer
in several places in the function, or if using the less clear GetDocument()->variable
version makes the code hard to understand.</P>
<BLOCKQUOTE>
<P>
<HR>
<strong>NOTE:</strong> In release versions of your program, the GetDocument() function is
inline, which means there is no performance advantage to saving the pointer like
this, but it does improve readability. Inline functions are expanded into your code
like macros, but offer type checking and other advantages, as discussed in Appendix
A. n
<HR>
</BLOCKQUOTE>
<P>Notice that the view class, like the document class, overrides a number of virtual
functions from its base class. As you'll soon see, the OnDraw() function, which is
the most important of these virtual functions, is where you paint your window's display.
As for the other functions, MFC calls PreCreateWindow() before the window element
(that is, the actual Windows window) is created and attached to the MFC window class,
giving you a chance to modify the window's attributes (such as size and position).
These two functions are discussed in more detail in Chapter 5, "Drawing on the
Screen." OnPreparePrinting() is used to modify the Print dialog box before it
displays for the user; the OnBeginPrinting() function gives you a chance to create
GDI objects like pens and brushes that you need to handle the print job; and OnEndPrinting()
is where you can destroy any objects you might have created in OnBeginPrinting().
These three functions are discussed in Chapter 6, "Printing and Print Preview."</P>
<BLOCKQUOTE>
<P>
<HR>
<strong>NOTE:</strong> When you first start using an application framework like MFC, it's
easy to get confused about the difference between an object instantiated from an
MFC class and the Windows element it represents. For example, when you create an
MFC frame-window object, you're actually creating two things: the MFC object that
has member functions and member variables, and a Windows window that you can manipulate
using the functions of the MFC object. The window element is associated with the
MFC class, but is also an entity unto itself. n
<HR>
</BLOCKQUOTE>
<H2><A NAME="Heading3"></A>Creating the Rectangles Application</H2>
<P>Now that you've had an introduction to documents and views, a little hands-on
experience should help you better understand how these classes work. In the steps
that follow, you build the Rectangles application, which demonstrates the manipulation
of documents and views. When you first run this application, it will draw an empty
window. Wherever you click in the window, a small rectangle will be drawn. You can
resize the window, or minimize and restore it, and the rectangles will be redrawn
at all the coordinates where you clicked, because Rectangles keeps an array of coordinate
points in the document and uses that array in the view.</P>
<P>First, use AppWizard to create the basic files for the Rectangles program, selecting
the options listed in the following table. (AppWizard is first discussed in Chapter
1, "Building Your First Windows Application." When you're done, the New
Project Information dialog box appears; it should look like Figure 4.1. Click the
OK button to create the project files.</P>
<P>
<TABLE BORDER="1">
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT"><B>Dialog Box Name</B></TD>
<TD ALIGN="LEFT"><B>Options to Select</B></TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">New Project </TD>
<TD ALIGN="LEFT">Name the project <B>recs</B> and set the project path to the directory into which
you want to store the project's files. Leave the other options set to their defaults. </TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">Step 1 </TD>
<TD ALIGN="LEFT">Select Single Document. </TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">Step 2 of 6 </TD>
<TD ALIGN="LEFT">Leave default settings. </TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">Step 3 of 6 </TD>
<TD ALIGN="LEFT">Leave default settings. </TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">Step 4 of 6 </TD>
<TD ALIGN="LEFT">Turn off all application features except Printing and Print </TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">Preview. </TD>
<TD ALIGN="LEFT"> <P>
</TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">Step 5 of 6 </TD>
<TD ALIGN="LEFT">Leave default settings. </TD>
</TR>
<TR ALIGN="LEFT" VALIGN="TOP">
<TD ALIGN="LEFT">Step 6 of 6 </TD>
<TD ALIGN="LEFT">Leave default settings. </TD>
</TR>
</TABLE>
</P>
<P><A HREF="javascript:popUp('04uvc01.gif')"><B>FIG. 4.1</B></A><B> </B><I>When you
create an SDI application with AppWizard, the project information summary confirms
your settings.</I></P>
<P>Now that you have a starter application, it's time to add code to the document
and view classes in order to create an application that actually does something.
This application will draw many rectangles in the view and save the coordinates of
the rectangles in the document.</P>
<P>Follow these steps to add the code that modifies the document class to handle
the application's data, which is an array of CPoint objects that determine where
rectangles should be drawn in the view window:</P>
<P>
<DL>
<DD><B>1. </B>Click the ClassView tab to display the ClassView in the project workspace
window at the left of the screen.
<P>
<DT></DT>
<DD><B>2. </B>Expand the recs classes by clicking the + sign before them.
<P>
<DT></DT>
<DD><B>3. </B>Right-click the CRecsDoc class and choose Add Member Variable from
the shortcut menu that appears.
<P>
<DT></DT>
<DD><B>4. </B>Fill in the Add Member Variable dialog box. For Variable Type, enter
<B>CPoint</B>. For Variable Name, enter <B>m_points[100]</B>. Make sure the Public
radio button is selected. Click OK.
<P>
<DT></DT>
<DD><B>5. </B>Again, right-click the CRecsDoc class and choose Add Member Variable.
<P>
<DT></DT>
<DD><B>6. </B>For Variable Type, enter <B>UINT</B>. For Variable Name, enter <B>m_pointIndex</B>.
Make sure the Public radio button is selected. Click OK.
<P>
<DT></DT>
<DD><B>7. </B>Click the + next to CRecsDoc in ClassView to see the member variables
and functions. The two member variables you added are now listed.
<P>
</DL>
<P>The m_points[] array holds the locations of rectangles displayed in the view window.
The m_pointIndex data member holds the index of the next empty element of the array.</P>
<BLOCKQUOTE>
<P>
<HR>
<strong>TIP:</strong> If you've programmed in C++ before and are not used to the ClassView,
you can open RecsDoc.h from the FileView and add (after a public: specifier) the
two lines of code that declare these variables:
<HR>
</BLOCKQUOTE>
<PRE>UINT m_pointIndex;
CPoint m_points[100];
</PRE>
<P>Now you need to get these variables initialized to appropriate values and then
use them to draw the view. MFC applications that use the document/view paradigm initialize
document data in a function called OnNewDocument(), which is called automatically
when the application first runs and whenever the user chooses File, New.</P>
<P>The list of member variables and functions of CRecsDoc should still be displayed
in ClassView. Double-click OnNewDocument() in that list to edit the code. Using Listing
4.3 as a guide, remove the comments left by AppWizard and initialize m_pointIndex
to zero.</P>
<P>
<H4>Listing 4.3  RECSDOC.CPP--CRecsDoc::OnNewDocument()</H4>
<PRE>BOOL CRecsDoc::OnNewDocument()
{
if (!CDocument::OnNewDocument())
return FALSE;
m_pointIndex = 0;
return TRUE;
</PRE>
<PRE>}
</PRE>
<P>There is no need to initialize the array of points because the index into the
array will be used to ensure no code tries to use an uninitialized element of the
array. At this point your modifications to the document class are complete. As you'll
see in Chapter 7, there are a few simple changes to make if you want this information
actually saved in the document. In order to focus on the way documents and views
work together, you will not be making those changes to the recs application.</P>
<P>Now turn your attention to the view class. It will use the document data to draw
rectangles onscreen. A full discussion of the way that drawing works must wait for
Chapter 5. For now it is enough to know that the OnDraw() function of your view class
does the drawing. Expand the CRecsView class in ClassView and double-click OnDraw().
Using Listing 4.4 as a guide, remove the comments left by AppWizard and add code
to draw a rectangle at each point in the array.</P>
<P>
<H4>Listing 4.4  RECSVIEW.CPP--CRecsView::OnDraw()</H4>
<PRE>void CRecsView::OnDraw(CDC* pDC)
{
CRecsDoc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
UINT pointIndex = pDoc->m_pointIndex;
for (UINT i=0; i<pointIndex; ++i)
{
UINT x = pDoc->m_points[i].x;
UINT y = pDoc->m_points[i].y;
pDC->Rectangle(x, y, x+20, y+20);
}
</PRE>
<PRE>}
</PRE>
<P>Your modifications to the starter application generated by AppWizard are almost
complete. You have added member variables to the document, initialized those variables
in the document's OnNewDocument() function, and used those variables in the view's
OnDraw() function. All that remains is to enable the user to add points to the array.
As discussed in Chapter 3, "Messages and Commands," you catch the mouse
message with ClassWizard and then add code to the message handler. Follow these steps:</P>
<P>
<DL>
<DT></DT>
<DD><B>1. </B>Choose View, ClassWizard. The ClassWizard dialog box appears.
<P>
<DT></DT>
<DD><B>2. </B>Make sure that CRecsView is selected in the Class Name and Object IDs
boxes. Then, double-click WM_LBUTTONDOWN in the Messages box to add the OnLButtonDown()
message-response function to the class. Whenever the application receives a WM_LBUTTONDOWN
message, it will call OnLButtonDown().
<P>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -