📄 ch04.htm
字号:
<pre><font color="#008000"> // DO NOT EDIT what you see in these blocks of generated code!</font></pre>
<pre><font color="#008000"> //}}AFX_MSG_MAP</font></pre>
<pre><font color="#008000"> // Standard file based document commands</font></pre>
<pre><font color="#008000"> ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)</font></pre>
<pre><font color="#008000"> ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)</font></pre>
<pre><font color="#008000"> // Standard print setup command</font></pre>
<pre><font color="#008000"> ON_COMMAND(ID_FILE_PRINT_SETUP, CWinApp::OnFilePrintSetup)</font></pre>
<pre><font color="#008000">END_MESSAGE_MAP()</font></pre>
<P><B>Message Map Macros</B></P>
<p><font color="#008000">BEGIN_MESSAGE_MAP</font> and <font color="#008000">END_MESSAGE_MAP</font> are macros that, like <font color="#008000">DECLARE_MESSAGE_MAP</font> in the <font color="#008000">include</font> file, declare some member variables and
functions that the framework can use to navigate the maps of all the objects in the system. There are a number of macros used in message maps, including these:</P>
<ul>
<li> <font color="#008000">DECLARE_MESSAGE_MAP</font>—Used in the <font color="#008000">include</font> file to declare that there will be a message map in the source file.</P>
<li> <font color="#008000">BEGIN MESSAGE MAP</font>—Marks the beginning of a message map in the source file.</P>
<li> <font color="#008000">END MESSAGE MAP</font>—Marks the end of a message map in the source file.</P>
<li> <font color="#008000">ON_COMMAND</font>—Used to delegate the handling of a specific command to a member function of the class.</P>
<li> <font color="#008000">ON_COMMAND_RANGE</font>—Used to delegate the handling of a group of commands, expressed as a range of command IDs, to a single member function of the class.</P>
<li> <font color="#008000">ON_CONTROL</font>—Used to delegate the handling of a specific custom-control-notification message to a member function of the class.</P>
<li> <font color="#008000">ON_CONTROL_RANGE</font>—Used to delegate the handling of a group of custom-control-notification messages, expressed as a range of control IDs, to a single member function of the class.</P>
<li> <font color="#008000">ON_MESSAGE</font>—Used to delegate the handling of a user-defined message to a member function of the class.</P>
<li> <font color="#008000">ON_REGISTERED_MESSAGE</font>—Used to delegate the handling of a registered user-defined message to a member function of the class.</P>
<li> <font color="#008000">ON_UPDATE_COMMAND_UI</font>—Used to delegate the updating for a specific command to a member function of the class.</P>
<li> <font color="#008000">ON_COMMAND_UPDATE_UI_RANGE</font>—Used to delegate the updating for a group of commands, expressed as a range of command IDs, to a single member function of the class.</P>
<li> <font color="#008000">ON_NOTIFY</font>—Used to delegate the handling of a specific control-notification message with extra data to a member function of the class.</P>
<li> <font color="#008000">ON_NOTIFY_RANGE</font>—Used to delegate the handling of a group of control-notification messages with extra data, expressed as a range of child identifiers, to a single member function of the class. The controls that send
these notifications are child windows of the window that catches them.</P>
<li> <font color="#008000">ON_NOTIFY_EX</font>—Used to delegate the handling of a specific control-notification message with extra data to a member function of the class that returns TRUE or FALSE to indicate if the notification should be passed on
to another object for further reaction.</P>
<li> <font color="#008000">ON_NOTIFY_EX_RANGE</font>—Used to delegate the handling of a group of control-notification messages with extra data, expressed as a range of child identifiers, to a single member function of the class that returns TRUE or
FALSE to indicate if the notification should be passed on to another object for further reaction. The controls that send these notifications are child windows of the window that catches them.</P>
</ul>
<P>In addition to these, there are about 100 macros, one for each of the more common messages, that direct a single specific message to a member function. For example, <font color="#008000">ON_CREATE</font> delegates the <font
color="#008000">WM_CREATE</font> message to a function called <font color="#008000">OnCreate()</font>. You cannot change the function names in these macros. Typically, these macros are added to your message map by ClassWizard, as demonstrated in <A
HREF="index09.htm" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/index09.htm" target="text">Chapter 9</A>, "Building a Complete Application: ShowString."</P>
<P><B>How Message Maps Work</B></P>
<P>The message maps presented in Listings 4.3 and 4.4 are for the <font color="#008000">CShowStringApp</font> class of the ShowString application. This class handles application-level tasks like opening a new file or displaying the About box. The entry
added to the header file's message map can be read as "there is a function called <font color="#008000">OnAppAbout()</font> that takes no parameters.” The entry in the source file's map means "when an <font
color="#008000">ID_APP_ABOUT</font> command message arrives, call <font color="#008000">OnAppAbout()</font>." It shouldn't be a big surprise that the <font color="#008000">OnAppAbout()</font> member function displays the About box for the
application.</P>
<P>But how do message maps really work? Every application has an object that inherits from <font color="#008000">CWinApp</font>, and has a member function called <font color="#008000">Run()</font>. That function calls <font
color="#008000">CWinThread::Run()</font>, which is far longer than the simple <font color="#008000">WinMain()</font> presented earlier, but has the same message loop at its heart: call <font color="#008000">GetMessage()</font>, call <font
color="#008000">TranslateMessage()</font>, call <font color="#008000">DispatchMessage()</font>. Almost every window object uses the same old-style windows class, and the same <font color="#008000">WindProc</font>, called <font
color="#008000">AfxWndProc()</font>. The <font color="#008000">WindProc</font>, as you've already seen, knows the handle, <font color="#008000">hWnd</font>, of the window the message is for. MFC keeps something called a handle map, a table of window
handles and pointers to objects, and the framework uses this to get a pointer to the C++ object, a <font color="#008000">CWnd*</font>. Next, it calls <font color="#008000">WindowProc()</font>, a virtual function of that object. Buttons or views might have
different <font color="#008000">WindowProc()</font> implementations, but through the magic of polymorphism, the right function gets called.</P>
<blockquote><p><img src="sidebar.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/sidebar.gif">
<P ALIGN="CENTER">
<CENTER><I>Polymorphism</I></CENTER></P>
<P>Virtual functions and polymorphism are important C++ concepts for anyone working with MFC. They only arise when you are using pointers to objects, and when the class of objects to which the pointers are pointing is derived from another class. Consider
as an example a class called <font color="#008000">CDerived</font> that is derived from a base class called <font color="#008000">CBase</font>, with a member function called <font color="#008000">Function()</font> that is declared in the base class and
overridden in the derived class. There are now two functions: one has the full name <font color="#008000">CBase::Function()</font> and the other is <font color="#008000">CDerived::Function()</font>.</P>
<P>If your code has a pointer to a base object, and sets that pointer equal to the address of the derived object, it can then call the function, like this:</P>
<p>
<pre><font color="#008000"> CDerived derivedobject;</font></pre>
<pre><font color="#008000"> CBase* basepointer;</font></pre>
<pre><font color="#008000"> basepointer = &derivedobject;</font></pre>
<pre><font color="#008000"> basepointer->Function();</font></pre>
</p>
<P>In this case, <font color="#008000">CBase::Function()</font> will be called. But there are times when that is not what you want, when you have to use a <font color="#008000">CBase</font> pointer but you really want <font
color="#008000">CDerived::Function()</font> to be called. To indicate this, in <font color="#008000">CBase</font>, <font color="#008000">Function()</font> is declared to be virtual. Think of it as an instruction to the compiler to override this function if
there is any way to do it.</P>
<P>Once <font color="#008000">Function()</font> is declared to be virtual in the base class, <font color="#008000">CBase</font>, the code fragment above would actually call <font color="#008000">CDerived::Function()</font> as desired. That's polymorphism,
and that shows up again and again when using MFC classes. You use a pointer to a window, a <font color="#008000">CWnd*</font>, that really points to a <font color="#008000">CButton</font> or a <font color="#008000">CView</font> or some other class derived
from <font color="#008000">CWnd</font>, and when a function like <font color="#008000">WindowProc()</font> is called, it will be the derived function, <font color="#008000">CButton::WindowProc()</font> for example, that is called.</P>
<p><img src="bottom.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/bottom.gif"></blockquote>
<p><font color="#008000">WindowProc()</font> calls <font color="#008000">OnWndMsg()</font>, the C++ function that really handles messages. First, it checks to see if this is a message, a command, or a notification. Assuming it’s a message, it looks
in the message map for the class, using the member variables and functions that were set up by <font color="#008000">DECLARE_MESSAGE_MAP</font>, <font color="#008000">BEGIN_MESSAGE_MAP</font>, and <font color="#008000">END_MESSAGE_MAP</font>. Part of what
those macros arrange is to allow access to the message map entries of the base class by the functions that search the message map of the derived class. That means if a class inherits from <font color="#008000">CView</font>, and doesn't catch a message
normally caught by <font color="#008000">CView</font>, then that message will still be caught by the same <font color="#008000">CView</font> function as inherited by the derived class. This message map inheritance parallels the C++ inheritance but is
independent of it, and saves a lot of trouble carrying virtual functions around.</P>
<P>The bottom line: You add a message map entry and when a message arrives, the functions called by the hidden message loop look in these tables to decide which of your objects, and which member function of the object, should handle the message. That's
what's really going on behind the scenes.</P>
<P><B>Messages Caught by MFC Code</B></P>
<P>The other great advantage of MFC is that the classes already catch most of the common messages and do the right thing, without any coding on your part at all. For example, you don't need to catch the message that tells you that the user has chosen
File, Save As—MFC classes catch it, put up the dialog box to get the new file name, handle all the behind-the-scenes work, and finally call one of your functions, which must be named <font color="#008000">Serialize()</font>, to actually write out the
document<B><font color="#008000">. (App </font></B><B><font color="#008000">Wizard typically makes an empty Serialize() function for you to fill in).</font></B> You only need to add message map entries for behavior that is not common to all
applications.</P>
<H3><B>ClassWizard Helps You to Catch Messages</B></H3>
<P>Message maps may not be simple to read, but they are simple to create if you use ClassWizard. There are two ways to add an entry to a message map in Visual C++ 5.0: with the main ClassWizard dialog box, or with one of the new dialog boxes that add
message handlers or virtual functions.</P>
<P><B>The ClassWizard Tabbed Dialog Box</B></P>
<P>The main ClassWizard dialog box is displayed by choosing View, ClassWizard or by pressing Ctrl+W. ClassWizard is a tabbed dialog box, and Figure 4.1 shows the Message Map tab. At the top of the dialog box are two drop-down list boxes, one that reminds
you which project you are working on (ShowString in this case) and the other that reminds you which class owns the message map you are editing. In this case, it is the <font color="#008000">CShowStringApp</font> class, whose message map you have already
seen.</P>
<A HREF="Ffigs01.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/figs/ch04/Ffigs01.gif"><b>Fig. 4.1</b></A>
<P><I>ClassWizard makes catching messages simple.</I></P>
<P>Below those single line boxes are a pair of multi-line boxes. The one on the left lists the class itself and all the commands that the user interface can generate. Commands are discussed in the "Commands" section later in this chapter. With
the class name highlighted, the box on the right lists all the Windows messages this class might catch. It also lists a number of virtual functions that catch common messages.</P>
<P>To the right of those boxes are buttons where you can add a new class to the project, add a function to the class to catch the highlighted message, remove a function that was catching a message, or open the source code for the function that catches the
highlighted message. Typically, you select a class, select a message, and click Add Function to catch the message. Here's what the Add Function button sets in motion:</P>
<ul>
<li> Adds a skeleton function to the bottom of the source file for the application.</P>
<li> Adds an entry to the message map in the source file.</P>
<li> Adds an entry to the message map in the include file.</P>
<li> Updates the list of messages and member functions in the dialog box.</P>
</ul>
<P>After you add a function, clicking Edit Code makes it simple to start filling in the behavior of that function. If you prefer, double-click the function name in the Member Functions list box.</P>
<P>Below the Object IDs and Messages boxes is a list of the member functions of this class that are related to messages. This class has two such functions:</P>
<ul>
<li> <font color="#008000">OnAppAbout()</font>—Catches the <font color="#008000">ID_APP_ABOUT</font> command, and is labeled with a <font color="#008000">W</font> (for Windows message) in the list.</P>
<li> <font color="#008000">InitInstance()</font>—Overrides a virtual function in <font color="#008000">CWinApp</font>, the base class for <font color="#008000">CShowStringApp</font>, and is labeled with a <font color="#008000">V</font> (for virtual
function) in the list.</P>
</ul>
<P>The <font color="#008000">InitInstance</font> function is called whenever an application first starts. You do not need to understand this function to see that ClassWizard reminds you the function has been overridden.</P>
<P>Finally, under the Member Functions box is a reminder of the meaning of the highlighted message. Called to implement wait cursors is a description of the <font color="#008000">DoWaitCursor</font> virtual function.</P>
<P><B>The Add Windows Message Handler Dialog Box</B></P>
<P>In release 5.0 of Visual C++, a new way of catching messages was added. Rather than bringing up ClassWizard and then remembering to set the right class name in a drop-down list box, you right-click on the class name in <font
color="#008000">ClassView</font> and then choose Add Windows Message Handler from the shortcut menu that appears. Figure 4.2 shows the dialog box that comes up when you make this choice.</P>
<A HREF="Ffigs02.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/figs/ch04/Ffigs02.gif"><b>Fig. 4.2</b></A>
<P><I>The New Windows Message and Event Handlers dialog box is another way to catch messages.</I></P>
<P>This dialog box does not show any of the virtual functions that were listed in the main ClassView dialog box. It is easy to see that this class catches the command <font color="#008000">ID_APP_ABOUT</font> but does not catch the command update.
(Commands and command updating are discussed in more detail later in this chapter.) To add a new virtual function, you right-click on the class in ClassView and choose Add New Virtual Function from the shortcut menu. Figure 4.3 shows this dialog box.</P>
<A HREF="Ffigs03.gif" tppabs="http://www.mcp.com/814147200/0-7897/0-7897-1145-1/figs/ch04/Ffigs03.gif"><b>Fig. 4.3</b></A>
<P><I>The New Virtual Override dialog box simplifies implementing virtual functions.</I></P>
<P>You can see in Figure 4.3 that <font color="#008000">CShowStringApp</font> already overrides the <font color="#008000">InitInstance()</font> virtual function, and you can see what other functions are available to be overridden. As in the tabbed dialog
box, a message area at the bottom of the dialog box reminds you of the purpose of each function: in fact the text—called to implement wait cursors—is identical to that in Figure 4.1.</P>
<P><B>Which Class should catch the message?</B></P>
<P>The only tricky part of message maps and message handling is deciding which class should catch the message. That's a decision you can't make until you understand all the different message and command targets that make up a typical application. The
choice is usually among the following:</P>
<ul>
<li> The active view</P>
<li> The document associated with the active view</P>
<li> The frame window that holds the active view</P>
<li> The application object</P>
</ul>
<P>Views, documents, and frames are discussed in the next chapter, "Documents and Views."</P>
<H3><B>List of Messages</B></H3>
<P>There are almost 900 Windows messages, so you won't find a list of them all in this chapter. Usually, you arrange to catch messages with ClassWizard, and are presented with a much shorter list that is appropriate for the class you are catching messages
with. Not every kind of window can receive every kind of message. For example, only classes that inherit from <font color="#008000">CListBox</font> receive list box messages like <font color="#008000">LB_SETSEL</font>, which directs the list box to move
the highlight to a specific list item. The first component of a message name indicates the kind of window this message is destined for, or coming from. These window types are listed in Table 4.1.</P>
<P><I>Table 4.1—Windows Message Prefixes and Window Types</I></P>
<TABLE BORDER>
<TR>
<TD>
<P><B>Prefix</B></P>
<TD>
<P><B>Window Type</B></P>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -