7_Part III MFC's Document-View Architecture

136
Part III: MFC's Document-View Architecture Chapter List Chapter 12: Menus, Keyboard Accelerators, the Rich Edit Control, an d Property Sheets Chapter 13: Toolbars and Status Bars Chapter 14: A Reusable Frame Window Base Class Chapter 15: Separating the Document from Its View Chapter 16: Reading and Writing Documents Chapter 17: Printing and Print Preview Chapter 18: Splitter Windows and Multiple Views Chapter 19: Context-Sensitive Help Chapter 20: Dynamic-Link Libraries Chapter 21: MFC Programs Without Document or View Classes Chapter 12: Menus, Keyboard Accelerators, the Rich Edit Control, and Property Sheets Overview In the book's examples so far, mouse clicks have triggered most program activity. Even though menu commands might have been more appropriate, we've used mouse clicks because mouse-click messages are han dled simply and directly within the Microsoft Foundation Class (MFC) library view window. If you want program activity to b e triggered when the user chooses a command from a menu, you must f irst become familiar with the other application framework elements. This chapter concentrates on menus and the command routing architecture. Along the way, I'll introduce frames and documents and explain the relationships between these new a pplication framework elements and the already familiar view element. You'll use the menu editor to lay out a menu visually, and you'll use the code wizards available from Class View to link document and view member functions to menu commands. You'll learn how to use special update command user interface member functions to enable and disable menu commands, and you'll learn how to use keyboard accelerators as menu shortcut keys. Because you're probably tired of circles and dialog boxes, we'll first examine two new MFC building blocks: the rich edit common control, which can add powerful text editing features to your application, and property sheets, which are ideal for setting edit options. The Main Frame Window and Document Classes Up to now, we've been using a view window as if it were the application's only window. In a Single Document Interface (SDI) application, the view window sits inside another window—the application's main frame window. The main frame window has the title bar and the menu bar. Various child windows, including the toolbar window, the view window, and the status bar window, occupy t he main frame window's client area, as shown in Figure 12-1. The application framework controls the interaction between the frame and the view by routing messages from the frame to the view. Figure 12-1: The chil d windows within an SDI main frame window. Look again at any project files generated by the MFC Application Wizard. The MainFrm.h and MainFrm.cpp files contain the Page 1 of 136 Part III: MFC's Document-View Architecture 03-05-2014 file://L:\TEMP\~hhA60B.htm

Transcript of 7_Part III MFC's Document-View Architecture

  • 5/26/2018 7_Part III MFC's Document-View Architecture

    1/136

    Part III: MFC's Document-View Architecture

    Chapter List

    Chapter 12: Menus, Keyboard Accelerators, the Rich Edit Control, and Property Sheets

    Chapter 13: Toolbars and Status Bars

    Chapter 14: A Reusable Frame Window Base Class

    Chapter 15: Separating the Document from Its View

    Chapter 16: Reading and Writing Documents

    Chapter 17: Printing and Print Preview

    Chapter 18: Splitter Windows and Multiple Views

    Chapter 19: Context-Sensitive Help

    Chapter 20: Dynamic-Link Libraries

    Chapter 21: MFC Programs Without Document or View Classes

    Chapter 12: Menus, Keyboard Accelerators, the Rich Edit Control,and Property Sheets

    Overview

    In the book's examples so far, mouse clicks have triggered most program activity. Even though menu commands might have beenmore appropriate, we've used mouse clicks because mouse-click messages are handled simply and directly within the MicrosoftFoundation Class (MFC) library view window. If you want program activity to be triggered when the user chooses a command from amenu, you must f irst become familiar with the other application framework elements.

    This chapter concentrates on menus and the command routing architecture. Along the way, I'll introduce frames and documents andexplain the relationships between these new application framework elements and the already familiar view element. You'll use themenu editor to lay out a menu visually, and you'll use the code wizards available from Class View to link document and viewmember functions to menu commands. You'll learn how to use special update command user interface member functions to enableand disable menu commands, and you'll learn how to use keyboard accelerators as menu shortcut keys.

    Because you're probably tired of circles and dialog boxes, we'll first examine two new MFC building blocks: the rich edit commoncontrol, which can add powerful text editing features to your application, and property sheets, which are ideal for setting edit options.

    The Main Frame Window and Document Classes

    Up to now, we've been using a view window as if it were the application's only window. In a Single Document Interface (SDI)application, the view window sits inside another windowthe application's main frame window. The main frame window has the titlebar and the menu bar. Various child windows, including the toolbar window, the view window, and the status bar window, occupy themain frame window's client area, as shown in Figure 12-1. The application framework controls the interaction between the frame andthe view by routing messages from the frame to the view.

    Figure 12-1: The child windows within an SDI main frame window.

    Look again at any project files generated by the MFC Application Wizard. The MainFrm.hand MainFrm.cppfiles contain the

    Page 1 of 136Part III: MFC's Document-View Architecture

    03-05-2014file://L:\TEMP\~hhA60B.htm

  • 5/26/2018 7_Part III MFC's Document-View Architecture

    2/136

    code for the application's main frame window class, which is derived from the class CFrameWnd. Other files, with names such as

    Ex12aDoc.hand Ex12aDoc.cpp, contain code for the application's document class, which is derived from CDocument. In thischapter, you'll begin working with the MFC document class. You'll start by learning that each view object has exactly one documentobject attached and that the view's inherited GetDocumentmember function returns a pointer to that object. You'll learn much moreabout the document-view interactions in Chapter 15.

    Windows Menus

    A Microsoft Windows menu is a familiar application element that consists of a top-level horizontal list of menus with submenus thatappear when the user selects a top-level command. Most of the time, you define for a frame window a default menu resource thatloads when the window is created. You can also define a menu resource independent of a frame window. In that case, your programmust call the functions necessary to load and activate the menu.

    A menu resource completely defines the initial appearance of a menu. Menu commands can be grayed out or have check marks,and bars can separate groups of menu commands. Multiple levels of associated menus are possible. If a first-level menu commandis associated with a submenu, the menu command carries a right-pointing arrow symbol, as shown next to the Windows menucommand in Figure 12-2.

    Figure 12-2: Submenus (shown in Microsoft Visual C++ .NET).

    Visual C++ .NET includes an easy-to-use menu-resource editing tool. This tool lets you edit menus in a WYSIWYG environment.Each menu command has a properties dialog box that defines all the characteristics of that command. The resulting resourcedefinition is stored in the application's resource script (RC) file. Each command is associated with an ID, such as ID_FILE_OPEN,

    that is defined in the Resource.hfile.

    The MFC library extends the functionality of the standard menus for Windows. Each menu command can have a prompt string thatappears in the frame's status bar when the command is highlighted. These prompts are really Windows string resource elementslinked to the menu command by a common ID. From the point of view of the menu editor and your program, the prompts appear tobe part of the menu command definition.

    Keyboard Accelerators

    You've probably noticed that most menu commands contain an underlined letter. In Visual C++ .NET (and most other applications),pressing Alt+F,S activates the File Save menu command. This shortcut system is the standard Windows method of using thekeyboard to choose commands from menus. If you look at an application's menu resource script (or the menu editor's propertiesdialog box), you'll see an ampersand (&) preceding the character that is underlined in each of the application's menu commands.

    Windows offers an alternative way of linking keystrokes to menu commands. The keyboard accelerator resource consists of a tableof key combinations with associated command IDs. The Edit Copy command (with the command ID ID_EDIT_COPY), for example,might be linked to the Ctrl+C key combination through a keyboard accelerator entry. A keyboard accelerator entry does not have tobe associated with a menu command. If no Edit Copy command were present, the Ctrl+C key combination would nevertheless

    activate the ID_EDIT_COPYcommand.

    Command Processing

    As you saw in Chapter 2, the application framework provides a sophisticated routing system for command messages. Thesemessages originate from menu commands, keyboard accelerators, and toolbar and dialog box button clicks. Command messagescan also be sent by calls to the CWnd::SendMessageor PostMessagefunction. Each message is identified by a #defineconstantthat is often assigned by a resource editor. The application framework has its own set of internal command message IDs, such as

    ID_FILE_PRINTand ID_FILE_OPEN. Your project's Resource.hfile contains IDs that are unique to your application.

    Note If a keyboard accelerator is associated with a menu command or toolbar button, the accelerator key will be disabled whenthe command or button is disabled.

    Page 2 of 136Part III: MFC's Document-View Architecture

    03-05-2014file://L:\TEMP\~hhA60B.htm

  • 5/26/2018 7_Part III MFC's Document-View Architecture

    3/136

    Most command messages originate in the application's frame window, and without the application framework in the picture, that'swhere you would put the message handlers. With command routing, however, you can handle a message almost anywhere. Whenthe application framework sees a frame window command message, it starts looking for message handlers in one of the sequenceslisted here.

    Most applications have a particular command handler in only one class, but suppose your one-view application has an identicalhandler in both the view class and the document class. Because the view is higher in the command route, only the view's commandhandler function will be called.

    What is required to install a command handler function? The installation requirements are similar to those of the window messagehandlers you've already seen. You need the function itself, a corresponding message map entry, and the function prototype.Suppose you have a menu command named Zoom (with IDM_ZOOMas the associated ID) that you want your view class to handle.You first add the following code to your view implementation file:

    BEGIN_MESSAGE_MAP(CMyView, CView)ON_COMMAND(IDM_ZOOM, OnZoom)

    END_MESSAGE_MAP()

    void CMyView::OnZoom(){

    // command message processing code}

    Next, add the following function prototype to the CMyViewclass header file (before the DECLARE_MESSAGE_MAPmacro):

    afx_msg void OnZoom();

    Of course, Visual Studio .NET automates the process of inserting command message handlers the same way it facilitates theinsertion of window message handlers. You'll learn how this works in the next example, Ex12a.

    Command Message Handling in Derived Classes

    The command routing system is one dimension of command message handling. The class hierarchy is a second dimension. If youlook at the source code for the MFC library classes, you'll see lots of ON_COMMANDmessage map entries. When you derive a

    class from one of these base classesfor example, CView the derived class inherits all the CViewmessage map functions,including the command message functions. To override one of the base class message map functions, you must add both a functionand a message map entry to your derived class.

    Update Command User Interface Handlers

    You often need to change the appearance of a menu command to match the internal state of your application. If your application'sEdit menu includes a Clear All command, for example, you might want to disable that command if there's nothing to clear. You'veundoubtedly seen such grayed-out commands in Windows-based applications, and you've probably also seen check marks next tocommands.

    With Win32 programming, it's difficult to keep menu commands synchronized with the application's state. Every piece of code thatchanges the internal state must contain statements to update the menu. The MFC library takes a different approach by calling aspecial update command user interface handler function whenever a submenu is first displayed. The handler function's argument isa CCmdUIobject, which contains a pointer to the corresponding command. The handler function can then use this pointer to modifythe command's appearance. Update command user interface handlers apply only to commands on submenus, not to top-level menu

    commands that are permanently displayed. For example, you can't use an update command user interface handler to disable a Filemenu command.

    The update command user interface coding requirements are similar to those for commands. You need the function itself, a specialmessage map entry, and of course the prototype. The associated IDin this case, IDM_ZOOMis the same constant used for thecommand. Here is an example of the necessary additions to the view class code file:

    BEGIN_MESSAGE_MAP(CMyView, CView)ON_UPDATE_COMMAND_UI(IDM_ZOOM, OnUpdateZoom)

    END_MESSAGE_MAP()

    void CMyView::OnUpdateZoom(CCmdUI* pCmdUI){

    pCmdUI->SetCheck(m_bZoomed); // m_bZoomed is a class data member}

    SDI Application MDI Application

    View View

    Document DocumentSDI main frame window MDI child frame window

    Application MDI main frame window application

    Page 3 of 136Part III: MFC's Document-View Architecture

    03-05-2014file://L:\TEMP\~hhA60B.htm

  • 5/26/2018 7_Part III MFC's Document-View Architecture

    4/136

    Here is the function prototype that you must add to the class header (before the DECLARE_MESSAGE_MAPmacro):

    afx_msg void OnUpdateZoom(CCmdUI* pCmdUI);

    Needless to say, the code wizards available from Class View's Properties window automate the process of inserting updatecommand user interface handlers.

    Commands That Originate in Dialog Boxes

    Suppose you have a pop-up dialog box with buttons, and you want a particular button to send a command message. Command IDsmust be in the range 0x8000 to 0xDFFF, the same ID range that the resource editor uses for your menu commands. If you assignan ID in this range to a dialog box button, the button will generate a routable command. The application framework first routes thiscommand to the main frame window because the frame window owns all pop-up dialog boxes. The command routing then proceedsnormally; if your view has a handler for the button's command, that's where it will be handled. To ensure that the ID is in the range0x8000 to 0xDFFF, you must use Visual C++ .NET's Resource Symbols dialog box to enter the ID before you assign the ID to abutton.

    The Application Framework's Built-in Menu Commands

    You don't have to start each frame menu from scratchthe MFC library defines some useful menu commands for you, along with allthe command handler functions, as shown in Figure 12-3.

    Figure 12-3: The standard SDI frame menus.

    The menu commands and command message handlers that you get depend on the options you select in the MFC ApplicationWizard. If you deselect Printing And Print Preview, for example, the Print and Print Preview commands won't appear. Becauseprinting is optional, the message map entries are not defined in the CViewclass but are generated in your derived view class. That'swhy entries such as the following are defined in the CMyViewclass instead of in the CViewclass:

    ON_COMMAND(ID_FILE_PRINT, CView::OnFilePrint)ON_COMMAND(ID_FILE_PRINT_PREVIEW, CView::OnFilePrintPreview)

    Enabling and Disabling Menu Commands

    The application framework can disable a menu command if it does not find a command message handler in the current commandroute. This feature saves you the trouble of having to write ON_UPDATE_COMMAND_UIhandlers. You can disable the feature ifyou set the CFrameWnddata member m_bAutoMenuEnableto FALSE.

    Suppose you have two views for one document but only the first view class has a message handler for the IDM_ZOOMcommand.The Zoom command on the frame menu will be enabled only when the first view is active. Or consider the Edit Cut, Copy, and Pastecommands, which are supplied with the application framework. These will be disabled if you haven't provided message handlers inyour derived view or document class.

    MFC Text Editing OptionsWindows itself supplies two text editing tools: the edit control and the rich edit common control. Both can be used as controls withindialog boxes, but both can also be made to look like view windows. The MFC library supports this versatility with the CEditViewandCRichEditViewclasses.

    The CEditViewClass

    This class is based on the Windows edit control. The MFC Application Wizard gives you the option of making CEditViewthe baseclass of your view class. When the framework gives you an edit view object, it has all the functionality of both CViewand CEdit.There's no multiple inheritance here, just some magic that involves window subclassing. The CEditViewclass implements and mapsthe Clipboard cut, copy, and paste functions, so they appear active on the Edit menu. The default character limit for CEditViewis1,048,575. You can change the character limit by sending the EM_LIMITTEXTmessage to the underlying edit control. However, thelimits are different depending on the operating system and the type of edit control (single or multi-line). See the MSDN Library for

    Page 4 of 136Part III: MFC's Document-View Architecture

    03-05-2014file://L:\TEMP\~hhA60B.htm

  • 5/26/2018 7_Part III MFC's Document-View Architecture

    5/136

    more information on these limits.

    The CRichEditViewClass

    This class uses the rich edit control, so it supports mixed formats and large quantities of text. The CRichEditViewclass is designedto be used with the CRichEditDocand CRichEditCntrItemclasses to implement a complete ActiveX container application.

    The CRichEditCtrlClass

    This class wraps the rich edit control, and you can use it to make a fairly decent text editor. That's exactly what we'll do in theupcoming Ex12a example. We'll use an ordinary view class derived from CView, and we'll cover the view's client area with a big richedit control that resizes itself when the view size changes. The CRichEditCtrlclass has dozens of useful member functions, and itpicks up other functions from its CWndbase class. The functions we'll use in this chapter are listed in Table 12-1.

    The Ex12a Example

    This example illustrates the routing of menu and keyboard accelerator commands to both documents and views. The application'sview class is derived from CViewand contains a rich edit control. View-directed menu commands, originating from a new submenunamed Transfer, move data between the view object and the document object, and a Clear Document command erases thedocument's contents. On the Transfer menu, the Store Data In Document command is grayed out if the view hasn't been modifiedsince the last time the data was transferred. The Clear Document command, located on the Edit menu, is grayed out when thedocument is empty. Figure 12-4shows the first version of the Ex12a program in use.

    Figure 12-4: The Ex12a program in use.

    Table 12-1: Commonly Used CRichEditCtrl Functions

    Function Description

    Create Creates the rich edit control window (which is called from the parent's WM_CREATEhandler).

    SetWindowPos Sets the size and position of the edit window (sizes the control to cover the view'sclient area).

    GetWindowText Retrieves plain text from the control. (CRichEditCtrlincludes other functions forretrieving the text using rich text formatting codes.)

    SetWindowText Stores plain text in the control.

    GetModify Gets a flag that is TRUEif the text has been modified (when the user types in thecontrol or the program calls SetModify(TRUE)).

    SetModify Sets the modify flag to TRUEor FALSE.

    GetSel Gets a flag that indicates whether the user has selected text

    SetDefaultCharFormat Sets the control's default format characteristics.

    SetSelectionCharFormat Sets the format characteristics of the selected text.

    Note If you use the dialog editor to add a rich edit control to a dialog resource, your application class InitInstancememberfunction must call the functionAfxInitRichEdit.

    Page 5 of 136Part III: MFC's Document-View Architecture

    03-05-2014file://L:\TEMP\~hhA60B.htm

  • 5/26/2018 7_Part III MFC's Document-View Architecture

    6/136

    If we were to exploit the document-view architecture fully, we would tell the rich edit control to keep its text inside the document, butthat's rather difficult to do. Instead, we'll define a document CStringdata member named m_strText, the contents of which the usercan transfer to and from the control. The initial value of m_strTextis a Hello message; choosing Clear Document from the Edit menusets it to empty. By running this example, you'll start to understand the separation of the document and the view.

    The first part of the Ex12a example uses Visual C++ .NET's WYSIWYG menu editor and keyboard accelerator editor along with thecode wizards available from Class View's Properties window. You'll need to do very little C++ coding. Simply follow these steps:

    1. Run the MFC Application Wizard to generate the Ex12a project. Accept all the default settings but two: Select SingleDocument and deselect Printing And Print Preview.

    2. Use the resource editor to edit the application's main menu. In Resource View, edit the IDR_MAINFRAMEmenuresource to add a separator and a Clear Document command to the Edit menu, as shown here:

    Now add a Transfer menu, and then define the underlying commands:

    The MFC library has defined the following command IDs for your new menu commands in the Resource Symbols dialog box.(Note that \t is a tab characterbut type\t;don't press the Tab key.)

    After you add the commands, right-click on each of them and choose Properties from the shortcut menu. Type an appropriateprompt string in each command's Properties window. These prompts will appear in the application's status bar window whenthe command is highlighted.

    3. Use the resource editor to add keyboard accelerators. Open the IDR_MAINFRAMEaccelerator table by double-clickingon its icon in Resource View, and then click on the empty row entry at the bottom of the table to add the following items.

    Tip The resource editor's menu resource editor is intuitive, but you might need some help the first time you insert acommand in the middle of a menu. Just right-click where you want to insert the command and choose Insert Newfrom the shortcut menu. You'll automatically see where to add the command. To insert a separator, choose InsertSeparator from the shortcut menu.

    Menu Caption Command ID

    Edit Clear &Document ID_EDIT_CLEARDOCUMENTTransfer &Get Data From Document\tF2 ID_TRANSFER_GETDATAFROMDOCUMENT

    Transfer &Store Data In Document\tF3 ID_TRANSFER_STOREDATAINDOCUMENT

    Accelerator ID Key

    Page 6 of 136Part III: MFC's Document-View Architecture

    03-05-2014file://L:\TEMP\~hhA60B.htm

  • 5/26/2018 7_Part III MFC's Document-View Architecture

    7/136

    Be sure to select None from the drop-down list in the Modifier box to turn off the Ctrl, Alt, and Shift modifiers.

    4. Use Class View's Properties window for the CEx12aViewclass to add the view class command and update commanduser interface message handlers. Select the CEx12aView class, and then add the following member functions:

    5. Use Class View's Properties window for the CEx12aDocclass to add the document class command and updatecommand user interface message handlers.Select the CEx12aDocclass, and then add the following member functions:

    6. Insert the following line in the Ex12aDoc.cppfile:

    #include "Ex12aView.h"

    7. Add a CStringdata member to the CEx12aDocclass. Edit the file Ex12aDoc.hor use Class View.

    public:CString m_strText;

    8. Edit the document class member functions in Ex12aDoc.cpp. The OnNewDocumentfunction was generated byVisual Studio .NET. The framework calls this function after it first constructs the document and when the user chooses Newfrom the File menu. Your version sets some text in the string data member. Add the following boldface code:

    BOOL CEx12aDoc::OnNewDocument(){

    if (!CDocument::OnNewDocument())return FALSE;

    m_strText = "Hello (from CEx12aDoc::OnNewDocument)";return TRUE;}

    The Edit Clear Document message handler sets m_strTextto empty, and the update command user interface handler graysout the command if the string is already empty. Remember that the framework calls OnUpdateEditCleardocumentwhen theEdit menu is displayed. Add the following boldface code:

    void CEx12aDoc::OnEditCleardocument(){

    m_strText.Empty();//reflect changes to the viewsPOSITION pos = GetFirstViewPosition();

    while (pos != NULL){

    CEx12aView* pView = (CEx12aView*) GetNextView(pos);pView->m_rich.SetWindowText(m_strText);

    }}

    void CEx12aDoc::OnUpdateEditCleardocument(CCmdUI *pCmdUI){

    pCmdUI->Enable(!m_strText.IsEmpty());}

    9. Add a CRichEditCtrldata member to the CEx12aViewclass. Edit the file Ex12aView.hor use Class View.

    public:CRichEditCtrl m_rich;

    10. Use Class View's Properties window to map the WM_CREATEandWM_SIZEmessages in the CEx12aViewclass.TheOnCreatefunction creates the rich edit control. The control's size is 0 here because the view window doesn't have a size yet.

    ID_TRANSFER_GETDATAFROMDOCUMENT VK_F2

    ID_TRANSFER_STOREDATAINDOCUMENT VK_F3

    Object ID Event Member Function

    ID_TRANSFER_GETDATAFROMDOCUMENT COMMAND OnTransferGetdatafromdocument

    ID_TRANSFER_STOREDATAINDOCUMENT COMMAND OnTransferStoredataindocument

    ID_TRANSFER_STOREDATAINDOCUMENT UPDATE_COMMAND_UI OnUpdateTransferStoredataindocument

    Object ID Event Member Function

    ID_EDIT_CLEARDOCUMENT COMMAND OnEditCleardocument

    ID_EDIT_CLEARDOCUMENT UPDATE_COMMAND_UI

    OnUpdateEditCleardocument

    Page 7 of 136Part III: MFC's Document-View Architecture

    03-05-2014file://L:\TEMP\~hhA60B.htm

  • 5/26/2018 7_Part III MFC's Document-View Architecture

    8/136

    Add the following boldface code:

    int CEx12aView::OnCreate(LPCREATESTRUCT lpCreateStruct){

    CRect rect(0, 0, 0, 0);if (CView::OnCreate(lpCreateStruct) == -1)

    return -1;m_rich.Create(ES_AUTOVSCROLL | ES_MULTILINE | ES_WANTRETURN |

    WS_CHILD | WS_VISIBLE | WS_VSCROLL, rect, this, 1);return 0;

    }

    Windows sends the WM_SIZEmessage to the view as soon as the view's initial size is determined and again each time theuser changes the frame size. This handler simply adjusts the rich edit control's size to fill the view client area. Add thefollowing boldface code:

    void CEx12aView::OnSize(UINT nType, int cx, int cy){

    CRect rect;CView::OnSize(nType, cx, cy);GetClientRect(rect);m_rich.SetWindowPos(&wndTop, 0, 0, rect.right - rect.left,

    rect.bottom - rect.top, SWP_SHOWWINDOW);}

    11. Edit the menu command handler functions in Ex12aView.cpp. Visual Studio .NET generated these skeleton functionswhen you mapped the menu commands in step 4. The OnTransferGetdatafromdocumentfunction gets the text from thedocument data member and puts it in the rich edit control. The function then clears the control's modified flag. There is no

    update command user interface handler. Add the following boldface code:

    void CEx12aView::OnTransferGetdatafromdocument(){

    CEx12aDoc* pDoc = GetDocument();m_rich.SetWindowText(pDoc->m_strText);m_rich.SetModify(FALSE);

    }

    The OnTransferStoredataindocumentfunction copies the text from the view's rich edit control to the document string andresets the control's modified flag. The corresponding update command user interface handler grays out the command if thecontrol has not been changed since it was last copied to or from the document. Add the following boldface code:

    void CEx12aView::OnTransferStoredataindocument(){

    CEx12aDoc* pDoc = GetDocument();m_rich.GetWindowText(pDoc->m_strText);m_rich.SetModify(FALSE);

    }

    void CEx12aView::OnUpdateTransferStoredataindocument(CCmdUI* pCmdUI){

    pCmdUI->Enable(m_rich.GetModify());}

    12. Build and test the Ex12a application.When the application starts, the Clear Document command on the Edit menu shouldbe enabled. Choose Get Data From Document from the Transfer menu. Some text should appear. Edit the text, and thenchoose Store Data In Document. That command should now appear gray. Try choosing the Clear Document command, andthen choose Get Data From Document again.

    Property Sheets

    You've already seen property sheets in Visual C++ .NET and in many other modern Windows-based programs. A property sheet is a

    nice user interface element that allows you to cram lots of categorized information into a small dialog box. The user selects pages byclicking on their tabs. Windows offers a tab control that you can insert in a dialog box, but it's more likely that you'll want to put dialogboxes inside the tab control. The MFC library supports this, and the result is called a property sheet. The individual dialog boxes arecalledproperty pages.

    Building a Property Sheet

    Follow these general steps to build a property sheet using the Visual C++ .NET tools:

    1. Use the resource editor to create a series of dialog templates that are all approximately the same size. The captions are thestrings that you want to display on the tabs.

    2. Use the MFC Class Wizard to generate a class for each template. Select CPropertyPageas the base class. Add datamembers for the controls.

    Page 8 of 136Part III: MFC's Document-View Architecture

    03-05-2014file://L:\TEMP\~hhA60B.htm

  • 5/26/2018 7_Part III MFC's Document-View Architecture

    9/136

    3. Use the MFC Class Wizard to generate a single class derived from CPropertySheet.

    4. To the sheet class, add one data member for each page class.

    5. In the sheet class constructor, call theAddPagemember function for each page, specifying the address of the embeddedpage object.

    6. In your application, construct an object of the derived CPropertySheetclass, and then call DoModal. You must specify acaption in the constructor call, but you can change the caption later by calling CPropertySheet::SetTitle.

    7. Take care of programming for the Apply button.

    Property Sheet Data Exchange

    The framework puts three buttons on a property sheet (as in Figure 12-5in the next section.) Be aware that the framework calls theDialog Data Exchange (DDX) code for a property page each time the user switches to and from that page. As you would expect, theframework calls the DDX code for a page when the user clicks OK, thus updating that page's data members. From thesestatements, you can conclude that all data members for all pages are updated when the user clicks OK to exit the sheet. All this withno C++ programming on your part!

    Figure 12-5: The property sheet from Ex12a.

    What does the Apply button do? Nothing at all if you don't write some code. It won't even be enabled. To enable it for a given page,you must set the page's modified flag by calling SetModified(TRUE)when you detect that the user has made changes on the page.

    If you've enabled the Apply button, you can write a handler function for it in your page class by overriding the virtualCPropertyPage::OnApplyfunction. Don't try to understand property page message processing in the context of normal modal dialogboxes; it's quite different. The framework gets a WM_NOTIFYmessage for all button clicks. It calls the DDX code for the page if theOK or Apply button was clicked. It then calls the virtual OnApplyfunctions for allthe pages, and it resets the modified flag, whichdisables the Apply button. Don't forget that the DDX code has already been called to update the data members in all pages, so youneed to override OnApplyin only one page class.

    What you put in your OnApplyfunction is your business, but one option is to send a user-defined message to the object that createdthe property sheet. The message handler can get the property page data members and process them. Meanwhile, the propertysheet stays on the screen.

    The Ex12a Example Revisited

    Now we'll add a property sheet to Ex12a that allows the user to change the rich edit control's font characteristics. Of course, wecould use the standard MFC CFontDialogfunction, but then you wouldn't learn how to create property sheets. Figure 12-5shows theproperty sheet that you'll build as you continue with Ex12a.

    If you haven't built Ex12a, follow the instructions that begin on page 285to build it. If you already have Ex12a working with theTransfer menu commands, just continue on with these steps:

    1. Use the resource editor to edit the application's main menu.In Resource View, edit the IDR_MAINFRAMEmenuresource to add a Format menu that looks like this.

    Note With a normal modal dialog box, if the user clicks the Cancel button, the changes will be discarded and the dialog classdata members will remain unchanged. With a property sheet, however, the data members will be updated if the userchanges one page and then moves to another, even if the user exits by clicking the Cancel button.

    Page 9 of 136Part III: MFC's Document-View Architecture

    03-05-2014file://L:\TEMP\~hhA60B.htm

  • 5/26/2018 7_Part III MFC's Document-View Architecture

    10/136

    The MFC library has defined the following command IDs for the new Format menu commands.

    Add appropriate prompt strings for the two menu commands using the Properties window.

    2. Use Class View's Properties window to add the view class command and update command user interface messagehandlers. Select the CEx12aViewclass in Class View, and then add the following member functions.

    3. Use the resource editor to add four property page dialog templates.Right-click on the RC file in Resource View andchoose Add Resource from the shortcut menu. In the Add Resource dialog box, select the small property page template. Thetemplates are shown here with their associated IDs:

    Use the IDs listed below for the controls in the dialog boxes. Set theAuto Buddyand the Set Buddy Integerproperties for theSpin control, and set the Groupproperty for the IDC_FONT andIDC_COLORradio buttons. Set the minimum value ofIDC_FONTSIZEto 8and its maximum value to 24.

    Use the MFC Class Wizard to create the classes CPage1, CPage2, CPage3, and CPage4. In each case, selectCPropertyPageas the base class. Have the MFC Class Wizard generate the code for all these classes in the files

    Property.hand Property.cppby changing the filenames within the text boxes for the header file and the CPP file. WhenVisual Studio .NET asks you whether you want to merge the files, click Yes. Then add the data members shown here:

    Caption Command ID

    &Default ID_FORMAT_DEFAULT

    &Selection ID_FORMAT_SELECTION

    Object ID Event Member Function

    ID_FORMAT_DEFAULT COMMAND OnFormatDefault

    ID_FORMAT_SELECTION COMMAND OnFormatSelection

    ID_FORMAT_SELECTION UPDATE_COMMAND_UI

    OnUpdateFormatSelection

    Dialog Box Control ID Type Data Member

    Page 10 of 136Part III: MFC's Document-View Architecture

    03-05-2014file://L:\TEMP\~hhA60B.htm

  • 5/26/2018 7_Part III MFC's Document-View Architecture

    11/136

    Finally, use Class View's Properties window to override the OnInitDialogvirtual function for CPage4.

    4. Use the MFC Class Wizard to create a class derived from CPropertySheet. Select the name CFontSheet. Generate the

    code in the files Property.hand Property.cpp, the same files you used for the property page classes. The following codeshows these files with the added code in boldface:

    Property.h

    #pragma once// Property.h : header file//

    #define WM_USERAPPLY WM_USER + 5extern CView* g_pView;

    ////////////////////////////////////////////////////////////////////// CPage1 dialog

    class CPage1 : public CPropertyPage{

    DECLARE_DYNCREATE(CPage1)

    public:CPage1();virtual ~CPage1();

    // Dialog Dataenum { IDD = IDD_PAGE1 };int m_nFont;

    protected:virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDVvirtual BOOL OnApply();virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);

    DECLARE_MESSAGE_MAP()

    };

    ////////////////////////////////////////////////////////////////////

    // CPage2 dialog

    class CPage2 : public CPropertyPage{

    DECLARE_DYNCREATE(CPage2)

    public:CPage2();virtual ~CPage2();

    // Dialog Dataenum { IDD = IDD_PAGE2 };BOOL m_bBold;BOOL m_bItalic;

    IDD_PAGE1 First radio button IDC_FONT int m_nFont

    IDD_PAGE2 Bold checkbox

    IDC_BOLD BOOL m_bBold

    IDD_PAGE2 Italic checkbox

    IDC_ITALIC BOOL m_bItalic

    IDD_PAGE2 Underlinecheck box

    IDC_UNDERLINE BOOL m_bUnderline

    IDD_PAGE3 First radio button IDC_COLOR int m_nColor

    IDD_PAGE4 Edit control IDC_FONTSIZE

    int m_nFontSize

    IDD_PAGE4 Spin control IDC_SPIN1

    Page 11 of 136Part III: MFC's Document-View Architecture

    03-05-2014file://L:\TEMP\~hhA60B.htm

  • 5/26/2018 7_Part III MFC's Document-View Architecture

    12/136

    BOOL m_bUnderline;

    protected:virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDVvirtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);

    DECLARE_MESSAGE_MAP()

    };

    ////////////////////////////////////////////////////////////////////// CPage3 dialog

    class CPage3 : public CPropertyPage{

    DECLARE_DYNCREATE(CPage3)public:

    CPage3();virtual ~CPage3();

    // Dialog Dataenum { IDD = IDD_PAGE3 };int m_nColor;

    protected:virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDVvirtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);

    DECLARE_MESSAGE_MAP()};

    ////////////////////////////////////////////////////////////////////// CPage4 dialog

    class CPage4 : public CPropertyPage{

    DECLARE_DYNCREATE(CPage4)public:

    CPage4();virtual ~CPage4();

    // Dialog Dataenum { IDD = IDD_PAGE4 };int m_nFontSize;

    protected:virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV// support

    virtual BOOL OnCommand(WPARAM wParam, LPARAM lParam);DECLARE_MESSAGE_MAP()

    public:virtual BOOL OnInitDialog();

    };

    ////////////////////////////////////////////////////////////////////// CFontSheet

    class CFontSheet : public CPropertySheet{

    DECLARE_DYNAMIC(CFontSheet)public:

    CPage1 m_page1;CPage2 m_page2;CPage3 m_page3;CPage4 m_page4;

    public:CFontSheet(UINT nIDCaption, CWnd* pParentWnd = NULL,

    UINT iSelectPage = 0);CFontSheet(LPCTSTR pszCaption, CWnd* pParentWnd = NULL,

    UINT iSelectPage = 0);virtual ~CFontSheet();

    protected:DECLARE_MESSAGE_MAP()

    Page 12 of 136Part III: MFC's Document-View Architecture

    03-05-2014file://L:\TEMP\~hhA60B.htm

  • 5/26/2018 7_Part III MFC's Document-View Architecture

    13/136

    };

    Property.cpp

    // Property.cpp : implementation file

    #include "stdafx.h"#include "Ex12a.h"

    #include "Property.h"

    CView* g_pView;

    ////////////////////////////////////////////////////////////////////// CPage1 dialog

    IMPLEMENT_DYNCREATE(CPage1, CPropertyPage)

    CPage1::CPage1() : CPropertyPage(CPage1::IDD){

    m_nFont = -1;}

    CPage1::~CPage1(){

    }

    BOOL CPage1::OnApply(){

    TRACE("CPage1::OnApply\n");g_pView->SendMessage(WM_USERAPPLY);return TRUE;

    }

    BOOL CPage1::OnCommand(WPARAM wParam, LPARAM lParam){

    SetModified(TRUE);return CPropertyPage::OnCommand(wParam, lParam);

    }

    void CPage1::DoDataExchange(CDataExchange* pDX)

    { TRACE("Entering CPage1::DoDataExchange -- %d\n",pDX->m_bSaveAndValidate);

    CPropertyPage::DoDataExchange(pDX);DDX_Radio(pDX, IDC_FONT, m_nFont);

    }

    BEGIN_MESSAGE_MAP(CPage1, CPropertyPage)END_MESSAGE_MAP()

    ////////////////////////////////////////////////////////////////////// CPage1 message handlers

    ////////////////////////////////////////////////////////////////////// CPage2 dialog

    IMPLEMENT_DYNCREATE(CPage2, CPropertyPage)

    CPage2::CPage2() : CPropertyPage(CPage2::IDD){

    m_bBold = FALSE;m_bItalic = FALSE;m_bUnderline = FALSE;

    }

    CPage2::~CPage2(){}

    Page 13 of 136Part III: MFC's Document-View Architecture

    03-05-2014file://L:\TEMP\~hhA60B.htm

  • 5/26/2018 7_Part III MFC's Document-View Architecture

    14/136

    BOOL CPage2::OnCommand(WPARAM wParam, LPARAM lParam){

    SetModified(TRUE);return CPropertyPage::OnCommand(wParam, lParam);

    }

    void CPage2::DoDataExchange(CDataExchange* pDX){

    TRACE("Entering CPage2::DoDataExchange -- %d\n",pDX->m_bSaveAndValidate);

    CPropertyPage::DoDataExchange(pDX);DDX_Check(pDX, IDC_BOLD, m_bBold);DDX_Check(pDX, IDC_ITALIC, m_bItalic);DDX_Check(pDX, IDC_UNDERLINE, m_bUnderline);

    }

    BEGIN_MESSAGE_MAP(CPage2, CPropertyPage)END_MESSAGE_MAP()

    ////////////////////////////////////////////////////////////////////// CPage2 message handlers

    ////////////////////////////////////////////////////////////////////// CPage3 dialog

    IMPLEMENT_DYNCREATE(CPage3, CPropertyPage)

    CPage3::CPage3() : CPropertyPage(CPage3::IDD){

    m_nColor = -1;}

    CPage3::~CPage3(){}

    BOOL CPage3::OnCommand(WPARAM wParam, LPARAM lParam){

    SetModified(TRUE);return CPropertyPage::OnCommand(wParam, lParam);

    }

    void CPage3::DoDataExchange(CDataExchange* pDX){TRACE("Entering CPage3::DoDataExchange -- %d\n",

    pDX->m_bSaveAndValidate);CPropertyPage::DoDataExchange(pDX);DDX_Radio(pDX, IDC_COLOR, m_nColor);

    }

    BEGIN_MESSAGE_MAP(CPage3, CPropertyPage)END_MESSAGE_MAP()

    ////////////////////////////////////////////////////////////////////// CPage3 message handlers

    ////////////////////////////////////////////////////////////////////// CPage4 dialog

    IMPLEMENT_DYNCREATE(CPage4, CPropertyPage)

    CPage4::CPage4() : CPropertyPage(CPage4::IDD){

    m_nFontSize = 0;}

    CPage4::~CPage4(){}

    BOOL CPage4::OnCommand(WPARAM wParam, LPARAM lParam){

    Page 14 of 136Part III: MFC's Document-View Architecture

    03-05-2014file://L:\TEMP\~hhA60B.htm

  • 5/26/2018 7_Part III MFC's Document-View Architecture

    15/136

    SetModified(TRUE);return CPropertyPage::OnCommand(wParam, lParam);

    }

    void CPage4::DoDataExchange(CDataExchange* pDX){

    TRACE("Entering CPage4::DoDataExchange -- %d\n",pDX->m_bSaveAndValidate);

    CPropertyPage::DoDataExchange(pDX);DDX_Text(pDX, IDC_FONTSIZE, m_nFontSize);

    DDV_MinMaxInt(pDX, m_nFontSize, 8, 24);}

    BEGIN_MESSAGE_MAP(CPage4, CPropertyPage)END_MESSAGE_MAP()

    ////////////////////////////////////////////////////////////////////// CPage4 message handlers

    BOOL CPage4::OnInitDialog(){

    CPropertyPage::OnInitDialog();((CSpinButtonCtrl*) GetDlgItem(IDC_SPIN1))->SetRange(8, 24);return TRUE; // return TRUE unless you set the focus to a control

    // EXCEPTION: OCX Property Pages should return FALSE}

    ////////////////////////////////////////////////////////////////////// CFontSheet

    IMPLEMENT_DYNAMIC(CFontSheet, CPropertySheet)CFontSheet::CFontSheet(UINT nIDCaption, CWnd* pParentWnd,

    UINT iSelectPage):CPropertySheet(nIDCaption, pParentWnd, iSelectPage)

    {}

    CFontSheet::CFontSheet(LPCTSTR pszCaption, CWnd* pParentWnd,UINT iSelectPage)

    :CPropertySheet(pszCaption, pParentWnd, iSelectPage){AddPage(&m_page1);

    AddPage(&m_page2);AddPage(&m_page3);AddPage(&m_page4);

    }

    CFontSheet::~CFontSheet(){}BEGIN_MESSAGE_MAP(CFontSheet, CPropertySheet)END_MESSAGE_MAP()

    ////////////////////////////////////////////////////////////////////// CFontSheet message handlers

    5. Insert the following line in the Ex12aView.hfile:

    #include "Property.h"

    6. Add two data members and two prototypes to the CEx12aViewclass:

    private:CFontSheet m_sh;BOOL m_bDefault; // TRUE default format, FALSE selection

    Now add the prototype for the private function Format:

    void Format(CHARFORMAT &cf);

    Insert the prototype for the protected function OnUserApplybefore the DECLARE_MESSAGE_MAPmacro:

    afx_msg LRESULT OnUserApply(WPARAM wParam, LPARAM lParam);

    Page 15 of 136Part III: MFC's Document-View Architecture

    03-05-2014file://L:\TEMP\~hhA60B.htm

  • 5/26/2018 7_Part III MFC's Document-View Architecture

    16/136

    7. Edit and add code in the file Ex12aView.cpp.Map the user-defined WM_USERAPPLYmessage, as shown here:

    ON_MESSAGE(WM_USERAPPLY, OnUserApply)

    Add the following lines to the OnCreatefunction, just before the return 0statement:

    CHARFORMAT cf;Format(cf);m_rich.SetDefaultCharFormat(cf);

    Edit the view constructor to set default values for the property sheet data members, as follows:

    CEx12aView::CEx12aView() : m_sh(""){

    m_sh.m_page1.m_nFont = 0;m_sh.m_page2.m_bBold = FALSE;m_sh.m_page2.m_bItalic = FALSE;m_sh.m_page2.m_bUnderline = FALSE;m_sh.m_page3.m_nColor = 0;m_sh.m_page4.m_nFontSize = 12;g_pView = this;

    m_bDefault = TRUE;}

    Edit the format command handlers, as shown here:

    void CEx12aView::OnFormatDefault(){

    m_sh.SetTitle("Default Format");m_bDefault = TRUE;m_sh.DoModal();

    }

    void CEx12aView::OnFormatSelection(){

    m_sh.SetTitle("Selection Format");m_bDefault = FALSE;m_sh.DoModal();

    }

    void CEx12aView::OnUpdateFormatSelection(CCmdUI* pCmdUI){

    long nStart, nEnd;m_rich.GetSel(nStart, nEnd);pCmdUI->Enable(nStart != nEnd);

    }

    Add the following handler for the user-defined WM_USERAPPLYmessage:

    LRESULT CEx12aView::OnUserApply(WPARAM wParam, LPARAM lParam){

    TRACE("CEx12aView::OnUserApply -- wParam = %x\n", wParam);CHARFORMAT cf;Format(cf);if (m_bDefault) {

    m_rich.SetDefaultCharFormat(cf);}else {

    m_rich.SetSelectionCharFormat(cf);}return 0;

    }

    Add the Formathelper function, as shown below, to set a CHARFORMATstructure based on the values of the property sheetdata members:

    void CEx12aView::Format(CHARFORMAT& cf){

    cf.cbSize = sizeof(CHARFORMAT);cf.dwMask = CFM_BOLD | CFM_COLOR | CFM_FACE |

    CFM_ITALIC | CFM_SIZE | CFM_UNDERLINE;cf.dwEffects = (m_sh.m_page2.m_bBold ? CFE_BOLD : 0) |

    (m_sh.m_page2.m_bItalic ? CFE_ITALIC : 0) |(m_sh.m_page2.m_bUnderline ? CFE_UNDERLINE : 0);

    cf.yHeight = m_sh.m_page4.m_nFontSize * 20;switch(m_sh.m_page3.m_nColor) {

    Page 16 of 136Part III: MFC's Document-View Architecture

    03-05-2014file://L:\TEMP\~hhA60B.htm

  • 5/26/2018 7_Part III MFC's Document-View Architecture

    17/136

    case -1:case 0:

    cf.crTextColor = RGB(0, 0, 0);break;

    case 1:cf.crTextColor = RGB(255, 0, 0);break;

    case 2:cf.crTextColor = RGB(0, 255, 0);break;

    }switch(m_sh.m_page1.m_nFont) {case -1:case 0:

    strncpy(cf.szFaceName, "Times New Roman" ,LF_FACESIZE);break;

    case 1:strncpy(cf.szFaceName, "Arial" ,LF_FACESIZE);break;

    case 2:strncpy(cf.szFaceName, "Courier New" ,LF_FACESIZE);break;

    }cf.bCharSet = 0;cf.bPitchAndFamily = 0;

    }

    8. Build and test the enhanced Ex12a application. Type some text, and then choose Default from the Format menu.Observe the TRACEmessages in the Debug window as you click on property sheet tabs and click the Apply button. Tryhighlighting some text and then formatting the selection.

    Apply Button Processing

    You might be curious about the way the property sheet classes process the Apply button. In all the page classes, the overriddenOnCommandfunctions enable the Apply button whenever a control sends a message to the page. This works fine for pages 1through 3 in Ex12a, but for page 4, OnCommandis called during the initial conversation between the Spin control and its buddy.

    The OnApplyvirtual override in the CPage1class sends a user-defined message to the view. The function finds the view in anexpedient wayby using a global variable set by the view class. A better approach would be to pass the view pointer to the sheetconstructor and then to the page constructor.

    The view class calls the property sheet's DoModalfunction for both default formatting and selection formatting. It sets the

    m_bDefaultflag to indicate the mode. We don't need to check the return from DoModalbecause the user-defined message is sentfor both the OK button and the Apply button. If the user clicks Cancel, no message is sent.

    The CMenuClass

    Up to this point, the application framework and the menu editor have shielded you from the menu class, CMenu. A CMenuobjectcan represent each Windows menu, including the top-level menu commands and submenus. Most of the time, the menu's resourceis directly attached to a frame window when the window's Createor LoadFramefunction is called, and a CMenuobject is neverexplicitly constructed. The CWndmember function GetMenureturns a temporary CMenupointer. Once you have this pointer, youcan freely access and update the menu object.

    Suppose you want to switch menus after the application starts. IDR_MAINFRAMEalways identifies the initial menu in the resourcescript. If you want a second menu, you use the menu editor to create a menu resource with your own ID. Then, in your program, youconstruct a CMenuobject, use the CMenu::LoadMenufunction to load the menu from the resource, and call the CWnd::SetMenufunction to attach the new menu to the frame window. You then call the Detachmember function to separate the object's HMENUhandle so the menu is not destroyed when the CMenuobject goes out of scope.

    You can use a resource to define a menu, and then your program can modify the commands at run time. If necessary, however, youcan build the whole menu at run time, without benefit of a resource. In either case, you can use CMenumember functions such asModifyMenu, InsertMenu, and DeleteMenu. Each of these functions operates on an individual command identified by ID or by arelative position index.

    A menu object is actually composed of a nested structure of submenus. You can use the GetSubMenumember function to get aCMenupointer to a submenu contained in the main CMenuobject. The CMenu::GetMenuStringfunction returns the menu commandstring corresponding to either a zero-based index or a command ID. If you use the command ID option, the menu is searched,together with any submenus.

    Creating Floating Shortcut Menus

    Page 17 of 136Part III: MFC's Document-View Architecture

    03-05-2014file://L:\TEMP\~hhA60B.htm

  • 5/26/2018 7_Part III MFC's Document-View Architecture

    18/136

    Floating shortcut menus are one of the latest trends in user interface design. The user clicks the right mouse button and a floatingmenu offers commands that relate to the current selection. It's easy to create these menus using the resource editor and the MFClibrary CMenu::TrackPopupMenufunction. Just follow these steps:

    1. Use the menu editor to insert a new, empty menu in your project's resource file.

    2. Type some characters in the left top-level command, and then add commands in the resulting shortcut menu.

    3. Use Class View's Properties window to add a WM_CONTEXTMENUmessage handler in your view class or in some otherwindow class that receives mouse-click messages. Code the handler as shown here:

    void CMyView::OnContextMenu(CWnd *pWnd, CPoint point){

    CMenu menu;menu.LoadMenu(IDR_MYFLOATINGMENU);menu.GetSubMenu(0)

    ->TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON,point.x, point.y, this);

    }

    You can use Class View's Properties window to map the floating menu's command IDs in the same way you would map theframe menu's command IDs.

    Extended Command Processing

    In addition to the ON_COMMANDmessage map macro, the MFC library provides an extended variation, ON_COMMAND_EX. The

    extended command message map macro provides two features not supplied by the regular command messagea command IDfunction parameter and the ability to reject a command at run time, sending it to the next object in the command route. If theextended command handler returns TRUE, the command goes no further; if it returns FALSE, the application framework looks foranother command handler.

    The command ID parameter is useful when you want one function to handle several related command messages. You might inventsome of your own uses for the rejection feature.

    The code wizards available from Class View's Properties window can't help you with extended command handlers, so you have todo the coding yourself, outside theAFX_MSG_MAPbrackets. Assume that IDM_ZOOM_1and IDM_ZOOM_2 are related command

    IDs defined in Resource.h. Here's the class code you need to process both messages with one function, OnZoom:

    BEGIN_MESSAGE_MAP(CMyView, CView)ON_COMMAND_EX(IDM_ZOOM_1, OnZoom)ON_COMMAND_EX(IDM_ZOOM_2, OnZoom)

    END_MESSAGE_MAP()

    BOOL CMyView::OnZoom(UINT nID){

    if (nID == IDM_ZOOM_1) {// code specific to first zoom command

    }else {

    // code specific to second zoom command}// code common to both commandsreturn TRUE; // Command goes no further

    }

    Here's the function prototype:

    afx_msg BOOL OnZoom(UINT nID);

    Other MFC message map macros are helpful for processing ranges of commands, as you might see in dynamic menu applications.These macros include ON_COMMAND_RANGE, ON_COMMAND_EX_RANGE, and ON_UPDATE_COMMAND_UI_RANGE.

    If the values of IDM_ZOOM_1and IDM_ZOOM_2were consecutive, you could rewrite the CMyViewmessage map as follows:

    BEGIN_MESSAGE_MAP(CMyView, CView)ON_COMMAND_EX_RANGE(IDM_ZOOM_1, IDM_ZOOM_2, OnZoom)

    END_MESSAGE_MAP()

    Now OnZoomis called for both menu commands, and the handler can determine the command from the integer parameter.

    Chapter 13: Toolbars and Status Bars

    Overview

    Page 18 of 136Part III: MFC's Document-View Architecture

    03-05-2014file://L:\TEMP\~hhA60B.htm

  • 5/26/2018 7_Part III MFC's Document-View Architecture

    19/136

    All of the book's Microsoft Visual C++ examples up to this point have included toolbars and status bars. The MFC Application Wizardgenerates the code that initializes these application framework elements if you accept the wizard's default Standard Docking Toolbarand Initial Status Bar user interface features. The default toolbar provides graphics equivalents for many of the standard applicationframework menu commands, and the default status bar displays menu prompts together with the keyboard state indicators CAP,NUM, and SCRL.

    This chapter shows you how to customize the toolbar and the status bar for your application. You can add your own toolbargraphical buttons and control their appearance. You can also disable the status bar's normal display of menu prompts and keyboardindicators so that your application can take over the status bar for its own use.

    Control Bars and the Application Framework

    The toolbar is an object of class CToolBar, and the status bar is an object of class CStatusBar. Both of these classes are derivedfrom class CControlBar, which is itself derived from CWnd. The CControlBarclass supports control bar windows that are positionedinside frame windows. These control bar windows resize and reposition themselves as the parent frame moves and changes size.The application framework takes care of the construction and destruction of the control bar objects and window creation. The MFC

    Application Wizard generates control bar code for its derived frame class located in the files MainFrm.cppand MainFrm.h.

    In a typical Single Document Interface (SDI) application, a CToolBarobject occupies the top portion of the CMainFrameclient areaand a CStatusBarobject occupies the bottom portion. The view occupies the remaining (middle) part of the frame.

    Beginning with version 4.0 of the Microsoft Foundation Class (MFC) library, the toolbar has been built around the toolbar commoncontrol that was first introduced with Microsoft Windows 95. Thus the toolbar is fully dockable. The programming interface is muchthe same as it was in earlier versions of the MFC library, however. The button images are easy to work with because a specialresource type is supported by the resource editor.

    Assuming that MFC Application Wizard has generated the control bar code for your application, the user can enable and disable thetoolbar or the status bar individually by choosing commands from the application's View menu. When a control bar is disabled, itdisappears and the view size is recalculated. Apart from the common behavior just described, toolbar and status bar objects operateindependently of each other and have rather different characteristics.

    Version 6.0 of the MFC library, introduced a new MFC toolbar called the rebar. The rebar is based on the controls that come with thecommon controls and provides a Microsoft Internet Explorerstyle "sliding" toolbar. I'll cover the rebar later in this chapter.

    Toolbars

    A toolbar consists of a number of horizontally (or vertically) arranged graphical buttons that might be clustered in groups. Theprogramming interface determines the grouping. The graphical images for the buttons are stored in a single bitmap that is attachedto the application's resource file. When a button is clicked, it sends a command message, as menus and keyboard accelerators do.An update command user interface message handler is used to update the button's state, which in turn is used by the applicationframework to modify the button's graphical image.

    The Toolbar Bitmap

    Each button on a toolbar appears to have its own bitmap, but actually a single bitmap serves the entire toolbar. The toolbar bitmaphas a tile, 15 pixels high and 16 pixels wide, for each button. The application framework supplies the button borders, and it modifiesthese borders, together with the button's bitmap tile color, to reflect the current button state. Figure 13-1shows the relationshipbetween the toolbar bitmap and the corresponding toolbar.

    Figure 13-1: A toolbar bitmap and an actual toolbar.

    The toolbar bitmap is stored in the file Toolbar.bmpin the application's \res subdirectory. The bitmap is identified in the resourcescript (RC) file as IDR_MAINFRAME. You don't edit the toolbar bitmap directly; instead, you use Microsoft Visual Studio's specialtoolbar editing facility.

    Toolbar Button States

    Each toolbar button can assume the states listed in Table 13-1. (There are additional states for later toolbar versions.)

    Table 13-1: Toolbar States

    State Description

    0 Normal, unpressed state.

    Page 19 of 136Part III: MFC's Document-View Architecture

    03-05-2014file://L:\TEMP\~hhA60B.htm

  • 5/26/2018 7_Part III MFC's Document-View Architecture

    20/136

    A toolbar button can be a pushbutton, which is down only when currently clicked by the mouse, or it can be a check box button,which can be toggled up and down with mouse clicks. All toolbar buttons in the standard application framework toolbar arepushbuttons.

    The Toolbar and Command Messages

    When the user clicks a toolbar button with the mouse, a command message is generated. This message is routed like the menucommand messages you saw in Chapter 12. Most of the time, a toolbar button matches a menu command. In the standardapplication framework toolbar, for example, the Disk button is equivalent to the File Save menu commandboth generate theID_FILE_SAVEcommand. The object receiving the command message doesn't need to know whether the message was producedby a button click or by the menu command.

    A toolbar button doesn't have to mirror a menu command. If you don't provide an equivalent menu command, however, you shoulddefine a keyboard accelerator for the button so the user can activate the command with the keyboard or with a keyboard macro

    product for Windows. You can use Class View and the Properties window to define commands and update command user interfacemessage handlers for toolbar buttons, whether or not they have corresponding menu commands.

    A toolbar has an associated bitmap resource and, in the RC file, a companion toolbar resource that defines the menu commandsassociated with the buttons. Both the bitmap and the toolbar resource have the same ID, typically IDR_MAINFRAME. The text of thetoolbar resource generated by the MFC Application Wizard is shown here:

    IDR_MAINFRAME TOOLBAR 16, 15BEGIN

    BUTTON ID_FILE_NEWBUTTON ID_FILE_OPENBUTTON ID_FILE_SAVE

    SEPARATORBUTTON ID_EDIT_CUTBUTTON ID_EDIT_COPYBUTTON ID_EDIT_PASTE

    SEPARATORBUTTON ID_FILE_PRINTBUTTON ID_APP_ABOUT

    END

    The SEPARATORconstants serve to group the buttons by inserting corresponding spaces on the toolbar. If the number of toolbarbitmap panes exceeds the number of resource elements (excluding separators), the extra buttons are not displayed.

    When you edit the toolbar using the resource editor, you're editing both the bitmap resource and the toolbar resource. You select abutton image, and then you edit the properties, including the button's ID, in the Properties window.

    Toolbar Update Command User Interface Message Handlers

    You'll recall from Chapter 12that update command user interface message handlers are used to disable or add check marks tomenu commands. These same message handlers apply to toolbar buttons. If your update command user interface message handlercalls the CCmdUI::Enablemember function with a FALSEparameter, the corresponding button will be set to the disabled (grayed-out) state and no longer respond to mouse clicks.

    Next to a menu command, the CCmdUI::SetCheckmember function displays a check mark. For the toolbar, the SetCheckfunctionimplements check box buttons. If the update command user interface message handler calls SetCheckwith a parameter value of 1,the button will be toggled to the down (checked) state; if the parameter is 0, the button will be toggled up (unchecked).

    The update command user interface message handlers for a shortcut menu are called only when the menu is painted. The toolbar isdisplayed all the time, so when are its update command user interface message handlers called? They're called during theapplication's idle processing so the buttons can be updated continuously. If the same handler covers a menu command and atoolbar button, it is called both during idle processing and when the shortcut menu is displayed.

    ToolTips

    TBSTATE_CHECKED Checked (down) state.

    TBSTATE_ENABLED Available for use. Button is grayed-out and unavailable if this state is not set.

    TBSTATE_HIDDEN Not visible.

    TBSTATE_INDETERMINATE Grayed-out.

    TBSTATE_PRESSED Currently selected (pressed) using the mouse.

    TBSTATE_WRAP Line break follows the button.

    Note If the SetCheckparameter value is 2, the button will be set to the indeterminate state. This state looks like the disabledstate, but the button is still active and its color is a bit brighter.

    Page 20 of 136Part III: MFC's Document-View Architecture

    03-05-2014file://L:\TEMP\~hhA60B.htm

  • 5/26/2018 7_Part III MFC's Document-View Architecture

    21/136

    You've seen ToolTips in various Windows applications, including Visual Studio. When the user positions the mouse on a toolbarbutton for a certain interval of time, text is displayed in a little ToolTip box next to the button. In Chapter 12, you learned that menucommands can have associated prompt strings, which are string resource elements with matching IDs. To create a ToolTip, yousimply add the tip text to the end of the menu prompt, preceded by a newline ( \n) character. The resource editor lets you edit theprompt string while you're editing the toolbar images. Just select a toolbar image and edit the Prompt property in the Propertieswindow.

    Locating the Main Frame Window

    The toolbar and status bar objects you'll be working with are attached to the application's main frame window, not to the viewwindow. How does your view find its main frame window? In an SDI application, you can use the CWnd::GetParentFrame function.Unfortunately, this function won't work in an MDI application because the view's parent frame is the MDI child frame, not the MDIframe window.

    If you want your view class to work in both SDI and MDI applications, you must find the main frame window through the applicationobject. TheAfxGetAppglobal function returns a pointer to the application object. You can use that pointer to get the CWinAppdatamember m_pMainWnd. In an MDI application, the MFC Application Wizard generates code that sets m_pMainWnd, but in an SDIapplication, the framework sets m_pMainWndduring the view creation process. Once m_pMainWndis set, you can use it in a viewclass to get the frame's toolbar with statements such as this:

    CMainFrame* pFrame = (CMainFrame*) AfxGetApp()->m_pMainWnd;CToolBar* pToolBar = &pFrame->m_wndToolBar;

    You can use similar logic to locate menu commands, status bar objects, and dialog objects.

    The Ex13a Example: Using Toolbars

    In this example, we'll add three special-purpose buttons that control drawing in the view window. We'll also construct a Draw menuwith three commands, as follows:

    The menu and toolbar choices force the user to alternate between drawing circles and squares. After the user draws a circle, theCircle command and toolbar button are disabled; after the user draws a square, the Square command and toolbar button aredisabled.

    On the application's Draw menu, the Pattern command gets a check mark when pattern fill is active. On the toolbar, thecorresponding button is a check box button that is down when pattern fill is active and up when it is not active.

    Figure 13-2shows the application in action. The user has just drawn a square with pattern fill. Notice the states of the three drawingbuttons.

    Figure 13-2: The Ex13a program in action.

    The Ex13a example introduces the resource editor for toolbars. You'll need to do very little C++ coding. Simply follow these steps:

    Note You'll need to cast m_pMainWndfrom CFrameWnd* to CMainFrame* because m_wndToolBaris a member of that derivedclass. You'll also have to make m_wndToolBarpublic or make your class a friend of CMainFrame.

    Note In an SDI application, the value of m_pMainWndis not set when the view's OnCreatemessage handler is called. If youneed to access the main frame window in your OnCreatefunction, you must use the GetParentFramefunction.

    Command Function

    Circle Draws a circle in the view window

    Square Draws a square in the view window

    Pattern Toggles a diagonal line fill pattern for new squares and circles

    Page 21 of 136Part III: MFC's Document-View Architecture

    03-05-2014file://L:\TEMP\~hhA60B.htm

  • 5/26/2018 7_Part III MFC's Document-View Architecture

    22/136

    1. Run the MFC Application Wizard to generate a project named Ex13a. Choose New Project from Visual Studio's Filemenu. In the New Project dialog box, select the MFC Application template, type the name Ex13a, and click OK. In the MFCApplication Wizard, accept all the defaults but two: On the Application Type page, select Single Document, and on theAdvanced Features page, deselect Printing And Print Preview.

    2. Use the resource editor to edit the application's main menu. In Resource View, double-click on IDR_MAINFRAMEunderMenu. Edit the IDR_MAINFRAMEmenu resource to create a new Draw menu that looks like the following. To reposition amenu, you can just drag the menu.

    In the Properties window, verify that the following properties are set for your new Draw menu commands:

    3. Use the resource editor to update the application's toolbar.Edit the IDR_MAINFRAMEtoolbar resource to create agroup of three new buttons that looks like this:

    The toolbar editor is fairly intuitive. You add new buttons by editing the blank button at the far right of the toolbar. Use theEllipsis, Rectangle, and Line tools on the Image Editor toolbar to draw on a button. You can move buttons around by draggingthem with the mouse. To add a separator between buttons, drag the button where the separator should appear slightly to theright or left and the buttons will be nudged over. The Delete key erases a button's pixels. If you want to eliminate a buttonentirely, just drag it off the toolbar.

    In the Properties window, set the ID property for the new buttons to ID_DRAW_CIRCLE, ID_DRAW_SQUARE, andID_DRAW_PATTERN.

    4. Add the CEx13aView class message handlers. Select the CEx13aViewclass in Class View, click the Events button in theProperties window, and add message handlers for the following command and update command user interface messages:

    Caption ID Prompt

    Circle ID_DRAW_CIRCLE Draw a circle\nCircle

    Square ID_DRAW_SQUARE Draw a square\nSquare

    Pattern ID_DRAW_PATTERN Change the pattern\nPattern

    Object ID Message Member Function

    ID_DRAW_CIRCLE COMMAND OnDrawCircle

    ID_DRAW_CIRCLE UPDATE_COMMAND_UI OnUpdateDrawCircle

    ID_DRAW_PATTERN COMMAND OnDrawPattern

    ID_DRAW_PATTERN UPDATE_COMMAND_UI OnUpdateDrawPattern

    ID_DRAW_SQUARE COMMAND OnDrawSquare

    ID_DRAW_SQUARE UPDATE_COMMAND_UI OnUpdateDrawSquare

    Page 22 of 136Part III: MFC's Document-View Architecture

    03-05-2014file://L:\TEMP\~hhA60B.htm

  • 5/26/2018 7_Part III MFC's Document-View Architecture

    23/136

    5. Add three data members to the CEx13aViewclass. Add the following code to Ex13aView.h:

    protected:CRect m_rect;BOOL m_bCircle;BOOL m_bPattern;

    6. Edit the Ex13aView.cppfile. The CEx13aViewconstructor simply initializes the class data members. Add the followingboldface code:

    CEx13aView::CEx13aView() : m_rect(0, 0, 100, 100){m_bCircle = TRUE;m_bPattern = FALSE;

    }

    The OnDrawfunction draws an ellipse or a rectangle, depending on the value of the m_bCircleflag. The brush is plain whiteor a diagonal pattern, depending on the value of m_bPattern.

    void CEx13aView::OnDraw(CDC*pDC){

    Cex13aDoc* pDoc = GetDocument();ASSERT_VALID(pDoc);

    CBrush brush(HS_BDIAGONAL, 0L); // brush with diagonal pattern

    if (m_bPattern) {

    pDC->SelectObject(&brush);}else {

    pDC->SelectStockObject(WHITE_BRUSH);}if (m_bCircle) {

    pDC->Ellipse(m_rect);}else {

    pDC->Rectangle(m_rect);}

    pDC->SelectStockObject(WHITE_BRUSH); // Deselects brush// if selected

    }

    The OnDrawCirclefunction handles the ID_DRAW_CIRCLEcommand message, and the OnDrawSquarefunction handlesthe ID_DRAW_SQUAREcommand message. These two functions move the drawing rectangle down and to the right, and

    then they invalidate the rectangle, causing the OnDrawfunction to redraw it. The effect of this invalidation strategy is adiagonal cascading of alternating squares and circles. Also, the display is not buffered, so when the window is hidden orminimized, previously drawn items are not redisplayed.

    void CEx13aView::OnDrawCircle(){

    m_bCircle = TRUE;m_rect += CPoint(25, 25);InvalidateRect(m_rect);

    }

    void CEx13aView::OnDrawSquare(){

    m_bCircle = FALSE;m_rect += CPoint(25, 25);InvalidateRect(m_rect);

    }

    The following two update command user interface functions alternately enable and disable the Circle and Square buttons andcorresponding menu commands. Only one item can be enabled at a time.

    void CEx13aView::OnUpdateDrawCircle(CCmdUI* pCmdUI){

    pCmdUI->Enable(!m_bCircle);}

    void CEx13aView::OnUpdateDrawSquare(CCmdUI* pCmdUI){

    pCmdUI->Enable(m_bCircle);}

    The OnDrawPatternfunction toggles the state of the m_bPatternflag.

    Page 23 of 136Part III: MFC's Document-View Architecture

    03-05-2014file://L:\TEMP\~hhA60B.htm

  • 5/26/2018 7_Part III MFC's Document-View Architecture

    24/136

    void CEx13aView::OnDrawPattern(){

    m_bPattern = 1;}

    The OnUpdateDrawPatternfunction updates the Pattern button and menu command according to the state of the m_bPatternflag. The toolbar button appears to move in and out, and the command check mark appears and disappears.

    void CEx13aView::OnUpdateDrawPattern(CCmdUI* pCmdUI){

    pCmdUI->SetCheck(m_bPattern);}

    7. Build and test the Ex13a application. Notice the behavior of the toolbar buttons. Try the corresponding menu commands,and notice that they too are enabled, disabled, and checked as the application's state changes. Observe the ToolTip and theprompt in the status bar when you stop the mouse pointer on one of the new toolbar buttons.

    Status Bars

    The status bar window neither accepts user input nor generates command messages. Its job is simply to display text in panes underprogram control. The status bar supports two types of text panesmessage line panes and status indicator panes. To use thestatus bar for application-specific data, you must first disable the standard status bar that displays the menu prompt and keyboardstatus.

    The Status Bar Definition

    The static indicatorsarray that the MFC Application Wizard generates in the MainFrm.cppfile defines the panes for theapplication's status bar. The constant ID_SEPARATORidentifies a message line pane; the other constants are string resource IDsthat identify indicator panes. Figure 13-3shows the indicatorsarray and its relationship to the standard framework status bar.

    Figure 13-3: The status bar and the indicators array.

    The CStatusBar::SetIndicatorsmember function, called in the application's derived frame class, configures the status bar according

    to the contents of the indicatorsarray.

    The Message Line

    A message line pane displays a string that the program supplies dynamically. To set the value of the message line, you must firstget access to the status bar object and then you must call the CStatusBar::SetPaneTextmember function with a zero-based indexparameter. Pane 0 is the leftmost pane, 1 is the next pane to the right, and so forth.

    The following code fragment is part of a view class member function. Note that you must navigate up to the application object andthen back down to the main frame window.

    CMainFrame* pFrame = (CMainFrame*) AfxGetApp()->m_pMainWnd;CStatusBar* pStatus = &pFrame->m_wndStatusBar;pStatus->SetPaneText(0, "message line for first pane");

    Normally, the length of a message line pane is exactly one-fourth the width of the display. If, however, the message line is the first(index 0) pane, it is a stretchy pane without a beveled border. Its minimum length is one-fourth the display width, and it expands if

    room is available in the status bar.

    The Status Indicator

    A status indicator pane is linked to a single resource-supplied string that is displayed or hidden by logic in an associated updatecommand user interface message handler function. An indicator is identified by a string resource ID, and that same ID is used toroute update command user interface messages. The Caps Lock indicator is handled in the frame class by a message map entryand a handler function equivalent to those shown below. The Enablefunction turns on the indicator if the Caps Lock mode is set.

    ON_UPDATE_COMMAND_UI(ID_INDICATOR_CAPS, OnUpdateKeyCapsLock)

    void CMainFrame::OnUpdateKeyCapsLock(CCmdUI* pCmdUI){

    Page 24 of 136Part III: MFC's Document-View Architecture

    03-05-2014file://L:\TEMP\~hhA60B.htm

  • 5/26/2018 7_Part III MFC's Document-View Architecture

    25/136

    pCmdUI->Enable(::GetKeyState(VK_CAPITAL) & 1);}

    The status bar update command user interface functions are called during idle processing so that the status bar is updatedwhenever your application receives messages.

    The length of a status indicator pane is the exact length of the corresponding resource string.

    Taking Control of the Status Bar

    In the standard application framework implementation, the status bar has the child window ID AFX_IDW_STATUS_BAR. Theapplication framework looks for this ID when it wants to display a menu prompt. The update command user interface handlers forthe keyboard state indicators, embedded in the frame window base class, are linked to the following string IDs:ID_INDICATOR_CAPS, ID_INDICATOR_NUM, and ID_INDICATOR_SCRL . To take control of the status bar, you must use adifferent child window ID and different indicator ID constants.

    The status bar window ID is assigned in the CStatusBar::Createfunction called by the derived frame class OnCreatemember

    function. That function is contained in the MainFrm.cppfile that the MFC Application Wizard generates. The window ID is the thirdCreateparameter, and it defaults toAFX_IDW_STATUS_BAR.

    To assign your own ID, you must replace this call

    m_wndStatusBar.Create(this);

    with this callm_wndStatusBar.Create(this, WS_CHILD | WS_VISIBLE | CBRS_BOTTOM,

    ID_MY_STATUS_BAR);

    You must also, of course, define the ID_MY_STATUS_BARconstant in the resource.hfile (using Visual C++'s resource symboleditor).

    We left out one thing. The standard application framework's View menu allows the user to turn the status bar on and off. That logic ispegged to theAFX_IDW_STATUS_BARwindow ID, so you have to change the menu logic, too. In your derived frame class, youmust write message map entries and handlers for the ID_VIEW_STATUS_BARcommand and update command user interfacemessages. ID_VIEW_STATUS_BARis the ID of the Status Bar menu command. The derived class handlers override the standardhandlers in the CFrameWndbase class. See the upcoming Ex13b example for code details.

    The Ex13b Example: Using Status Bars

    The Ex13b example replaces the standard application framework status bar with a new status bar that has the following text panes:

    The resulting status bar is shown in Figure 13-4. Notice that the leftmost pane stretches past its normal screen length as thedisplayed frame window expands.

    Figure 13-4: The status bar of the Ex13b example.

    Follow these steps to produce the Ex13b example:

    1. Run the MFC Application Wizard to generate a project named Ex13b. Choose New Project from Visual Studio's File

    Note The only reason to change the status bar's child window ID is to prevent the framework from writing menu prompts in pane0. If you like the menu prompts, you can disregard the following instructions.

    Pane Index String ID Type Description

    0 ID_SEPARATOR(0) Message line xcursor coordinate

    1 ID_SEPARATOR(0) Message line y cursor coordinate

    2 ID_INDICATOR_LEFT Status indicator Left mouse button status

    3 ID_INDICATOR_RIGHT Status indicator Right mouse button status

    Page 25 of 136Part III: MFC's Document-View Architecture

    03-05-2014file://L:\TEMP\~hhA60B.htm

  • 5/26/2018 7_Part III MFC's Document-View Architecture

    26/136

    menu. In the New Project dialog box, select the MFC Application template, type the name Ex13b, and click OK. In the MFCApplication Wizard, accept all the defaults but two: On the Application Type page, select Single Document, and on theAdvanced Features page, deselect Printing And Print Preview.

    2. Use the string editor to edit the application's string table resource. The application has a single string table resourcewith artificial "segment" divisions left over from the 16-bit era. In Resource View, double-click on the String Table icon in theString Table folder to bring up the string editor. Then select the empty entry at the end of the list and add the following twostrings:

    When you're finished, the string table should appear as follows:

    3. Edit the application's symbols.Choose Resource Symbols from the Edit menu. Click the New button and add the newstatus bar identifier, ID_MY_STATUS_BAR, and accept the default value as shown here:

    4. Add View menu command handlers in the class CmainFrame.Select the CMainFrameclass in Class View, click theEvents button in the Properties window, and add the following command message handlers:

    5. Add the following function prototypes to MainFrm.h.You must add these CMainFramemessage handler prototypesmanually because Visual Studio doesn't recognize the associated command message IDs.

    afx_msg void OnUpdateLeft(CCmdUI* pCmdUI);afx_msg void OnUpdateRight(CCmdUI* pCmdUI);

    While MainFrm.his open, make m_wndStatusBarpublic rather than protected.

    6. Edit the MainFrm.cppfile.Replace the original indicatorsarray with the following boldface code:

    static UINT indicators[] =

    String ID String Caption

    ID_INDICATOR_LEFT LEFT

    ID_INDICATOR_RIGHT RIGHT

    Object ID Message Member Function

    ID_VIEW_STATUS_BAR COMMAND OnViewStatusBar

    ID_VIEW_STATUS_BAR UPDATE_COMMAND_UI OnUpdateViewStatusBar

    Page 26 of 136Part III: MFC's Document-View Architecture

    03-05-2014file://L:\TEMP\~hhA60B.htm

  • 5/26/2018 7_Part III MFC's Document-View Architecture

    27/136

    {ID_SEPARATOR, // first message line paneID_SEPARATOR, // second message line paneID_INDICATOR_LEFT,ID_INDICATOR_RIGHT,

    };

    Next, edit the OnCreatemember function. Replace the following statement

    if (!m_wndStatusBar.Create(this) ||!m_wndStatusBar.SetIndicators(indicators,

    sizeof(indicators)/sizeof(UINT))){

    TRACE0("Failed to create status bar\n");return -1; // fail to create

    }

    with the statement shown here:

    if (!m_wndStatusBar.Create(this,WS_CHILD | WS_VISIBLE | CBRS_BOTTOM, ID_MY_STATUS_BAR) ||

    !m_wndStatusBar.SetIndicators(indicators,sizeof(indicators)/sizeof(UINT)))

    {TRACE0("Failed to create status bar\n");return -1; // fail to create

    }

    The modified call to Createuses our own status bar ID, ID_MY_STATUS_BAR, instead ofAFX_IDW_STATUS_BAR(theapplication framework's status bar object).

    Now add the following message map entries for the class CMainFrame. Visual Studio can't add these for you because itdoesn't recognize the string table IDs as object IDs.

    ON_UPDATE_COMMAND_UI(ID_INDICATOR_LEFT, OnUpdateLeft)ON_UPDATE_COMMAND_UI(ID_INDICATOR_RIGHT, OnUpdateRight)

    Add the following CMainFramemember functions that update the two status indicators:

    void CMainFrame::OnUpdateLeft(CCmdUI* pCmdUI){

    pCmdUI->Enable(::GetKeyState(VK_LBUTTON) < 0);}

    void CMainFrame::OnUpdateRight(CCmdUI* pCmdUI){

    pCmdUI->Enable(::GetKeyState(VK_RBUTTON) < 0);}

    Note that the left and right mouse buttons have virtual key codes like keys on the keyboard have. You don't have to dependon mouse-click messages to determine the button status.

    Finally, edit the following View menu functions in MainFrm.cpp:

    void CMainFrame::OnViewStatusBar(){

    m_wndStatusBar.ShowWindow((m_wndStatusBar.GetStyle() &WS_VISIBLE) == 0);

    RecalcLayout();}void CMainFrame::OnUpdateViewStatusBar(CCmdUI* pCmdUI){

    pCmdUI->SetCheck((m_wndStatusBar.GetStyle() & WS_VISIBLE) != 0);

    }

    These functions ensure that the View menu's Status Bar command is properly linked to the new status bar.

    7. Edit the OnDrawfunction in Ex13bView.cpp. The OnDrawfunction displays a message in the view window. Add thefollowing boldface code:

    void CEx13bView::OnDraw(CDC*pDC){

    CEx13bDoc* pDoc = GetDocument();ASSERT_VALID(pDoc);

    pDC->TextOut(0, 0,"Watch the status bar while you move and click the mouse.");

    }

    Page 27 of 136Part III: MFC's Document-View Architecture

    03-05-2014file://L:\TEMP\~hhA60B.htm

  • 5/26/2018 7_Part III MFC's Document-View Architecture

    28/136

    8. Add aWM_MOUSEMOVEhandler in the CEx13bViewclass.Select the CEx13bViewclass in Class View, click theMessages button in the Properties window, and add the OnMouseMovefunction. Edit the function as shown below. Thisfunction gets a pointer to the status bar object and then calls the SetPaneTextfunction to update the first and secondmessage line panes.

    void CEx13bView::OnMouseMove(UINT nFlags, CPoint point){

    CString str;CMainFrame* pFrame = (CMainFrame*) AfxGetApp()->m_pMainWnd;CStatusBar* pStatus = &pFrame->m_wndStatusBar;

    if (pStatus) {str.Format("x = %d", point.x);pStatus->SetPaneText(0, str);str.Format("y = %d", point.y);pStatus->SetPaneText(1, str);

    }}

    Finally, add the statement

    #include "MainFrm.h"

    near the top of the file Ex13bView.cpp.

    9. Build and test the Ex13b application. Move the mouse and observe that the first two status bar panes accurately reflectthe mouse cursor's position. Try the left and right mouse buttons. Can you toggle the status bar on and off from the Viewmenu?

    Rebars

    As you learned in Chapter 8, Visual C++ contains features originally found in Internet Explorer: the common controls. One of theseis a new kind of toolbar called a rebar. You're probably familiar with the rebar if you've used Internet Explorer. The rebar differs fromthe default MFC toolbar in that it provides grippers and allows the user to "slide" its horizontal and vertical positions. In contrast, you

    change the MFC toolbar's position using drag-and-drop docking. Rebars also allow the developer to provide many more internalcontrol types such as drop-down menusthan are available in CToolBar.

    Anatomy of a Rebar

    Figure 13-5shows the various terms used on a rebar. Each internal toolbar in a rebar is called a band. The raised edge where theuser slides the band is called a gripper. Each band can also have a label.

    Figure 13-5: Rebar terminology.

    MFC provides two classes that facilitate working with rebars:

    CReBarA high-level abstraction class that provides members for adding CToolBarand CDialogBarclasses to rebars as bands.CReBaralso handles communication (such as message notifications) between the underlying control and the MFC framework.

    CReBarCtrlA low-level wrapper class that wraps the ReBar control. This class provides numerous members for creating andmanipulating rebars but does not provide the niceties that are found in CReBar.

    Most MFC applications use CReBarand call the member function GetReBarCtrl, which returns a CReBarCtrlpointer to gain accessto the lower-level control if needed.

    The Ex13c Example: Using Rebars

    Note If you want the first (index 0) status bar pane to have a beveled border like the other panes and you want the statusbar to grow and resize to fit the contents, include the following two lines in the CMainFrame::OnCreatefunction,following the call to the status bar Createfunction.

    m_wndStatusBar.SetPaneInfo(0, 0, 0, 50);m_wndStatusBar.SetPaneInfo(1, 0, SBPS_STRETCH, 50);

    These statements change the width of the first two panes (from their default of one-fourth the display size) andmake the second pane (index 1) the stretchy one.

    Page 28 of 136Part III: MFC's Document-View Architecture

    03-05-2014file://L:\TEMP\~hhA60B.htm

  • 5/26/2018 7_Part III MFC's Document-View Architecture

    29/136

    Let's get familiar with the rebar by jumping into an example. This example creates an SDI application that has a rebar with twobands: a familiar toolbar band and a dialog bar band. Figure 13-6shows the example in action.

    Figure 13-6: Ex13c rebar example.

    Here are the steps required to create the Ex13c example:

    1. Run the MFC Application Wizard to generate a project named Ex13c. Choose New Project from Visual Studio's Filemenu. In the New Project dialog box, select the MFC Application template, type the name Ex13c, and click OK. In the MFCApplication Wizard, accept all the defaults but two: On the Application Type page, select Single Document, and on the UserInterface Features page under Toolbars, select Standard Docking and Browser Style.

    2. Compile and run the application. When you run the application, you'll see that the MFC Application Wizard hasautomatically created a rebar with two bands. One band contains a conventional toolbar and the other contains the text

    TODO: layout dialog barin the band.

    Open the MainFrm.hheader file and see the code below, which declares the CReBardata member m_ndReBar.

    protected: // control bar embedded membersCStatusBar m_wndStatusBar;CToolBar m_wndToolBar;CReBar m_wndReBar;CDialogBar m_wndDlgBar;

    In the MainFrm.cppfile, you can see the code that adds the toolbar and the dialog bar to the CReBarobject:

    if (!m_wndReBar.Create(this) ||!m_wndReBar.AddBar(&m_wndToolBar) ||!m_wndReBar.AddBar(&m_wndDlgBar))

    {TRACE0("Failed to create rebar\n");return -1; // fail to create

    }

    3. Lay out the dialog bar. In Resource View, under the Dialog node, you'll find a dialog resource for the dialog bar with the IDIDR_MAINFRAME. Open IDR_MAINFRAME, and you'll see the dialog bar with the text TODO: layout dialog bar. Let's putsome real controls onto the dialog bar. First, delete the static control with the TODO text in it. Then place a combo box on thedialog bar and use the Properties window to enter the following default data items in the Dataproperty:One;Two;Buckle;My;Shoe!;. Now place a button on the dialog bar and change the button's Captionproperty to Increment.Place a progress control on the dialog bar and set the Smoothproperty to True. Finally, place another button on the dialogbar and change the Captionproperty to Decrement. When you're done laying out out the dialog bar, it should look similar tothis.

    Page 29 of 136Part III: MFC's Document-View Architecture

    03-05-2014file://L:\TEMP\~hhA60B.htm

  • 5/26/2018 7_Part III MFC's Document-View Architecture

    30/136

    4. Edit the MainFrm.hfile.Visual Studio doesn't understand how to connect the controls on the dialog bar with handlers in

    the CMainFrameclass. We need to add them by hand. Open up MainFrm.hand add the following prototypes toCMainFrame.

    afx_msg void OnButton1();afx_msg void OnButton2();

    5. Edit the MainFrm.cppfile.Open MainFrm.cppand add the following message maps for Button1 and Button2:

    BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)ON_WM_CREATE()ON_BN_CLICKED(IDC_BUTTON1, OnButton1)ON_BN_CLICKED(IDC_BUTTON2, OnButton2)

    END_MESSAGE_MAP()

    Add the following OnButton1and OnButton2methods to CMainFrame.cpp:void CMainFrame::OnButton1(){

    CProgressCtrl * pProgress =(CProgressCtrl*)m_wndDlgBar.GetDlgItem(IDC_PROGRESS1);

    pPr