📄 ch10.htm
字号:
All mouse messages are delivered to this application until the mouse is released
in the OnLButtonUp function, using the ReleaseCapture function. In the meantime,
by placing the GetCapture function in an if statement and comparing its return value
to this, you can determine whether your application has captured the mouse. If you
capture the mouse, you want to execute the rest of the code in those functions; otherwise,
you don't.</P>
<P>In the OnMouseMove function, after you create your device context, you do several
things in a single line of code. The line</P>
<P>
<PRE>CLine *pLine = GetDocument()->AddLine(m_ptPrevPos, point);
</PRE>
<P>creates a new pointer to a CLine class instance. Next, it calls the GetDocument
function, which returns a pointer to the document object. This pointer is used to
call the document class's AddLine function, passing the previous and current points
as arguments. The return value from the AddLine function is used to initialize the
CLine object pointer. The CLine pointer can now be used to call the line object's
Draw function.</P>
<BLOCKQUOTE>
<P>
<HR>
<STRONG>NOTE:</STRONG> A pointer is the address of an object. It is used to pass
an object more efficiently around a program. Passing a pointer to an object, instead
of the object itself, is like telling someone that the remote control is "on
the couch between the second and third cushion, beside the loose pocket change"
instead of handing the remote to the person. Actually, in programming terms, handing
the remote to the person requires making an exact copy of the remote and handing
the copy to the other person. It is obviously more efficient to tell the person where
to find the remote than to manufacture an exact copy of the remote.<BR>
The notation -> denotes that the object's functions or properties are accessed
through a pointer, as opposed to directly through the object itself with the period
(.) notation.
<HR>
</BLOCKQUOTE>
<H4>Drawing the Painting</H4>
<P>In the view class, the function OnDraw is called whenever the image presented
to the user needs to be redrawn. Maybe another window was in front of the application
window, the window was just restored from being minimized, or a new document was
just loaded from a file. Why the view needs to be redrawn doesn't matter. All you
need to worry about as the application developer is adding the code to the OnDraw
function to render the document that your application is designed to create.</P>
<P>Locate the OnDraw function in the CDay10View class and add the code in Listing
10.7.</P>
<P>
<H4>LISTING 10.7. THE CDay10View OnDraw FUNCTION.</H4>
<PRE> 1: void CDay10View::OnDraw(CDC* pDC)
2: {
3: CDay10Doc* pDoc = GetDocument();
4: ASSERT_VALID(pDoc);
5:
6: // TODO: add draw code for native data here
7:
8: ///////////////////////
9: // MY CODE STARTS HERE
10: ///////////////////////
11:
12: // Get the number of lines in the document
13: int liCount = pDoc->GetLineCount();
14:
15: // Are there any lines in the document?
16: if (liCount)
17: {
18: int liPos;
19: CLine *lptLine;
20:
21: // Loop through the lines in the document
22: for (liPos = 0; liPos < liCount; liPos++)
23: {
24: // Get the from and to point for each line
25: lptLine = pDoc->GetLine(liPos);
26: // Draw the line
27: lptLine->Draw(pDC);
28: }
29: }
30:
31: ///////////////////////
32: // MY CODE ENDS HERE
33: ///////////////////////
34: }
</PRE>
<P>In this function, the first thing you did was find out how many lines are in the
document to be drawn. If there aren't any lines, then there is nothing to do. If
there are lines in the document, you loop through the lines using a for loop, getting
each line object from the document and then calling the line object's Draw function.</P>
<P>Before you can compile and run your application, you'll need to include the header
file for the Cline class in the source code file for the document and view classes.
To add this to your application, edit both of these files (Day10Doc.cpp and Day10View.cpp),
adding the Line.h file to the includes, as shown in Listing 10.8.</P>
<P>
<H4>LISTING 10.8. THE CDay10Doc includes.</H4>
<PRE> 1: #include "stdafx.h"
2: #include "Day10.h"
3: #include "MainFrm.h"
4: #include "Line.h"
5: #include "Day10Doc.h"
</PRE>
<P>At this point, you should be able to compile and run your application, drawing
figures in it as shown in Figure 10.4. If you minimize the window and then restore
it, or if you place another application window in front of your application window,
your drawing should still be there when your application window is visible again
(unlike the application you built a week ago).</P>
<P><A HREF="javascript:popUp('10fig08.gif')"><B>FIGURE 10.4.</B></A><B> </B><I>Drawing
with your application.</I></P>
<P>
<H2><A NAME="Heading7"></A>Saving and Loading the Drawing</H2>
<P>Now that you can create drawings that don't disappear the moment you look away,
it'd be nice if you could make them even more persistent. If you play with the menus
on your application, it appears that the Open, Save, and Save As menu entries on
the File menu activate, but they don't seem to do anything. The printing menu entries
all work, but the entries for saving and loading a drawing don't. Not even the New
menu entry works! Well, you can do something to fix this situation.</P>
<P>
<H3><A NAME="Heading8"></A>Deleting the Current Drawing</H3>
<P>If you examine the CDay10Doc class, you'll see the OnNewDocument function that
you can edit to clear out the current drawing. Wrong! This function is intended for
initializing any class settings for starting work on a new drawing and not for clearing
out an existing drawing. Instead, you need to open the Class Wizard and add a function
on the DeleteContents event message. This event message is intended for clearing
the current contents of the document class. Edit this new function, adding the code
in Listing 10.9.</P>
<P>
<H4>LISTING 10.9. THE CDay10Doc DeleteContents FUNCTION.</H4>
<PRE> 1: void CDay10Doc::DeleteContents()
2: {
3: // TODO: Add your specialized code here and/or call the base class
4:
5: ///////////////////////
6: // MY CODE STARTS HERE
7: ///////////////////////
8:
9: // Get the number of lines in the object array
10: int liCount = m_oaLines.GetSize();
11: int liPos;
12:
13: // Are there any objects in the array?
14: if (liCount)
15: {
16: // Loop through the array, deleting each object
17: for (liPos = 0; liPos < liCount; liPos++)
18: delete m_oaLines[liPos];
19: // Reset the array
20: m_oaLines.RemoveAll();
21: }
22:
23: ///////////////////////
24: // MY CODE ENDS HERE
25: ///////////////////////
26:
27: CDocument::DeleteContents();
28: }
</PRE>
<P>This function loops through the object array, deleting each line object in the
array. Once all the lines are deleted, the array is reset by calling its RemoveAll
method. If you compile and run your application, you'll find that you can select
File|New, and if you decide not to save your current drawing, your window is wiped
clean.</P>
<P>
<H3><A NAME="Heading9"></A>Saving and Restoring the Drawing</H3>
<P>Adding the functionality to save and restore your drawings is pretty easy to implement,
but it might not be so easy to understand. That's okay; you'll spend an entire day
on understanding saving and restoring files, also known as serialization, in three
days. In the meantime, find the Serialize function in the CDay10Doc class. The function
should look something like</P>
<P>
<PRE> 1: void CDay10Doc::Serialize(CArchive& ar)
2: {
3: if (ar.IsStoring())
4: {
5: // TODO: add storing code here
6: }
7: else
8: {
9: // TODO: add loading code here
10: }
11: }
</PRE>
<P>Remove all the contents of this function, and edit the function so that it looks
like Listing 10.10.</P>
<P>
<H4>LISTING 10.10. THE CDay10Doc Serialize FUNCTION.</H4>
<PRE> 1: void CDay10Doc::Serialize(CArchive& ar)
2: {
3: ///////////////////////
4: // MY CODE STARTS HERE
5: ///////////////////////
6:
7: // Pass the serialization on to the object array
8: m_oaLines.Serialize(ar);
9:
10: ///////////////////////
11: // MY CODE ENDS HERE
12: ///////////////////////
13: }
</PRE>
<P>This function takes advantage of the functionality of the CObArray class. This
object array will pass down its array of objects, calling the Serialize function
on each of the objects. This means that you need to add a Serialize function to the
CLine class. Specify it as a void function type with the declaration of Serialize(CArchive&
ar). Edit the function, adding the code in Listing 10.11.</P>
<P>
<H4>LISTING 10.11. THE CLine Serialize FUNCTION.</H4>
<PRE> 1: void CLine::Serialize(CArchive &ar)
</PRE>
<PRE> 2: {
3: CObject::Serialize(ar);
4:
5: if (ar.IsStoring())
6: ar << m_ptFrom << m_ptTo;
7: else
8: ar >> m_ptFrom >> m_ptTo;
9: }
</PRE>
<P>This function follows basically the same flow that the original Serialize function
would have followed in the CDay10Doc class. It uses the I/O stream functionality
of C++ to save and restore its contents.</P>
<P>At this point, if you compile and run your application, you expect the save and
open functions to work. Unfortunately, they don't (yet). If you run your application
and try to save a drawing, a message box will tell you that the application was unable
to save the file, as in Figure 10.5.</P>
<P><A HREF="javascript:popUp('10fig09.gif')"><B>FIGURE 10.5.</B></A><B> </B><I>Unable
to save drawings.</I></P>
<P>The reason that you are unable to save your drawing is that Visual C++ must be
told that a class should be serializable. To do this, you add one line to the CLine
class header file and one line to the CLine source code file. Open the CLine header
file (Line.h), and add the DECLARE_SERIAL line in Listing 10.12 just after the first
line of the class definition.</P>
<P>
<H4>LISTING 10.12. THE Line.h EDIT FOR SERIALIZATION.</H4>
<PRE>1: class CLine : public CObject
2: {
3: DECLARE_SERIAL (CLine)
4: public:
5: CLine(CPoint ptFrom, CPoint ptTo, UINT nWidth, COLORREF crColor);
</PRE>
<P>Next, open the CLine source code file, and add the IMPLEMENT_SERIAL line in Listing
10.13 just before the class constructor functions.</P>
<P>
<H4>LISTING 10.13. THE Line.cpp EDIT FOR SERIALIZATION.</H4>
<PRE> 1: // Line.cpp: implementation of the CLine class.
2: //
3: //////////////////////////////////////////////////////////////////////
4:
5: #include "stdafx.h"
6: #include "Day10.h"
7: #include "Line.h"
8:
9: #ifdef _DEBUG
10: #undef THIS_FILE
11: static char THIS_FILE[]=__FILE__;
12: #define new DEBUG_NEW
13: #endif
14:
15: IMPLEMENT_SERIAL (CLine, CObject, 1)
16: //////////////////////////////////////////////////////////////////////
17: // Construction/Destruction
18: //////////////////////////////////////////////////////////////////////
19:
20: CLine::CLine()
21: {
22:
23: }
</PRE>
<P>Now if you compile and run your application, you should be able to draw your own
self-portrait and save it for posterity, as shown in Figure 10.6.</P>
<P><A HREF="javascript:popUp('10fig10.gif')"><B>FIGURE 10.6.</B></A><B> </B><I>My
self-portrait.</I></P>
<P>
<H2><A NAME="Heading10"></A>Interacting with the Menu</H2>
<P>Now that you have a working drawing program, it would be nice if the user could
choose the color with which she wants to draw. Adding this functionality requires
making changes in the CLine class to associate the color with the line and to CDay10Doc
to maintain the currently selected color. Finally, you need to add a pull-down menu
to select the desired color.</P>
<P>
<H3><A NAME="Heading11"></A>Adding Color to the CLine Class</H3>
<P>The changes to the CLine class are fairly straightforward. The first thing that
you need to do is to add another member variable to the CLine class to hold the color
of each line. Next, you need to modify the class constructor to add color to the
list of attributes to be passed in. Third, you need to modify the Draw function to
use the specified color. Finally, you need to modify the Serialize function to save
and restore the color information along with the point information. To do all these
things, follow these steps:</P>
<DL>
<DT></DT>
<DD><B>1. </B>Select the CLine class in the Class View tab of the workspace pane.
Right-click the mouse and select Add Member Variable from the pop-up menu.
<P>
<DT></DT>
<DD><B>2. </B>Specify the variable type as COLORREF, the name as m_crColor, and the
access as private. Click OK to add the variable.
<P>
<DD><B>3. </B>Right-click the CLine constructor in the Class View tree. Select Go
to Declaration from the pop-up menu.
<DT></DT>
<DD><B>4. </B>Add COLORREF crColor as a third argument to the constructor declaration.
<P>
<DT></DT>
<DD><B>5. </B>Right-click the CLine constructor in the Class View tree. Select Go
to Definition from the pop-up menu.
<P>
<DT></DT>
<DD><B>6. </B>Modify the constructor to add the third argument and to set the m_crColor
member to the new argument, as in Listing 10.14.
<P>
</DL>
<H4>LISTING 10.14. THE MODIFIED CLine CONSTRUCTOR.</H4>
<PRE>1: CLine::CLine(CPoint ptFrom, CPoint ptTo, COLORREF crColor)
2: {
3: //Initialize the from and to points
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -