The EX13A Example

This example illustrates the routing of menu and keyboard accelerator commands to both documents and views. The application's view class is derived from CView and contains a rich edit control. View-directed menu commands, originating from a new pop-up menu named Transfer, move data between the view object and the document object, and a Clear Document menu item erases the document's contents. On the Transfer menu, the Store Data In Document item is grayed when the view hasn't been modified since the last time the data was transferred. The Clear Document item, located on the Edit menu, is grayed when the document is empty. Figure 13-4 shows the first version of the EX13A program in use.

Figure 13-4. The EX13A program in use.

If we exploited the document-view architecture fully, we would tell the rich edit control to keep its text inside the document, but that's rather difficult to do. Instead, we'll define a document CString data member named m_strText, the contents of which the user can transfer to and from the control. The initial value of m_strText is a Hello message; choosing Clear Document from the Edit menu sets 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 EX13A example exercises Visual C++'s wysiwyg menu editor and keyboard accelerator editor together with ClassWizard. You'll need to do very little C++ coding. Simply follow these steps:

  1. Run AppWizard to generate \vcpp32\ex13a\ex13a. Accept all the default settings but two: select Single Document and deselect Printing and Print Preview.

  2. Use the resource editor to edit the application's main menu. Click on the ResourceView tab in the Workspace window. Edit the IDR_MAINFRAME menu resource to add a separator and a Clear Document item to the Edit menu, as shown here.

    The resource editor's menu resource editor is intuitive, but you might need some help the first time you insert an item in the middle of a menu. A blank item is present at the bottom of each menu. Using the mouse, drag the blank item to the insertion position to define a new item. A new blank item will appear at the bottom when you're finished.

    Now add a Transfer menu, and then define the underlying items.

    Use the following command IDs for your new menu items.

    MenuCaptionCommand ID
    EditClear &DocumentID_EDIT_CLEAR_ALL
    Transfer&Get Data From Document\tF2 ID_TRANSFER_GETDATA
    Transfer&Store Data In Document\tF3 ID_TRANSFER_STOREDATA

    The MFC library has defined the first item, ID_EDIT_CLEAR_ALL. (Note: \t is a tab character—but type \t; don't press the Tab key.)

    When you add the menu items, type appropriate prompt strings in the Menu Item Properties dialog. These prompts will appear in the application's status bar window when the menu item is highlighted.

  3. Use the resource editor to add keyboard accelerators. Open the IDR_MAINFRAME accelerator table, and then use the insert key to add the following items.
    Accelerator IDKey
    ID_TRANSFER_GETDATAVK_F2
    ID_TRANSFER_STOREDATAVK_F3

    Be sure to turn off the Ctrl, Alt, and Shift modifiers. The Accelerator edit screen and Accel Properties dialog are shown in the illustration below.

    Click to view at full size.

  4. Use ClassWizard to add the view class command and update command UI message handlers. Select the CEx13aView class, and then add the following member functions.

    Object IDMessageMember Function
    ID_TRANSFER_GETDATACOMMANDOnTransferGetData
    ID_TRANSFER_STOREDATACOMMANDOnTransferStoreData
    ID_TRANSFER_STOREDATAUPDATE_COMMAND_UI OnUpdateTransferStoreData

  5. Use ClassWizard to add the document class command and update command UI message handlers. Select the CEx13aDoc class, and then add the following member functions.

    Object IDMessageMember Function
    ID_EDIT_CLEAR_ALLCOMMANDOnEditClearDocument
    ID_EDIT_CLEAR_ALLUPDATE_COMMAND_UIOnUpdateEditClearDocument

  6. Add a CString data member to the CEx13aDoc class. Edit the file ex13aDoc.h or use ClassView.

    public:
        CString m_strText;
    

  7. Edit the document class member functions in ex13aDoc.cpp. The OnNewDocument function was generated by ClassWizard. As you'll see in Chapter 16, the framework calls this function after it first constructs the document and when the user chooses New from the File menu. Your version sets some text in the string data member. Add the following boldface code:

    BOOL CEx13aDoc::OnNewDocument()
    {
        if (!CDocument::OnNewDocument())
            return FALSE;
        m_strText = "Hello (from CEx13aDoc::OnNewDocument)";
        return TRUE;
    }

    The Edit Clear Document message handler sets m_strText to empty, and the update command UI handler grays the menu item if the string is already empty. Remember that the framework calls OnUpdateEditClearDocument when the Edit menu pops up. Add the following boldface code:

    void CEx13aDoc::OnEditClearDocument()
    {
        m_strText.Empty();
    }
    
    void CEx13aDoc::OnUpdateEditClearDocument(CCmdUI* pCmdUI)
    {
        pCmdUI->Enable(!m_strText.IsEmpty());
    }

  8. Add a CRichEditCtrl data member to the CEx13aView class. Edit the file ex13aView.h or use ClassView.

    public:
        CRichEditCtrl m_rich;
    

  9. Use ClassWizard to map the WM_CREATE and WM_SIZE messages in the CEx13aView class. The OnCreate function creates the rich edit control. The control's size is 0 here because the view window doesn't have a size yet. The code for the two handlers is shown below.

    int CEx13aView::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_SIZE message to the view as soon as the view's initial size is determined and again each time the user changes the frame size. This handler simply adjusts the rich edit control's size to fill the view client area. Add the following boldface code:

    void CEx13aView::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);
    }

  10. Edit the menu command handler functions in ex13aView.cpp. ClassWizard generated these skeleton functions when you mapped the menu commands in step 4. The OnTransferGetData function gets the text from the document data member and puts it in the rich edit control. The function then clears the control's modified flag. There is no update command UI handler. Add the following boldface code:

    void CEx13aView::OnTransferGetData()
    {
        CEx13aDoc* pDoc = GetDocument();
        m_rich.SetWindowText(pDoc->m_strText);
        m_rich.SetModify(FALSE);
    }
    

    The OnTransferStoreData function copies the text from the view's rich edit control to the document string and resets the control's modified flag. The corresponding update command UI handler grays the menu item if the control has not been changed since it was last copied to or from the document. Add the following boldface code:

    void CEx13aView::OnTransferStoreData()
    {
        CEx13aDoc* pDoc = GetDocument();
        m_rich.GetWindowText(pDoc->m_strText);
        m_rich.SetModify(FALSE);
    }
    
    void CEx13aView::OnUpdateTransferStoreData(CCmdUI* pCmdUI)
    {
        pCmdUI->Enable(m_rich.GetModify());
    }

  11. Build and test the EX13A application. When the application starts, the Clear Document item on the Edit menu should be enabled. Choose Get Data From Document from the Transfer menu. Some text should appear. Edit the text, and then choose Store Data In Document. That menu item should now appear gray. Try choosing the Clear Document command, and then choose Get Data From Document again.