Teach Yourself Visual C++® 5 in 24 Hours

Previous chapterNext chapterContents


- Hour 23 -
Advanced Views

In this hour you learn more about MFC and Document/View, expanding on the material covered in Hour 9, "The Document/View Architecture." In this hour, you will learn about

In this hour, you will also modify the DVTest project from Hour 9 so that it includes form views as well as multiple views for a document.

What Is a Form View?

New Term: A form view is a view that can contain controls, much like a dialog box.

You usually add controls to a form view using a dialog resource, the same way you build dialog boxes. However, unlike dialog boxes, a form view is never modal; in fact, several form views can be open simultaneously, just like other views.

The most common reason to use a form view is because it's an easy-to-use view that looks like a dialog box. With practice, you can create a form view in a few minutes. You can add controls used in a form view to the form view's dialog resource, just as you add controls to a resource used by a normal dialog box. After you add the controls, you can associate them with MFC objects and class member variables, just as you associate controls with dialog boxes.

Using form views enables you to easily adapt the DDX and DDV routines used by dialog boxes to a view. Unlike a modal dialog box, several different form views can be open at once, making your program much more flexible. Like other views, a form view has access to all the Document/View interfaces, giving it direct access to the document class.

It's common for a form view to be one of several possible views for a document. In an MDI application, it's common to have more than one view for each document type. For example, a form view can be used as a data entry view, and another type of view can be used for display purposes. Figure 23.1 presents a class diagram showing some of the classes derived from CView.

Figure 23.1.
The MFC view classes.

What Are the Other Types of Views?

New Term: A scroll view is a view that can be larger than its visible area.

In a scroll view, the invisible part of the view can be made visible using scrollbars associated with the view. An easy way to visualize how scrolling works is to imagine a large virtual view, hidden except for a small window used as a viewport, as shown in Figure 23.2.

Only a portion of the entire view is visible in Figure 23.2. The view window scrolls to different parts of the document; the underlying large view retains its original position. Although you can implement scrolling yourself for any class derived from CView, much of the work is done for you if you use CScrollView.

Figure 23.2.
Scrolling a view using CScrollView.

New Term: The edit view is a view that consists of an edit control. The edit view automatically supports printing, using the clipboard cut, paste, and copy functions, and Find and Replace. The edit view is supported by the CEditView class, so it can be associated with a document just like any other view.


Just a Minute: The edit view does not support true what-you-see-is-what-you-get (WYSIWYG) editing; only one font is supported for the entire view, and the display is not always 100 percent accurate with regard to a printed page.

Using a Form View


Just a Minute: Using a form view requires only a few more steps than using a dialog box. All the hard work is handled by the framework and the MFC CFormView class. Using ClassWizard, you can add a form view to a project using 30 or 40 lines of your own code.

To illustrate how form views are used, add a form view to a project that was built using AppWizard. To reduce the amount of code that must be entered, you can reuse the DVTest project built in Hour 9. To recap, the DVTest project stored a collection of names in its document class. In this hour, you will associate a form view with the document and use it to display and input names into the program. For now, the form view is the only view associated with the document; you will learn about multiple views later this hour.

Creating a Dialog Resource for a Form View

Although a form view uses a dialog resource to lay out its controls, a form view isn't really a dialog box; instead, a form view uses the dialog resource as a template when the view is created. For this reason, the CFormView class has special requirements for the dialog resources it uses. Use the properties shown in Table 23.1 for dialog resources used by form views.

Table 23.1. Properties for dialog resources used by form views.

Property Value
Style Child
Border None
Visible Unchecked
Titlebar Unchecked

Other than the values listed in Table 23.1, there are no other limitations for dialog box properties or controls. Any controls you can add to a dialog box can be used in a form view.

Developer Studio enables you to add a dialog box resource to your project that has all of the properties needed for a form view. Follow these steps:

1. Right-click the tree inside the ResourceView window.

2. Select Insert from the context menu. A dialog box will be displayed containing a tree of resources that can be added to your project.

3. Expand the Dialog tree; a list of dialog box templates is displayed.

4. Select the IDD_FORMVIEW template, and click the New button.

To get started on the form view example, add a dialog resource to the DVTest project. This dialog resource will be used in a form view, as shown in Figure 23.3.

Figure 23.3.
The dialog resource used as a form view in the CDVTest sample project.

Set the dialog resource and control resource IDs as listed in Table 23.2. The list box should not be automatically sorted by Windows for this example, so clear the Sort attribute for the list box control. Use default properties for all other controls.

Table 23.2. Properties for the CDVTest form view dialog resource.

Property ID
Dialog IDD_FORM_VIEW
Edit Control IDC_EDIT
List Control IDC_LIST
Close Button IDC_CLOSE
Apply Button IDC_APPLY

Adding a Form View Class to a Project

Use ClassWizard to add a form view class to a project, much as you would add a dialog box class to a project. After creating the dialog resource, add the form view class using the following steps:

1. Open ClassWizard. Because ClassWizard knows that a new resource has been added, a dialog box prompts you to choose between two options for the new dialog resource. Select the option labeled Create a New Class.

2. Fill in the Add Class dialog box using the values from Table 23.3, then click OK.

Table 23.3. Sample values for the Add Class dialog box.

Control Value
Name CFormTest
Base Class CFormView
File FormTest.cpp
Dialog ID IDD_FORM_VIEW
Automation None

Use ClassWizard to add two member variables to the CFormTest class, as shown in Table 23.4.

Table 23.4. Control variables added to the CFormTest class.

Control ID Control Type Variable Type Variable Name
IDC_LIST Control CListCtrl m_lbNames
IDC_EDIT Control CEdit m_edNames

Using CFormView Instead of CView

Because CFormView is a subclass derived from CView, you can substitute it for CView in most cases. As was discussed in Hour 9, a document class is associated with a view class using a CMultiDocTemplate object in MDI programs. You can change the view associated with a particular document by editing the parameters used when the CMultiDocTemplate object is constructed.

Listing 23.1 associates the CFormTest view class with the CDVTestDoc document class. Update the code that creates the document template in the CDVTestApp::InitInstance function, found in the CDVTestApp.cpp source file. You must change only the fourth parameter to the constructor, as shown by the comment.

TYPE: Listing 23.1. Constructing a CMultiDocTemplate object that associates CDVTestDoc and CFormTest.

CMultiDocTemplate* pDocTemplate;

pDocTemplate = new CMultiDocTemplate(

        IDR_DVTESTTYPE,

        RUNTIME_CLASS(CDVTestDoc),

        RUNTIME_CLASS(CChildFrame), // custom MDI child frame

        RUNTIME_CLASS(CFormTest));  // Change this line

AddDocTemplate(pDocTemplate); Because CFormTest is now used, the class declaration for CFormTest must be included into the DVTest.cpp source file. Add the following line after all other #include directives at the top of the DVTest.cpp source file:

#include "FormTest.h"

Handling Events and Messages in the Form View Class

A form view must handle a wide variety of messages. Just like any view, it must support several interfaces as part of the Document/View architecture. However, unlike other views, a form view must also handle any controls contained by the view. For example, two events generated by controls must be handled in the CFormTest class:

Use ClassWizard to add two message-handling functions for these events, using the values from Table 23.5.

Table 23.5. Message-handling events added to the CFormTest class.

Object ID Message Function Name
IDC_APPLY BN_CLICKED OnApply
IDC_CLOSE BN_CLICKED OnClose

The code to handle control events is fairly straightforward. Edit the new functions added to the CFormTest class so that they look like the code in Listing 23.2.

TYPE: Listing 23.2. CFormTest functions used to handle control messages.

void CFormTest::OnApply()

{

    CDVTestDoc* pDoc;

    pDoc = (CDVTestDoc*)GetDocument();

    ASSERT_VALID( pDoc );



    CString szName;

    m_edNames.GetWindowText( szName );

    m_edNames.SetWindowText( "" );

    m_edNames.SetFocus();



    if( szName.GetLength() > 0 )

    {

        int nIndex = pDoc->AddName( szName );

        m_lbNames.InsertString( nIndex, szName );

        m_lbNames.SetCurSel( nIndex );

    }

}



void CFormTest::OnClose()

{

    PostMessage( WM_COMMAND, ID_FILE_CLOSE );

} You must manually add an include statement for the document class. At the top of the FormView.cpp file, add the following line just after all the other #include directives:

#include "DVTestDoc.h"

The OnApply function is split into three main parts:

The OnClose member function uses the PostMessage function to send an ID_FILE_CLOSE message to the application. This has the same effect as selecting Close from the File menu.

Handling OnInitialUpdate

When using a form view, update it during OnInitialUpdate, as the view is initially displayed. In Hour 9, CDVTestView used OnDraw to retrieve the document's contents and display the items in the view. The OnInitialUpdate function uses similar code, as shown in Listing 23.3. Before editing the code, add the OnInitialUpdate function to the CFormTest class using ClassWizard.

TYPE: Listing 23.3. Using OnInitialUpdate to retrieve data from the document.

void CFormTest::OnInitialUpdate()

{

    CFormView::OnInitialUpdate();



    CDVTestDoc* pDoc = (CDVTestDoc*)GetDocument();

    ASSERT_VALID(pDoc);



    for( int nIndex = 0; nIndex < pDoc->GetCount(); nIndex++ )

    {

        CString szName = pDoc->GetName( nIndex );

        m_lbNames.AddString( szName );

    }

}


Time Saver: When a dialog box is displayed, the dialog resource is used to size the dialog box's window. A form view is not automatically sized this way, which leads to an unexpected display if you aren't aware of this behavior. However, you can resize the view to the exact dimensions of the dialog resource by using the ResizeParentToFit function. Add the following two lines of code to the CFormTest::OnInitialUpdate member function:

ResizeParentToFit( FALSE );

ResizeParentToFit();

Nope, it's not a typo; you must call ResizeParentToFit twice to make sure that the size is calculated correctly. The first call allows the view to expand and the second call shrinks the view to fit the dialog resource.


Preventing a View Class from Being Resized

Like all views, you can resize a form view in three ways:

Although the minimize button is handy, the other sizing methods are a problem for form views. Because a form view looks like a dialog box and the control layout is specified in the dialog resource, preventing the user from resizing is a good idea.

The form view class doesn't actually have any control over the minimize and maximize buttons--they belong to the frame, which also controls the capability to change the size of the view by dragging it with a mouse. The CChildFrame class is the frame used by default in MDI applications, although you can change the frame class by using a different class name when the document template is created.

To remove the sizable frame and minimize button from the frame class, add two lines of code to the frame class PreCreateWindow member function. The PreCreateWindow function is called just before the window is created. This enables you to change the style of the window, as shown in Listing 23.4.

TYPE: Listing 23.4. Using the PreCreateWindow function to change CChildFrame style attributes.

BOOL CChildFrame::PreCreateWindow(CREATESTRUCT& cs)

{

    // Mask away the thickframe and maximize button style bits.

    cs.style &= ~WS_THICKFRAME;

    cs.style &= ~WS_MAXIMIZEBOX;

    return CMDIChildWnd::PreCreateWindow(cs);

} The &= operator is the C++ bitwise AND operator, which is used to clear or remove a bit that is set in a particular value. The tilde (~) is the C++ inversion operator, used to "flip" the individual bits of a particular value. These two operators are commonly used together to mask off attributes that have been set using the bitwise OR operator. In Listing 23.4, the WS_THICKFRAME and WS_MAXIMIZEBOX attributes are cleared from the cs.style variable.

Compile and run the DVTest project. Figure 23.4 shows DVTest after a few names have been added to the list box.

Figure 23.4.
DVTest after adding a form view to the project.

Using Multiple Views

Programs written for Windows sometimes offer multiple views for their data. For example, many word processors offer print preview and layout views of a document, in addition to the normal WYSIWYG view that's used most of the time. Providing multiple views for a single document is a different issue than allowing several different documents to be open at the same time; each view actually is connected to a single document, as shown in Figure 23.5.

Figure 23.5.
Multiple views connected to a single document in an MDI application.

The most common reason to use multiple views is because there are different ways of looking at information contained in a document. For example, a form view often is used to give detailed information about a particular item in a database; another view might be used for data entry; still another type of view might be used to provide a summary of all items in the same database. Offering several views at the same time provides maximum flexibility for users of the program.

Because each of these views is connected to a single document, there must be some way to update the views when needed to keep them synchronized. When one of the views changes the document, all views must immediately be updated.


Just a Minute: Using multiple views allows each view to be specialized for a particular purpose. If only a single view were allowed, that view would have to be extremely flexible to suit the needs of every user of your program. Creating specialized views for particular purposes allows each of these views to do a single job for which they are well suited.

How to Use Multiple Views

Using multiple views in an MDI application is easy because the Document/View architecture keeps the document and view classes separate from each other. The document class is mainly passive; it notifies the framework when views should be updated, but otherwise relies on the view classes to change or request data stored in the document.

A new view is easily associated with an existing document. After a document class has been modified to work with multiple views, any number of view classes can be added to the program without further modifications to the document class. The following steps are required to modify an MDI program to use multiple views:

You will learn about each of these steps in the following sections. Because the Document/View architecture is designed to support multiple views, you can rely on ClassWizard to write much of the code for you. To reduce the amount of typing needed, continue to modify the DVTest program from Hour 9.

Creating a New View

The first step in adding a new view to an existing document is to define the view by creating a view class. Any type of view can be added to an existing MDI program. In this set of examples, the new view displays the names contained in the document class. The existing form view is used to add names to the DVTestDoc document.

The new view class, CDisplayView, is derived directly from CView. Because CDisplayView only displays information, it must support only two new interfaces:

To create a new view to add to the CDVTest project, follow these steps:

1. Open ClassWizard.

2. Press the button labeled Add Class and select the New option from the drop-down menu.

3. Use the values from Table 23.6 to fill in the Add Class dialog box.

4. Click OK and close ClassWizard.

Table 23.6. Values used to add the CDisplayView class.

Control Value
Name CDisplayView
Base Class CView
OLE Automation None

ClassWizard adds the new view to the project and creates some default initialization functions. However, the view class isn't useful until you do some additional work to associate it with the document class and define how it displays information.

When you create a new view using ClassWizard, you must add functions to handle the Document/View interfaces; they aren't automatically created as they are for views created by AppWizard when a new project is created. Using ClassWizard, add two message-handling functions to the CDisplayView class using the values from Table 23.7.

Table 23.7. New member functions for the CDisplayView class.

Class Name Object ID Message
CDisplayView CDisplayView OnInitialUpdate
CDisplayView CDisplayView OnUpdate

Modifying the OnDraw Function

As discussed earlier, when you complete it, the CDisplayView class will list the names contained in the CDVTestDoc document class. Like other OnDraw functions, CDisplayView::OnDraw retrieves a pointer to the document class and collects information about the items to be displayed in the view. The source code for CDisplayView::OnDraw is provided in Listing 23.5.

TYPE: Listing 23.5. Source code for CDisplayView::OnDraw.

void CDisplayView::OnDraw(CDC* pDC)

{

    CDVTestDoc* pDoc = (CDVTestDoc*)GetDocument();

    ASSERT_VALID(pDoc);



    // Calculate the space required for a single

    // line of text, including the inter-line area.

    TEXTMETRIC  tm;

    pDC->GetTextMetrics( &tm );

    int nLineHeight = tm.tmHeight + tm.tmExternalLeading;



    CPoint  ptText( 0, 0 );

    for( int nIndex = 0; nIndex < pDoc->GetCount(); nIndex++ )

    {

        CString szName = pDoc->GetName( nIndex );

        pDC->TextOut( ptText.x, ptText.y, szName );

        ptText.y += nLineHeight;

    }

} Notice that the OnDraw function used in CDisplayView is the same as the CDVTestView::OnDraw function in Listing 9.14. Although that view had exclusive access to its document, the same source code works when the document is shared by multiple views. You add the code for the OnInitialUpdate and OnUpdate member functions later, in the section "Adding the OnInitialUpdate and OnUpdate Member Functions."

Because the OnDraw function must access the CDVTestDoc class, add this #include directive for the CDVTestDoc class:

#include "DVTestDoc.h"

Add this include statement after the other include statements near the beginning of the DisplayView.cpp source file.

Creating and Maintaining Multiple Document Templates

When a single view and document are associated with each other, a CMultiDocTemplate is passed to the MFC framework, and the application never sees it again. When multiple views are created, the application class must keep track of the document templates used for the document and view associations. The application class stores these pointers and provides them to the CMainFrame class when needed. Add the source code in Listing 23.6 to the implementation section of the CDVTestApp class declaration. These additions declare two member variables that are used to cache pointers to the document templates and two member functions used to get access to the pointers.

TYPE: Listing 23.6. Changes to the CDVTestApp class declaration.

public:

    CDocTemplate* GetDisplayTemplate() const;

    CDocTemplate* GetFormTemplate() const;

private:

    CDocTemplate* m_pDisplayTemplate;

CDocTemplate* m_pFormTemplate; The two document template pointers are set during the CDVTestApp::InitInstance member function. Instead of creating a CMultiDocTemplate object and passing it immediately to the AddDocTemplate function, CMultiDocTemplate objects are created, and their pointers are stored in the new member variables. Replace the current code used to create the document templates in CDVTestApp::InitInstance with the source code provided in Listing 23.7.

TYPE: Listing 23.7. Changes to CDVTestApp::InitInstance creating two document templates.

m_pFormTemplate = new CMultiDocTemplate(

                                   IDR_DVTESTTYPE,

                                   RUNTIME_CLASS(CDVTestDoc),

                                   RUNTIME_CLASS(CChildFrame),

                                   RUNTIME_CLASS(CFormTest) );

 m_pDisplayTemplate = new CMultiDocTemplate(

                                   IDR_DISPLAYTYPE,

                                   RUNTIME_CLASS(CDVTestDoc),

                                   RUNTIME_CLASS(CChildFrame),

                                   RUNTIME_CLASS(CDisplayView) );

AddDocTemplate( m_pFormTemplate ); Each of the document templates created in Listing 23.7 describes views associated with the CDVTestDoc class. One of the document templates uses the CFormTest class from earlier this hour, whereas the other template uses the CDisplayView class. Because this class is new to the DVTest.cpp file, add an #include directive for the CDisplayView class:

#include "DisplayView.h"

Listing 23.8 contains the source for the new CDVTestApp functions that return pointers to the CDocTemplate pointers created during CDVTest::OnInitInstance. The CMainFrame class uses these pointers when creating new views. Add the source code in Listing 23.8 to the DVTest.cpp file.

TYPE: Listing 23.8. CDVTestApp functions used to return pointers to the document templates.

CDocTemplate* CDVTestApp::GetDisplayTemplate() const

{

    return m_pDisplayTemplate;

}



CDocTemplate* CDVTestApp::GetFormTemplate() const

{

    return m_pFormTemplate;

}

Adding Shared Resources

One of the parameters used when creating a document template is the shared-resource identifier. This resource ID is used to identify several different resources used by the view:

Each of these resources must be created for a new view. Although sharing an existing resource ID is possible, providing at least a customized icon for the new view is a much better practice. The name of the current shared resource ID is IDR_DVTESTTYPE; for the new view, you will create a shared resource ID named IDR_DISPLAYTYPE.

Creating a Menu for the New View

Click the IDR_DVTESTTYPE menu item, and use Copy and Paste to create a new menu item. Rename the new item as IDR_DISPLAYTYPE; you can open the property page by right-clicking the icon and selecting Properties from the pop-up menu.

Creating an Icon for the New View

Create an IDR_DISPLAYTYPE icon by opening the Icon folder on the resource tree. Create a copy of the existing IDR_DVTESTTYPE icon by using the Edit menu to copy and paste the icon, or by pressing Ctrl+C, then Ctrl+V.

Using a Resource String

The resource string for each document template is stored in a String Table resource. Go to the ResourceView window and click the String Table icon. In the DVTest project, the resource string for the current document template is stored under the name IDR_DVTESTTYPE. You can add a new string to the String Table by pressing the Insert key on the keyboard. Create a new string resource named IDR_DISPLAYTYPE with the following string value:

\nDVTest\n\n\n\nDVTest.Document\nDVTest Document

The contents of the resource string are split into seven sections, and each section is separated by \n. Each of the seven sections has a particular purpose, as shown in Table 23.8.

Table 23.8. Values for subsections of resource strings used in DVTest.

Section IDR_DVTEST IDR_DISPLAYTYPE
Title
Document Name DVTest DVTest
New File Name DVTest
Filter Name
Filter Extension
Type ID DVTest.Document DVTest.Document
Type Name DVTest Document DVTest Document

The new resource string is almost the same as the original string. The only difference is that there is no entry for the section marked New File Name. This is a clue to the MFC framework that this document template is not used to create new documents; instead, it is used only to open a new view on an existing document. You don't have to worry too much about the purpose of each segment. The MFC framework uses these segments when registering your application with Windows, and when opening new views and documents.

Adding Menu Items for New Views

You add new views by selecting a menu item from the Window menu. Add the menu items using the Developer Studio resource editor, as you learned in Hour 10, "Menus." Use the values from Table 23.9 to add the menu items to the IDR_DISPLAYTYPE and IDR_DVTESTTYPE menus, and to add message-handling functions to the CMainFrame class.

Table 23.9. New member functions for the CMainFrame class.

Menu ID Caption Event Function Name
ID_WINDOW_DISPLAY &Display View COMMAND OnWindowDisplay
ID_WINDOW_FORM &Form View COMMAND OnWindowForm


CAUTION: You must add the new menu items to both the IDR_DISPLAYTYPE and IDR_DVTESTTYPE menus. If you don't, you won't be able to access the new menu items when either view is active.

Listing 23.9 provides the source code for the message-handling functions.

TYPE: Listing 23.9. CMainFrame member functions used to create new views.

void CMainFrame::OnWindowForm()

{

    CMDIChildWnd* pActiveChild = MDIGetActive();

    if( pActiveChild != 0 )

    {

        CDocument*  pDocument = pActiveChild->GetActiveDocument();

        if( pDocument != 0 )

        {

            CDVTestApp*     pApp = (CDVTestApp*)AfxGetApp();

            CDocTemplate*   pTemp;

            CFrameWnd*      pFrame;

            pTemp = pApp->GetFormTemplate();

            pFrame = pTemp->CreateNewFrame(pDocument,pActiveChild);

            if( pFrame != 0 )

            {

                pTemp->InitialUpdateFrame(pFrame, pDocument);

            }

        }

    }

}



void CMainFrame::OnWindowDisplay()

{

    CMDIChildWnd* pActiveChild = MDIGetActive();

    if( pActiveChild != 0 )

    {

        CDocument*  pDocument = pActiveChild->GetActiveDocument();

        if( pDocument != 0 )

        {

            CDVTestApp*     pApp = (CDVTestApp*)AfxGetApp();

            CDocTemplate*   pTemp;

            CFrameWnd*      pFrame;

            pTemp = pApp->GetDisplayTemplate();

            pFrame = pTemp->CreateNewFrame(pDocument,pActiveChild);

            if( pFrame != 0 )

            {

                pTemp->InitialUpdateFrame(pFrame, pDocument);

            }

        }

    }

} These functions are nearly identical: the only difference between them is the call to either GetDisplayTemplate or GetFormTemplate. The functions provided in Listing 23.9 follow these steps when creating a new view:

1. Get a pointer to the active child window.

2. Get a pointer to the active document.

3. Get a pointer to the application.

4. Using the application pointer, get the document template for the new view.

5. Using the document template, create a new frame associated with the active frame from step 1 and the document pointer from step 2.

6. Update the frame.

These basic steps can be followed no matter what classes are involved or how many views and documents are being managed by the application.

Updating Multiple Views

One of the most important issues when a document has more than one view is ensuring that each view is accurate. If one view changes data loaded in the document, all views must be notified about the change; otherwise, they will present out-of-date information. The mechanism used by Document/View applications to keep documents and views synchronized is shown in Figure 23.6.

Figure 23.6.
The document class controls the updating of all views.

Every document should provide updates to its associated views by calling the UpdateAllViews function when data contained by the document has been changed. To update all views associated with a document, you can use a line like this:

UpdateAllViews( NULL );

The default implementation of UpdateAllViews notifies every view that the document has been changed by calling each view object's OnUpdate member function. The NULL parameter causes all views to be updated. If a view pointer is passed as a parameter, that view is not updated. Listing 23.10 provides the new source code for the CDVTestDoc::AddName function.

TYPE: Listing 23.10. A new version of CDVTestDoc::AddName that causes views to be updated.

int CDVTestDoc::AddName( const CString& szName )

{

    TRACE("CDVTestDoc::AddName, string = %s\n", (LPCSTR)szName);

    int nPosition = m_arNames.Add( szName );

    UpdateAllViews( NULL );

    return nPosition;

}

Adding the OnInitialUpdate and OnUpdate Member Functions

The OnInitialUpdate and OnUpdate member functions for CDisplayView invalidate the view area, causing the view to be repainted. When Windows sends a WM_PAINT message to the view, the OnDraw member function is called, redrawing the view with the new contents. Edit the OnInitialUpdate and OnUpdate functions as shown in Listing 23.11.

TYPE: Listing 23.11. Source code the CDisplayView update functions.

void CDisplayView::OnInitialUpdate()

{

    CView::OnInitialUpdate();

    InvalidateRect( NULL );

}



void CDisplayView::OnUpdate(CView* pSender, LPARAM lHint,

                            CObject* pHint)

{

    InvalidateRect( NULL );

} 

All view classes should provide OnUpdate member functions that are called by the MFC framework after the document class calls UpdateAllViews. Note that the entire view is redrawn whenever the document has been updated.

The current view, CFormTest, must also support OnUpdate. Add the OnUpdate function to the CFormTest class using ClassWizard. Listing 23.12 provides the source code for CFormTest::OnUpdate.

TYPE: Listing 23.12. Source code for the CFormTest::OnUpdate function.

void CFormTest::OnUpdate(CView* pSender, LPARAM lHint,

                         CObject* pHint)

{

    CDVTestDoc* pDoc = (CDVTestDoc*)GetDocument();

    ASSERT_VALID(pDoc);



    m_lbNames.ResetContent();

    for( int nIndex = 0; nIndex < pDoc->GetCount(); nIndex++ )

    {

        CString szName = pDoc->GetName( nIndex );

        m_lbNames.AddString( szName );

    }

} 

Now that you have implemented OnUpdate, change the OnInitialUpdate member function so that it performs only work that must be done when the view is initially displayed. Remove source code from CFormTest::OnInitialUpdate so it looks like the function provided in Listing 23.13.

TYPE: Listing 23.13. CFormTest::OnInitialUpdate after removing unnecessary code.

void CFormTest::OnInitialUpdate()

{

    CFormView::OnInitialUpdate();

    ResizeParentToFit( FALSE );

    ResizeParentToFit();

} 

Because OnUpdate handles the insertion of new items into the list box, you should change the OnApply member function so that it does not add strings to the list box. Edit the OnApply member function so it looks like the code in Listing 23.14.

TYPE: Listing 23.14. CFormTest::OnApply after removing list box AddString code.

void CFormTest::OnApply()

{

    CDVTestDoc* pDoc = (CDVTestDoc*)GetDocument();

    ASSERT_VALID(pDoc);



    CString szName;

    m_edNames.GetWindowText( szName );

    m_edNames.SetWindowText( "" );

    m_edNames.SetFocus();

    if( szName.GetLength() > 0 )

    {

        pDoc->AddName( szName );

    }

} 

Compile and run the DVTest project. Figure 23.7 shows DVTest with new names added to the document, and multiple open views.

Figure 23.7.
DVTest after adding the display view.

Summary

In this hour you learned about using form views in place of standard views or dialog boxes. Form views enable you to easily use controls in a view, just as they are used in dialog boxes. You also learned about associating multiple views with a document class. The DVTest program from Hour 9 was modified to take advantage of form views and multiple views.

Q&A

Q I have a view that must populate the menu with different menu items than other views. How should I handle the different menu choices?

A There is no requirement that all view menus have the same items; each view menu can be customized to suit the needs of each view. You should give each menu item a unique identifier--two menu items that perform different tasks should have different identifiers, even if they have the same names.

Q Why is it useful to pass a CView pointer as a parameter in UpdateAllViews and prevent that view from receiving an OnUpdate notification?

A Often, a view that causes a document to be updated can efficiently update its own view. In this case, there is no need for that particular view to be updated.

Workshop

The Workshop is designed to help you anticipate possible questions, review what you've learned, and begin thinking ahead to putting your knowledge into practice. The answers to the quiz are in Appendix B, "Quiz Answers."

Quiz

1. What are some differences between a form view and a dialog box?

2. What are the special requirements for dialog box resources used in a form view?

3. How do you size the frame of a form view so that it is the same size as its dialog box resource?

4. What is the difference between OnInitialUpdate and OnUpdate?

5. How do you prevent an MDI child window from being resized?

6. What function is called by a document class to notify views that the document has been changed?

7. What resources are identified through a shared resource identifier?

8. What view class enables you to use an edit control as a view?

9. What view class enables your view to have a large virtual area that is seen through a smaller scrolling viewport?

10. What class is used in an MDI application to associate a view class and a document class?

Exercises

1. Because the CChildFrame class was modified to prevent resizing, the instances of the CDisplayView class cannot be resized. Modify DVTest so that display views can be resized and form views cannot be resized.

2. Modify the form view in DVTest so that it displays the number of items stored in the document.


Previous chapterNext chapterContents


Macmillan Computer Publishing USA

© Copyright, Macmillan Computer Publishing. All rights reserved.