Visual C++ 6 Unleashed

Visual C++ 6 Unleashed

By MICKEY WILLIAMS and David Bennett

Creating Your Application

The easiest way to create an application that comes prewired to support the document/ view architecture is to use MFC AppWizard to create a project, choosing Single Docu ment or Multiple Documents in Step 1. Although using MFC AppWizard is by no means a requirement for using the document view architecture, it provides you with premade classes for your documents, views, and frames, as well as initializes your document template. Even if you do not use MFC AppWizard to generate your application, you may find that creating a sample application (which takes about 10 seconds) can provide a useful example of how MFC sets things up for working with documents and views.

In any case, developing an application that uses the document view architecture will involve creating your document, view, frame, and document template classes. After you have done this and have properly initialized your objects, your documents and views will pretty much take care of themselves.

Creating Your Document Class

Your document class derives from the CDocument class. If you plan to use OLE (which you will explore in Part VI, "MFC Support for COM and ActiveX" ), your documents may derive from COleDocument or COleServerDoc (these classes are themselves derived from CDocument ). To start, let's look at the following declaration for a document class created with MFC AppWizard, with some of the extra comments generated by the wizard stripped out for clarity:

class CMDIAppDoc : public CDocument
{
protected:
    CMDIAppDoc();
    DECLARE_DYNCREATE(CMDIAppDoc)

    // ClassWizard generated virtual function overrides
    //{{AFX_VIRTUAL(CMDIAppDoc)
    public:
    virtual BOOL OnNewDocument();
    virtual void Serialize(CArchive& ar);
    //}}AFX_VIRTUAL

public:
    virtual ~CMDIAppDoc();
#ifdef _DEBUG
    virtual void AssertValid() const;
    virtual void Dump(CDumpContext& dc) const;
#endif
protected:
    //{{AFX_MSG(CMDIAppDoc)
    //}}AFX_MSG
    DECLARE_MESSAGE_MAP()
};
						

In this example, MFC AppWizard was used to create an MDI project named MDIApp. The code fragment shown here is the class declaration for the document class, derived from CDocument, including the constructor and destructor. Notice that the constructor is protected. This is done because you will never create an instance of a document class directly. Usually, the MFC framework creates documents for you, and the framework only creates new document objects using serialization. It also may seem strange to talk about serialization when you use only the DECLARE_DYNCREATE macro instead of DECLARE_SERIAL. You're only required to use the DECLARE_SERIAL macro if you plan to use polymorphic pointers to your object to access serialization functions. This example calls the Serialize() function directly, which is fully supported by DECLARE_DYNCREATE.

In addition, MFC AppWizard has declared overrides for the OnNewDocument() and Serialize() functions, as well as the AssertValid() and Dump() debug functions. The class declaration also declares an empty message map for the document class.

Document Data

As mentioned previously, your document object will hold the data that is used by your application. When deciding how to structure the data in your document class, you should consider that you will want to support the following operations efficiently:

It is a good practice to include your document data as member variables of your document class. This allows you to take full advantage of the serialization features provided by MFC and the predefined operations for File New, Open, Save, Save As, and the most recently used files list. After you decide how to represent your data, you implement some of the function overrides discussed in the following sections.

The Dirty Flag

Users have come to expect that any good Windows app will not let them accidentally do things such as exiting an application without saving their data. To help implement this in your applications, classes derived from CDocument provide a dirty flag to keep track of whether the document has changed since it was last saved. The framework checks this flag before closing a document and prompts the user to save the file.

The dirty flag is cleared when you save or open a document, and it is set for you only when you change an OLE object in your document. Your code is responsible for setting the flag whenever your document changes. This is accomplished by using SetModifiedFlag(), which takes a parameter of TRUE or FALSE. Setting the modified flag to TRUE, which is the default parameter, tells the framework that the document contains changes since the last save. You can query the dirty flag in your application by calling the IsModified() member of CDocument.

Several overrideable functions of CDocument also affect how the framework handles the dirty flag. If you are interested in modifying the default behavior, take a look at CDocument::CanCloseFrame() and CDocument::SaveModified().

Serialize()

Recall that the Serialize() function of your document class is used to implement several functions involving your document and files. Your serialize implementation should look something like the following:

void CMyDocument::Serialize(CArchive& ar)
{
    // call base class Serialize() first
    CDocument::Serialize(ar);
    // then serialize the data for this class
    if(ar.IsLoading())
    {
        ar >> m_MyDataVariable;
        // load other data members
    }
    else
    {
        ar << m_MyDataVariable;
        // store other data members
    }
    // Serialize your member objects
    m_MyDataObject.Serialize(ar);
    m_MoreDataObject.Serialize(ar);
}  // end CMyDocument::Serialize

Remember to call the Serialize() function of the base class first. You also will notice that the serialize functions for the member objects included in the document class are called. This allows the objects to serialize themselves and is included outside of the IsLoading() block, because the Serialize() functions of these classes will have their own check for IsLoading().

OnNewDocument()

The OnNewDocument() function of your document class will be called by the framework whenever the user chooses the New command from the File menu. In MDI applications, a new document object is created, and this function is responsible for initializing it. In SDI applications, the same document object is reused. Your OnNewDocument() function then is responsible for reinitializing the document.

If you have created your application with the MFC AppWizard, you will see that the default implementation of OnNewDocument() simply defers to the CDocument:: OnNewDocument() function. This function will call the DeleteContents() member function of your class to ensure that the document is empty and will reset the dirty flag of your document. If you choose to override OnNewDocument(), you first should call the base class function:

BOOL CMyDoc::OnNewDocument()
{
    if(!CDocument::OnNewDocument())
        return FALSE;
    // Do any other initialization of your document here
    return TRUE;
}  // end OnNewDocument()

If this function returns FALSE, creation of the new document is aborted. If an exception is thrown during this operation, the ReportSaveLoadException() function, described later in this section, is called.

DeleteContents()

The DeleteContents() member of your document class is called by the default implementation of OnNewDocument() to clear out the data in your document without actually deleting the document object. This is particularly necessary for SDI apps, where the same document object is reused. DeleteContents() also is called just before your document object is destroyed. The default implementation of DeleteContents does nothing, but it can be overridden like this:

void CMyDoc::DeleteContents()
{
    // clear data elements
    m_MyCounter = 0;
    // delete dynamic objects created by your document
    delete(m_MyObjectPtr);
    m_MyObjectPtr = NULL;
}  // end DeleteContents()

You also may want to call this function to implement something like an Edit | Clear All function to clear your document.

OnOpenDocument()

Whenever the user chooses the File | Open command from a menu, the default handler will call the OnOpenDocument() function of your document object. The default implementation opens the file specified in lpszPathName, calls DeleteContents() to clear out the document object, resets the dirty flag for the document, and then calls the Serialize() function to load the new document from the file. Once again, SDI apps will reuse the same document object, and MDI apps will create a new one. If you need to do any initialization of your document that is not provided by the Serialize() function, you can override OnOpenDocument() like this:

BOOL CMyDoc::OnOpenDocument(LPCTSTR lpszPathName)
{
    if(!CDocument::OnOpenDocument())
        return FALSE;
    // Perform any additional initialization for your document here
    return TRUE;
}  // end OnOpenDocument

If this function returns FALSE, the open document operation will fail.

OnSaveDocument()

After the user selects either the Save or Save As command from the File menu, the framework calls the OnSaveDocument() function of your document class. For most applications, the default implementation is adequate. It opens the selected file, calls the Serialize() function to write your document data to the file, and resets the dirty flag.

OnCloseDocument()

The OnCloseDocument() function is called by the framework whenever the user closes a document. The default implementation calls the DeleteContents() member of your document, and then closes the frame windows for all views associated with this document.

ReportSaveLoadException()

If, while saving or loading your document, an exception is thrown that is not handled within your code, the framework calls the ReportSaveLoadException() function, which presents error messages to the user. You can override this function if you want to do any special messaging. You'll learn more about exceptions in Chapter 14, "Error Detection and Exception Handling Techniques."

Accessing Your Document

MFC provides several ways for you to access your document object from within your application. All objects derived from CView are associated with a document object when they are created. You can access the document associated with a view by calling CView::GetDocument(). In addition, you can access the currently active document from any CFrameWnd derivative, including your MDI main frame, by calling CFrameWnd::GetActiveDocument(). If you want to find the active document for your application, you can use the m_pMainWnd member of your CWinApp object like this:

pDoc = theApp.m_pMainWnd.GetActiveDocument();

Share ThisShare This

Informit Network