📄 ch10.htm
字号:
<H3><A NAME="Heading5"></A>Implementing the Document Functionality</H3>
<P>Now that you have an object to use for representing the drawings made by the user,
you can store these CLine objects on the document object in a simple dynamic array.
To hold this array, you can add a CObArray member variable to the document class.</P>
<P>The CObArray class is an object array class that dynamically sizes itself to accommodate
the number of items placed in it. It can hold any objects that are descended from
the CObject class, and it is limited in size only by the amount of memory in the
system. Other dynamic array classes in MFC include CStringArray, CByteArray, CWordArray,
CDWordArray, and CPtrArray. These classes differ by the type of objects they can
hold.</P>
<P>Add the CObArray to CDay10Doc, using the Add Member Variable Wizard and giving
it a name of m_oaLines.</P>
<P>
<H4>Adding Lines</H4>
<PRE>The first functionality that you need to add to the document class is the ability to add new lines. This should be a simple process of getting the from and to points, creating a new line object, and then adding it to the object array. To
implement this function, add a new member function to the CDay10Doc class, specifying the type as CLine* and the declaration as AddLine(CPoint ptFrom, CPoint ptTo) with public access. Edit the function, adding the code in Listing 10.3.
</PRE>
<H4>LISTING 10.3. THE CDay10Doc AddLine FUNCTION.</H4>
<PRE> 1: CLine * CDay10Doc::AddLine(CPoint ptFrom, CPoint ptTo)
2: {
3: // Create a new CLine object
4: CLine *pLine = new CLine(ptFrom, ptTo);
5: try
6: {
7: // Add the new line to the object array
8: m_oaLines.Add(pLine);
9: // Mark the document as dirty
10: SetModifiedFlag();
11: }
12: // Did we run into a memory exception?
13: catch (CMemoryException* perr)
14: {
15: // Display a message for the user, giving him or her the
16: // bad news
17: AfxMessageBox("Out of memory", MB_ICONSTOP | MB_OK);
18: // Did we create a line object?
19: if (pLine)
20: {
21: // Delete it
22: delete pLine;
23: pLine = NULL;
24: }
25: // Delete the exception object
26: perr->Delete();
27: }
28: return pLine;
29: }
</PRE>
<P>At first, this function is understandable. You create a new CLine instance, passing
the from and to points as constructor arguments. Right after that, however, you have
something interesting, the following flow control construct:</P>
<P>
<PRE> 1: try
2: {
3: .
4: .
5: .
6: }
7: catch (...)
8: {
9: .
10: .
11: .
12: }
</PRE>
<P>What is this? This construct is an example of structured exception handling. Some
code could fail because of a factor beyond your control, such as running out of memory
or disk space, you can place a try section around the code that might have a problem.
The try section should always be followed by one or more catch sections. If a problem
occurs during the code in the try section, the program immediately jumps to the catch
sections. Each catch section specifies what type of exception it handles (in the
case of the AddLine function, it specifically handles memory exceptions only), and
if there is a matching catch section for the type of problem that did occur, that
section of code is executed to give the application a chance to recover from the
problem. If there is no catch section for the type of problem that did occur, your
program jumps to a default exception handler, which will most likely shut down your
application. For more information on structured exception handling, see Appendix
A, "C++ Review."</P>
<P>Within the try section, you add the new CLine instance to the array of line objects.
Next, you call the SetModifiedFlag function, which marks the document as "dirty"
(unsaved) so that if you close the application or open another file without saving
the current drawing first, the application prompts you to save the current drawing
(with the familiar Yes, No, Cancel message box).</P>
<P>In the catch section, you inform the user that the system is out of memory and
then clean up by deleting the CLine object and the exception object.</P>
<P>Finally, at the end of the function, you return the CLine object to the calling
routine. This enables the view object to let the line object draw itself.</P>
<P>
<H4>Getting the Line Count</H4>
<P>The next item you will add to the document class is a function to return the number
of lines in the document. This functionality is necessary because the view object
needs to loop through the array of lines, asking each line object to draw itself.
The view object will need to be able to determine the total number of lines in the
document and retrieve any specific line from the document.</P>
<P>Returning the number of lines in the document is a simple matter of returning
the number of lines in the object array, so you can just return the return value
from the GetSize method of the CObArray class. To implement this function, add a
new member function to the CDay10Doc class, specifying the type as int and the declaration
as GetLineCount with public access. Edit the function, adding the code in Listing
10.4.</P>
<P>
<H4>LISTING 10.4. THE CDay10Doc GetLineCount FUNCTION.</H4>
<PRE>1: int CDay10Doc::GetLineCount()
2: {
3: // Return the array count
4: return m_oaLines.GetSize();
5: }
</PRE>
<H4>Retrieving a Specific Line</H4>
<P>Finally, you need to add a function to return a specific line from the document.
This is a simple matter of returning the object at the specified position in the
object array. To implement this function, add a new member function to the CDay10Doc
class, specifying the type as CLine* and the declaration as GetLine(int nIndex) with
public access. Edit the function, adding the code in Listing 10.5.</P>
<P>
<H4>LISTING 10.5. THE CDay10Doc GetLine FUNCTION.</H4>
<PRE>1: CLine * CDay10Doc::GetLine(int nIndex)
2: {
3: // Return a pointer to the line object
4: // at the specified point in the object array
5: return (CLine*)m_oaLines[nIndex];
6: }</PRE>
<BLOCKQUOTE>
<P>
<HR>
<STRONG>NOTE:</STRONG> Notice that the object being returned had to be cast as a
pointer to a CLine object. Because the CObArray class is an array of CObjects, every
element that is returned by the array is a CObject instance, not a CLine object instance.
<HR>
</BLOCKQUOTE>
<H3><A NAME="Heading6"></A>Showing the User</H3>
<P>Now that you have built the capability into the document class to hold the drawing,
you need to add the functionality to the view object to read the user's drawing input
and to draw the image. The mouse events to capture the user input are almost identical
to those you created a week ago. The second part of the functionality that you need
to implement is drawing the image. You will make an addition to a function that already
exists in the view object class.</P>
<P>Before adding these functions, you need to add a member variable to the CDay10View
class to maintain the previous mouse point, just as you did a week ago. Add a member
variable to the CDay10View class through the workspace pane, specifying the type
as CPoint, the name as m_ptPrevPos, and the access as private.</P>
<P>
<H4>Adding the Mouse Events</H4>
<P>To add the mouse events to capture the user's drawing efforts, open the Class
Wizard and add functions to the CDay10View class for the WM_LBUTTONDOWN, WM_LBUTTONUP,
and WM_MOUSEMOVE event messages. Edit the functions as in Listing 10.6.</P>
<P>
<H4>LISTING 10.6. THE CDay10View MOUSE FUNCTIONS.</H4>
<PRE> 1: void CDay10View::OnLButtonDown(UINT nFlags, CPoint point)
2: {
3: // TODO: Add your message handler code here and/or call default
4:
5: ///////////////////////
6: // MY CODE STARTS HERE
7: ///////////////////////
8:
9: // Capture the mouse, so no other application can
10: // grab it if the mouse leaves the window area
11: SetCapture();
12: // Save the point
13: m_ptPrevPos = point;
14:
15: ///////////////////////
16: // MY CODE ENDS HERE
17: ///////////////////////
18:
19: CView::OnLButtonDown(nFlags, point);
20: }
21:
22: void CDay10View::OnLButtonUp(UINT nFlags, CPoint point)
23: {
24: // TODO: Add your message handler code here and/or call default
25:
26: ///////////////////////
27: // MY CODE STARTS HERE
28: ///////////////////////
29:
30: // Have we captured the mouse?
31: if (GetCapture() == this)
32: // If so, release it so other applications can
33: // have it
34: ReleaseCapture();
35:
36: ///////////////////////
37: // MY CODE ENDS HERE
38: ///////////////////////
39:
40: CView::OnLButtonUp(nFlags, point);
41: }
42:
43: void CDay10View::OnMouseMove(UINT nFlags, CPoint point)
44: {
45: // TODO: Add your message handler code here and/or call default
46:
47: ///////////////////////
48: // MY CODE STARTS HERE
49: ///////////////////////
50:
51: // Check to see if the left mouse button is down
52: if ((nFlags & MK_LBUTTON) == MK_LBUTTON)
53: {
54: // Have we captured the mouse?
55: if (GetCapture() == this)
56: {
57: // Get the Device Context
58: CClientDC dc(this);
59:
60: // Add the line to the document
61: CLine *pLine = GetDocument()->AddLine(m_ptPrevPos, point);
62:
63: // Draw the current stretch of line
64: pLine->Draw(&dc);
65:
66: // Save the current point as the previous point
67: m_ptPrevPos = point;
68: }
69: }
70:
71: ///////////////////////
</PRE>
<PRE>72: // MY CODE ENDS HERE
</PRE>
<PRE>73: ///////////////////////
74:
75: CView::OnMouseMove(nFlags, point);
76: }
</PRE>
<P>In the OnLButtonDown function, the first thing you do is call the SetCapture function.
This function "captures" the mouse, preventing any other applications from
receiving any mouse events, even if the mouse leaves the window space of this application.
This enables the user to drag the mouse outside the application window while drawing
and then drag the mouse back into the application window, without stopping the drawing.
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: }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -