📄 ch09.htm
字号:
set of favorite interfaces.</P>
<P>The interfaces defined by the Document/View architecture represent guarantees
about how each of the MFC classes that make up an application behave with regard
to each other. For example, the MFC framework always calls the <TT>CDocument::OnNewDocument</TT>
function when a new document is created. The MFC framework, and other classes that
might be part of an MFC-based program, expect the new document to be initialized
after this function has been called.</P>
<P>Using well-defined interfaces like <TT>CDocument::OnNewDocument</TT> to perform
specific tasks enables you to modify only the functions where you must take special
action; you can let the MFC framework handle most functions and interfaces if you
want the default behavior.</P>
<P>The Document/View architecture also makes it easy to separate work. For example,
data belongs only to the document; a view calls the <TT>GetDocument</TT> function
to collect a document pointer and then uses member functions to collect or update
data.
<H3><FONT COLOR="#000077"><B>Creating a Data Model</B></FONT></H3>
<P>Each of the interfaces discussed earlier has a specific role. For the remaining
examples in this chapter, you will use the DVTest example created earlier in this
hour.</P>
<P>Return to the DVTest example and add a <TT>CArray</TT> template object to the
document class as a private data member. Add the source code from Listing 9.11 to
the <TT>CDVTestDoc</TT> class header, found in the <TT>DVTestDoc.h</TT> file. Add
the source code to the attributes section of the class declaration, which begins
with the <TT>// Attributes</TT> comment generated by AppWizard.
<H4><FONT COLOR="#000077">TYPE: Listing 9.11. Changes to the CDVTestDoc class declaration.</FONT></H4>
<PRE><FONT COLOR="#0066FF"><TT>// Attributes</TT>
<TT>public:</TT>
<TT> CString GetName( int nIndex ) const;</TT>
<TT> int AddName( const CString& szName );</TT>
<TT> int GetCount() const;</TT>
<TT>private:</TT>
<TT> CArray<CString, CString> m_arNames;</TT>
</FONT></PRE>
<P>Because the <TT>CDVTestDoc</TT> class contains a <TT>CArray</TT> member variable,
the template collection declarations must be included in the project. Add an <TT>#include</TT>
statement at the bottom of the <TT>StdAfx.h</TT> file.</P>
<PRE><FONT COLOR="#0066FF"><TT>#include "afxtempl.h"</TT>
</FONT></PRE>
<P>The next step is to implement the functions described in the <TT>CDVTestDoc</TT>
class interface. These functions provide access to the data stored in the document.
Add the source code in Listing 9.12 to the <TT>DVTestDoc.cpp</TT> file.
<H4><FONT COLOR="#000077">TYPE: Listing 9.12. New functions added to the CDVTestDoc
class.</FONT></H4>
<PRE><FONT COLOR="#0066FF"><TT>CString CDVTestDoc::GetName( int nIndex ) const</TT>
<TT>{</TT>
<TT> ASSERT( nIndex < m_arNames.GetSize() );</TT>
<TT> return m_arNames[nIndex];</TT>
<TT>}</TT>
<TT>int CDVTestDoc::AddName( const CString& szName )</TT>
<TT>{</TT>
<TT> return m_arNames.Add( szName );</TT>
<TT>}</TT>
<TT>int CDVTestDoc::GetCount() const</TT>
<TT>{</TT>
<TT> return m_arNames.GetSize();</TT>
<TT>}</TT>
</FONT></PRE>
<P>Every document class must specify some access functions to add and retrieve data.
The three functions in Listing 9.12 are typical access functions in that they do
not just expose the <TT>CArray</TT> template. The data could also be stored in another
type of collection. Storing the data in a <TT>CArray</TT> object is an implementation
detail that should not be of interest to users of the <TT>CDVTestDoc</TT> class.
This enables the internal implementation of <TT>CDVTestDoc</TT> to be changed in
the future, if necessary.
<H3><FONT COLOR="#000077"><B>Initializing a Document's Contents</B></FONT></H3>
<P>You create and initialize document objects in two different ways, depending on
the type of application using the document:
<UL>
<LI>A new MDI document object is created for every new document opened by the program.<BR>
<BR>
<LI>SDI programs create a single document object that is reinitialized each time
a new document is opened.
</UL>
<P>In most cases, the best place to perform any initialization is in the <TT>CDocument::OnNewDocument</TT>
member function. This function is provided with some default code inserted by AppWizard.
Edit the <TT>OnNewDocument</TT> function so it looks like the code provided in Listing
9.13.
<H4><FONT COLOR="#000077">TYPE: Listing 9.13. Changes to the CDVTestDoc::OnNewDocument
member function.</FONT></H4>
<PRE><FONT COLOR="#0066FF"><TT>BOOL CDVTestDoc::OnNewDocument()</TT>
<TT>{</TT>
<TT> TRACE( "CDVTest::OnNewDocument" );</TT>
<TT> if (!CDocument::OnNewDocument())</TT>
<TT> return FALSE;</TT>
<TT> m_arNames.RemoveAll();</TT>
<TT> m_arNames.Add( "Curly" );</TT>
<TT> m_arNames.Add( "Moe" );</TT>
<TT> m_arNames.Add( "Shemp" );</TT>
<TT> return TRUE;</TT>
<TT>}</TT>
</FONT></PRE>
<P>Listing 9.13 clears the contents of the <TT>m_arNames</TT> collection and adds
three new names.
<BLOCKQUOTE>
<P>
<HR>
<B> </B><FONT COLOR="#000077"><B>Time Saver:</B></FONT><B> </B>The <TT>TRACE</TT>
macro sends an output message to the compiler's debug window, which displays useful
information as the program executes.<BR>
In Listing 9.13, the <TT>TRACE</TT> macro will display a line of text when a new
document is created. It's a good idea to have your program provide tracing information
whenever an interesting event occurs.
<HR>
</BLOCKQUOTE>
<H3><FONT COLOR="#000077"><B>Getting the Document Pointer</B></FONT></H3>
<P>Every view is associated with only one document. When a view must communicate
with its associated document, the <TT>GetDocument</TT> function is used. If the view
is created by AppWizard, as <TT>CDVTestView</TT> is, the <TT>GetDocument</TT> function
returns a pointer to the proper document type. Listing 9.14 is a version of <TT>OnDraw</TT>
that uses <TT>GetDocument</TT> to retrieve a pointer to the <TT>CDVTestDoc</TT> class;
then it uses the pointer to collect the names contained in the document.
<H4><FONT COLOR="#000077">TYPE: Listing 9.14. Using GetDocument to fetch a document
pointer.</FONT></H4>
<PRE><FONT COLOR="#0066FF"><TT>void CDVTestView::OnDraw(CDC* pDC)</TT>
<TT>{</TT>
<TT> CDVTestDoc* pDoc = GetDocument();</TT>
<TT> ASSERT_VALID(pDoc);</TT>
<TT> // Calculate the space required for a single</TT>
<TT> // line of text, including the inter-line area.</TT>
<TT> TEXTMETRIC tm;</TT>
<TT> pDC->GetTextMetrics( &tm );</TT>
<TT> int nLineHeight = tm.tmHeight + tm.tmExternalLeading;</TT>
<TT> CPoint ptText( 0, 0 );</TT>
<TT> for( int nIndex = 0; nIndex < pDoc->GetCount(); nIndex++ )</TT>
<TT> {</TT>
<TT> CString szName = pDoc->GetName( nIndex );</TT>
<TT> pDC->TextOut( ptText.x, ptText.y, szName );</TT>
<TT> ptText.y += nLineHeight;</TT>
<TT> }</TT>
<TT>}</TT>
</FONT></PRE>
<P>There are three main parts to Listing 9.14:
<UL>
<LI>In the first part, the document pointer is retrieved using <TT>GetDocument</TT>.
The pointer value is validated using the <TT>ASSERT_VALID</TT> macro. You should
always use this macro after an old-style cast to ensure that the pointer is accurate.<BR>
<BR>
<LI>In the second part, the size of a line of text is calculated using the <TT>CDC::GetTextMetrics</TT>
function. This function fills the <TT>TEXTMETRICS</TT> structure with information
about the current font used by the device context. The <TT>tmHeight</TT> member is
the maximum height of a character, and the <TT>tmExternalLeading</TT> member is the
spacing between character lines. Adding these two values together results in a good
spacing value between displayed rows of text.<BR>
<BR>
<LI>Finally, the third part collects each name in turn from the document, using the
functions added earlier to the document class. After each line of text is displayed,
the <TT>ptText.x</TT> value is increased by the line spacing value calculated earlier.
</UL>
<P>Compile and run the DVTest project. DVTest displays the names stored in the document
class, as shown in Figure 9.6.</P>
<P><A NAME="06"></A><A HREF="06.htm"><B>Figure 9.6.</B> </A><I><BR>
DVTest displays three names in its view window.</I></P>
<P>In Hour 22, "Serialization," you'll learn how to save the document to
a file. In Hour 23, "Advanced Views," you'll extend DVTest to include multiple
views; one view will enable you to add names to the document.
<H2><FONT COLOR="#000077"><B>Summary</B></FONT></H2>
<P>In this hour you've learned about pointers and references, as well as the basic
Document/View architecture used in most MFC programs. You learned how to use AppWizard
and ClassWizard in SDI and MDI applications, and created a sample program demonstrating
the use of Document/View.
<H2><FONT COLOR="#000077"><B>Q&A</B></FONT></H2>
<DL>
<DD><B>Q When I use pointers, sometimes I get an Unhandled Exception error from Windows
and my program crashes. The code that causes the problem looks something like this:</B>
</DL>
<BLOCKQUOTE>
<PRE><FONT COLOR="#0066FF"><TT>int *pBadInt;</TT>
<TT>*pBadInt = 42; // Error here</TT></FONT></PRE>
</BLOCKQUOTE>
<PRE><FONT COLOR="#0066FF"><TT></TT></FONT></PRE>
<DL>
<DD><B>A</B> There are two problems. First, the pointer isn't initialized. You should
always initialize a pointer to either <TT>NULL</TT> or to an area of memory that
is dynamically allocated, like this:
</DL>
<BLOCKQUOTE>
<PRE><FONT COLOR="#0066FF"><TT>int *pInt = NULL;</TT>
<TT>int *pInt = new int;</TT></FONT></PRE>
</BLOCKQUOTE>
<PRE><FONT COLOR="#0066FF"><TT></TT></FONT></PRE>
<DL>
<DD>A pointer doesn't automatically set aside any storage area--in the preceding
code, <TT>pBadInt</TT> is uninitialized and is pointing to a random area of memory.
You must assign the pointer either an address of an existing variable or the address
of a block of dynamically allocated memory:
</DL>
<BLOCKQUOTE>
<PRE><FONT COLOR="#0066FF"><TT>int n;</TT>
<TT>int *pInt;</TT>
<TT>pInt = &n;</TT>
<TT>*pInt = 42; // Okay</TT>
<TT>pInt = new int;</TT>
<TT>*pInt = 42; // Okay</TT>
<TT>delete pInt;</TT></FONT></PRE>
</BLOCKQUOTE>
<PRE><FONT COLOR="#0066FF"><TT></TT></FONT></PRE>
<DL>
<DD><B>Q I don't get all this Document/View stuff. Wouldn't it be easier to store
all the data in the view class?</B><BR>
<BR>
<B>A</B> It might seem easier at first. However, the MFC framework will provide a
great deal of help for free if you follow the Document/View rules. For example, if
you try to store your data in your view class, it will be very difficult to provide
multiple views for the same document. As you will see in Hour 23, it's fairly straightforward
if you follow the Document/View model. Also, as you will see in Hour 22, MFC gives
you a great deal of support for loading and storing data stored in your document
classes.
</DL>
<H2><FONT COLOR="#000077"><B>Workshop</B></FONT></H2>
<P>The Workshop is designed to help you anticipate possible questions, review what
you've learned, and begin thinking ahead to putting your knowledge into practice.
The answers to the quiz are in Appendix B, "Quiz Answers."
<H3><FONT COLOR="#000077"><B>Quiz</B></FONT></H3>
<DL>
<DD>1. What is the <TT>sizeof</TT> operator used for?<BR>
<BR>
2. What are some of the differences between pointers and references?<BR>
<BR>
3. What is more efficient to pass as a parameter--a pointer or an object? Why?<BR>
<BR>
4. What keyword is used to dynamically allocate memory?<BR>
<BR>
5. What keyword is used to release dynamically allocated memory?<BR>
<BR>
6. In the Document/View architecture, which classes are responsible for maintaining
the user interface?<BR>
<BR>
7. What are the four main categories of classes in the Document/View architecture?<BR>
<BR>
8. What part of the Document/View architecture is responsible for the application's
data?<BR>
<BR>
9. What <TT>CView</TT> member function is used to retrieve a pointer to the document
associated with the view?<BR>
<BR>
10. What <TT>CDocument</TT> member function is used to notify a document's views
that their user interface might need to be updated?
</DL>
<H3><FONT COLOR="#000077"><B>Exercises</B></FONT></H3>
<DL>
<DD>1. Use the <TT>TRACE</TT> macro to see when the view requests information from
the document. Add a <TT>TRACE</TT> macro to the document's <TT>GetName</TT> function,
and experiment with resizing and moving the view to see when the view requests the
data.<BR>
<BR>
2. Modify the DVTest project so that a line number is displayed for each item in
the view.<FONT COLOR="#000077"></FONT>
</DL>
<CENTER>
<P>
<HR>
<A HREF="../ch08/ch08.htm"><IMG SRC="../button/previous.gif" WIDTH="128" HEIGHT="28"
ALIGN="BOTTOM" ALT="Previous chapter" BORDER="0"></A><A HREF="../ch10/ch10.htm"><IMG
SRC="../button/next.gif" WIDTH="128" HEIGHT="28" ALIGN="BOTTOM" ALT="Next chapter"
BORDER="0"></A><A HREF="../index.htm"><IMG SRC="../button/contents.gif" WIDTH="128"
HEIGHT="28" ALIGN="BOTTOM" ALT="Contents" BORDER="0"></A> <BR>
<BR>
<BR>
<IMG SRC="../button/corp.gif" WIDTH="284" HEIGHT="45" ALIGN="BOTTOM" ALT="Macmillan Computer Publishing USA"
BORDER="0"></P>
<P>© <A HREF="../copy.htm">Copyright</A>, Macmillan Computer Publishing. All
rights reserved.
</CENTER>
</BODY>
</HTML>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -