?? ch07.htm
字號:
everywhere there might be a change to the data. Finally, this code forces a redraw
of the screen by calling Invalidate(), as discussed in Chapter 4.</P>
<BLOCKQUOTE>
<P>
<HR>
<strong>TIP:</strong> If m_message was a private member variable of the document class,
you could have a public SetMessage() function that called SetModifiedFlag() and be
guaranteed no programmer would ever forget to call it. That's one of the advantages
of writing truly object-oriented programs.
<HR>
</BLOCKQUOTE>
<P>The document class's Serialize() function handles the saving and loading of the
document's data. Listing 7.4 shows the empty shell of Serialize() generated by AppWizard.</P>
<P>
<H4>Listing 7.4  FILEVIEW.CPP--The Document Class Serialize() Function</H4>
<PRE>void CFileDoc::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
// TODO: add storing code here
}
else
{
// TODO: add loading code here
}
</PRE>
<PRE>}
</PRE>
<P>Because the CString class (of which m_message is an object) defines the >>
and << operators for transferring strings to and from an archive, it's a simple
task to save and load the document class's data. Simply add this line where the comment
reminds you to add storing code:</P>
<P>
<PRE>ar << m_message;
</PRE>
<P>Add this similar line where the loading code belongs:</P>
<P>
<PRE>ar >> m_message;
</PRE>
<P>The << operator sends the CString m_message to the archive; the >>
operator fills m_message from the archive. As long as all the document's member variables
are simple data types such as integers or characters, or MFC classes such as CString
with these operators already defined, it's easy to save and load the data. The operators
are defined for these simple data types:</P>
<UL>
<LI>BYTE
<P>
<LI>WORD
<P>
<LI>int
<P>
<LI>LONG
<P>
<LI>DWORD
<P>
<LI>float
<P>
<LI>double
</UL>
<P>Build File Demo and run it. Choose Edit, Change Message, and you should see the
new string onscreen, as shown in Figure 7.1. Choose File, Save and enter a filename
you can remember. Now change the message again. Choose File, New and you'll be warned
about saving your current changes first, as in Figure 7.2. Choose File, Open and
browse to your file, or just find your filename towards the bottom of the File menu
to re-open it, and you'll see that File Demo can indeed save and reload a string.</P>
<P><A HREF="javascript:popUp('07uvc01.gif')"><B>FIG. 7.1</B></A><B> </B><I>File Demo
changes the string on command.</I></P>
<P><A HREF="javascript:popUp('07uvc02.gif')"><B>FIG. 7.2</B></A><B> </B><I>Your users
will never lose unsaved data again.</I></P>
<BLOCKQUOTE>
<P>
<HR>
<BR>
<strong>NOTE:[ENND] If you change the file, save it, change it again, and re-open it,
File Demo will not ask Revert to saved document? as some applications do. Instead,
it will bail out of the File Open process partway through and leave you with your
most recent changes. This behavior is built in to MFC. If the name of the file you
are opening matches the name of the file that is already open, you will not revert
to the saved document. 
<HR>
</BLOCKQUOTE>
<H2><A NAME="Heading5"></A>Creating a Persistent Class</H2>
<P>What if you've created your own custom class for holding the elements of a document?
How can you make an object of this class persistent? You find the answers to these
questions in this section.</P>
<P>Suppose that you now want to enhance the File Demo application so that it contains
its data in a custom class called CMessages. The member variable is now called m_messages
and is an instance of CMessages. This class holds three CString objects, each of
which must be saved and loaded for the application to work correctly. One way to
arrange this is to save and load each individual string, as shown in Listing 7.5.</P>
<P>
<H4>Listing 7.5   One Possible Way to Save the New Class's Strings</H4>
<PRE>void CFileDoc::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
ar << m_messages.m_message1;
ar << m_messages.m_message2;
ar << m_messages.m_message3;
}
else
{
ar >> m_messages.m_message1;
ar >> m_messages.m_message2;
ar >> m_messages.m_message3;
}
</PRE>
<PRE>}
</PRE>
<P>You can write the code in Listing 7.5 only if the three member variables of the
CMessages class are public and if you know the implementation of the class itself.
Later, if the class is changed in any way, this code also has to be changed. It's
more object oriented to delegate the work of storing and loading to the CMessages
class itself. This requires some preparation. The following basic steps create a
class that can serialize its member variables:</P>
<DL>
<DT></DT>
<DD><B>1. </B>Derive the class from CObject.
<P>
<DT></DT>
<DD><B>2. </B>Place the DECLARE_SERIAL() macro in the class declaration.
<P>
<DT></DT>
<DD><B>3. </B>Place the IMPLEMENT_SERIAL() macro in the class implementation.
<P>
<DT></DT>
<DD><B>4. </B>Override the Serialize() function in the class.
<P>
<DT></DT>
<DD><B>5. </B>Provide an empty, default constructor for the class.
<P>
</DL>
<P>In the following section, you build an application that creates persistent objects
in just this way.</P>
<P>
<H3><A NAME="Heading6"></A>The File Demo 2 Application</H3>
<P>The next sample application, File Demo 2, demonstrates the steps you take to create
a class from which you can create persistent objects. It will have an Edit, Change
Messages command that changes all three strings. Like File Demo, it will save and
reload the document when the user chooses File, Save or File, Open.</P>
<P>Build an SDI application called MultiString just as you built File Demo. Add a
member variable to the document, as before, so that the Attributes section of MultiStringDoc.h
reads</P>
<P>
<PRE>// Attributes
public:
CMessages m_messages;
</PRE>
<P>The next step is to write the CMessages class.</P>
<P>
<H3><A NAME="Heading7"></A>Looking at the CMessages Class</H3>
<P>Before you can understand how the document class manages to save and load its
contents successfully, you have to understand how the CMessages class, of which the
document class's m_messages data member is an object, works. As you work with this
class, you will see how to implement the preceding five steps for creating a persistent
class.</P>
<P>To create the CMessages class, first choose Insert, New Class. Change the class
type to generic class and name it <B>CMessages</B>. In the area at the bottom of
the screen, enter <B>CObject</B> as the base class name and leave the As column set
to public, as shown in Figure 7.3.</P>
<P><A HREF="javascript:popUp('07uvc03.gif')"><B>FIG. 7.3</B></A><B> </B><I>Create
a new class to hold the messages.</I></P>
<P>This will create two files: messages.h for the header and messages.cpp for the
code. It also adds some very simple code to these files for you. (You may get a warning
about not being able to find the header file for CObject: just click OK and ignore
it because CObject, like all MFC files, is available to you without including extra
headers.)</P>
<P>Switch back to Multistringdoc.h and add this line before the class definition:</P>
<P>
<PRE>#include "Messages.h"
</PRE>
<P>This will ensure the compiler knows about the CMessages class when it compiles
the document class. You can build the project now if you want to be sure you haven't
forgotten anything. Now switch back to Messages.h and add these lines:</P>
<P>
<PRE> DECLARE_SERIAL(CMessages)
protected:
CString m_message1;
CString m_message2;
CString m_message3;
public:
void SetMessage(UINT msgNum, CString msg);
CString GetMessage(UINT msgNum);
void Serialize(CArchive& ar);
</PRE>
<P>The DECLARE_SERIAL() macro provides the additional function and member variable
declarations needed to implement object persistence.</P>
<P>Next come the class's data members, which are three objects of the CString class.
Notice that they are protected member variables. The public member functions are
next. SetMessage(), whose arguments are the index of the string to set and the string's
new value, changes a data member. GetMessage() is the complementary function, enabling
a program to retrieve the current value of any of the strings. Its single argument
is the number of the string to retrieve.</P>
<P>Finally, the class overrides the Serialize() function, where all the data saving
and loading takes place. The Serialize() function is the heart of a persistent object,
with each persistent class implementing it in a different way. Listing 7.6 shows
the code for each of these new member functions. Add it to messages.cpp.</P>
<P>
<H4>Listing 7.6  MESSAGES.CPP--The CMessages Class Implementation File</H4>
<PRE>void CMessages::SetMessage(UINT msgNum, CString msg)
{
switch (msgNum)
{
case 1:
m_message1 = msg;
break;
case 2:
m_message2 = msg;
break;
case 3:
m_message3 = msg;
break;
}
SetModifiedFlag();
}
CString CMessages::GetMessage(UINT msgNum)
{
switch (msgNum)
{
case 1:
return m_message1;
case 2:
return m_message2;
case 3:
return m_message3;
default:
return "";
}
}
void CMessages::Serialize(CArchive& ar)
{
CObject::Serialize(ar);
if (ar.IsStoring())
{
ar << m_message1 << m_message2 << m_message3;
}
else
{
ar >> m_message1 >> m_message2 >> m_message3;
}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -