Home | API | MFC | C++ | C | Previous | Next

Programming Windows with MFC

Document View Architecture

The MFC Document View Architecture is a framework designed to separate the storage and maintenance of data from the display of data. This is achieved by encapsulating the data within a document class and encapsulating the presentation specifics within a view class. The advantage of decoupling the view from the application data is of most use in larger applications when there are multiple views and multiple different types of date object to incorporate into an application.

MFC supports two types of document/view applications: single-document and multiple document interface (SDI) applications.

Single Document Interface

The expression Single Document Interface or SDI refers to a document that presents only one view to the user. This means that the application is limited to displaying one document at a time. Notepad is an example of a SDI application. Notepad cannot open more that one text file at once without starting up another instance of the application. A simple single document/view application will contain four key class components-

The CDocumet class provides the basic functionality for an application’s document object. This functionality includes the ability to create load, save and update documents. This process of writing or reading data to storage medium is known as serialization. The serialization objects supplied with MFC provides a standard and consistent interface, relieving the user from the need to perform file operations manually.

The CView Class encapsulates the visual presentation of the document data rendering an image of the document on the screen or printer and handling user interaction through the view window. MFC provides several variations on the view class and the capabilities of a view class will depend on the MFC view class from which it derives. Further information about these view classes can be found at the follow
https://docs.microsoft.com/en-us/cpp/mfc/derived-view-classes-available-in-mfc?view=vs-2019

The CMainFrame class encapsulates the frame window. MFC places the application view window into the client area of the frame window. In an SDI application, the view window is a child of the main frame window.

The CWinApp class is the base class from which every MFC programmer derives a Windows application object. This application object provides member functions for initializing and running the application.

Dynamic Creation

In a document view application the view , frames and documents objects are created dynamically using the following marco’s

DECLARE_DYNCREATE (class name) – found in the class declaration and allows dynamic creation of the class
IMPLEMENT_DYNCREATE(class-name,parent name)-found inside the class declaration. Thr class name name of the class being enabled and parent class if the name of the MFC base class

Once the documents view and frame class have been created then can be used as parameters in a runtime class structure which in turn is used as a parameter to create the document template using the class CsingleDocTemplate. The prototype of this class is –

CSingleDocTemplate( UINT nIDResource, CRuntimeClass* pDocClass, CRuntimeClass* pFrameClass, CRuntimeClass* pViewClass );

Where
nIDResource – Specifies the ID of the resources used with the document type.
pDocClass – Points to the CRuntimeClass object of the document class.
fFrameClass – Points to the CRuntimeClass object of the frame window class. This class can be a CFrameWnd-derived class, or the CFrameWnd itself.
pViewClass – Points to the CRuntimeClass object of the view class. This class is a CView-derived class you define to display your documents.

The newly created template can be added to the list of available document templates available to the application by calling the AddDocTemplate member function.

The CCommandLineInfo class encapsulated command line information when an application starts
And the ParseCommandLine parses the command line information.

Saving and Loading Documents

In a document view program the Serialize member function, which is defined in the CObject class, is responsible for actually loading or saving an object’s current state. The Serialize function contains a CArchive argument that it uses to read and write the object data. The IsStoring or IsLoadfing member function, indicates whether Serialize is storing (writing data) or loading (reading data). An application either inserts or retrieves an applications object’s data using the insertion operator (<<) or extract data with the extraction operator (>>).

In the following example a SDI application is created which places a sequence of markers at the position of the mouse click. Selecting the relevant display option will change the display marker from x to asterisk and vice versa


///////////////////////////////////////////////////////////////////////////
//Microsoft Visual Studio generated resource script.
///////////////////////////////////////////////////////////////////////////

IDR_MAINFRAME MENU PRELOAD DISCARDABLE
BEGIN
POPUP "&File"
BEGIN
MENUITEM "New", ID_FILE_NEW
MENUITEM "Open", ID_FILE_OPEN
MENUITEM "Save", ID_FILE_SAVE
MENUITEM "Save ", ID_FILE_SAVE_AS
MENUITEM SEPARATOR
MENUITEM "Recent File", ID_FILE_MRU_FILE1, GRAYED
MENUITEM SEPARATOR
MENUITEM "Exit", ID_APP_EXIT
END
POPUP "Select"
BEGIN
MENUITEM "View1", IDR_VIEW1
MENUITEM "View2", IDR_VIEW2
END
END
/////////////////////////////////////////////////////////////////////////////
#include "resource.h"

//application class declaration
class App : public CWinApp
{
public:
virtual BOOL InitInstance();
void SwapView1();
void SwapView2();
void Switchviews(CRuntimeClass*);
DECLARE_MESSAGE_MAP()
};

//document class declaration
class Doc : public CDocument
{
protected:
DECLARE_DYNCREATE(Doc)
DECLARE_MESSAGE_MAP()
public:
virtual BOOL OnNewDocument();
virtual void Serialize(CArchive& ar);
SetNewValue(int,int);
int px[100];
int py[100];
int clickcount;
};

//frame window class declaration
class CMainFrame : public CFrameWnd
{
protected:
DECLARE_DYNCREATE(CMainFrame)
DECLARE_MESSAGE_MAP()
};

//first view class declaration
class View : public CView
{
protected:
DECLARE_DYNCREATE(View)
DECLARE_MESSAGE_MAP()
public:
Doc* GetDocument();
void OnLButtonDown(UINT,CPoint);
virtual void OnDraw(CDC* pDC); // overridden to draw this view
};

//second view class declaration
class View1 : public CView
{
protected:
DECLARE_DYNCREATE(View1)
DECLARE_MESSAGE_MAP()
public:
Doc* GetDocument();
void OnLButtonDown(UINT,CPoint);
virtual void OnDraw(CDC* pDC);
};

// enable CObject-derived classes to be created dynamically at run time
IMPLEMENT_DYNCREATE(View1, CView)
IMPLEMENT_DYNCREATE(View, CView)
IMPLEMENT_DYNCREATE(Doc, CDocument)
IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd)

//message map for the application window
BEGIN_MESSAGE_MAP(App, CWinApp)
ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen)
ON_COMMAND(IDR_VIEW1, SwapView1)
ON_COMMAND(IDR_VIEW2, SwapView2)
END_MESSAGE_MAP()

//message map for the frame window
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
ON_WM_CREATE()
END_MESSAGE_MAP()

//message map for the document window
BEGIN_MESSAGE_MAP(Doc, CDocument)
END_MESSAGE_MAP()

//message map for the first view window
BEGIN_MESSAGE_MAP(View, CView)
ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()

//message map for the second view window
BEGIN_MESSAGE_MAP(View1, CView)
ON_WM_LBUTTONDOWN()
END_MESSAGE_MAP()

App theApp;//instantiate the application

// Application initialization
BOOL App::InitInstance()
{
//Enable and load the list of most recently used (MRU) files
LoadStdProfileSettings();
//defines a document template that implements the single document interface
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(Doc),
RUNTIME_CLASS(CMainFrame), // main SDI frame window
RUNTIME_CLASS(View));
AddDocTemplate(pDocTemplate);
// Parse command line for standard shell commands, DDE, file open
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
// Dispatch commands specified on the command line
if (!ProcessShellCommand(cmdInfo))
return FALSE;
// The one and only window has been initialized, so show and update it.
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
return TRUE;
}

//responds to swapview request view1
void App::SwapView1()
{
CRuntimeClass* crtclView = RUNTIME_CLASS(View);
Switchviews(crtclView);
}

//responds to swapview request view2
void App::SwapView2()
{
CRuntimeClass* crtclView = RUNTIME_CLASS(View1);
Switchviews(crtclView);
}

//stitches application views
void App::Switchviews(CRuntimeClass* pNewView)
{
CFrameWnd* pMainWnd = (CFrameWnd*)AfxGetMainWnd();
CView* pOldActiveView = pMainWnd->GetActiveView();
CDocument* pOldActiveDoc = pMainWnd->GetActiveDocument();
CString m_temp;
if (pOldActiveView->IsKindOf(pNewView))
return ;
SetWindowLong(pOldActiveView->m_hWnd, GWL_ID, 0);
CCreateContext context;
context.m_pNewViewClass = pNewView;
context.m_pCurrentDoc = pOldActiveDoc;
CView* pDisplayView = STATIC_DOWNCAST(CView, pMainWnd->CreateView(&context));
if (pDisplayView != NULL)
{
pDisplayView->ShowWindow(SW_SHOW);
pDisplayView->OnInitialUpdate();
pMainWnd->SetActiveView(pDisplayView);
pMainWnd->RecalcLayout();
pOldActiveView->ShowWindow(SW_HIDE);
}
}

//sets array to position of click
Doc::SetNewValue(int x,int y)
{
clickcount=clickcount+1;
if (clickcount<10)
{
px[clickcount]=x;
py[clickcount]=y;
}
}

//new document request
BOOL Doc::OnNewDocument()
{
clickcount=0;
if (!CDocument::OnNewDocument())
return FALSE;
return TRUE;
}

//deals with loading or saving the document
void Doc::Serialize(CArchive& ar)
{
//used for saving document data
if (ar.IsStoring())
{
ar<< clickcount;
for (int i=1;i<=clickcount;i++)
{
ar<<px[i];
ar<<py[i];
}
}
//loading document data
else
{
ar >> clickcount;
for (int i=1;i<=clickcount;i++)
{
ar>>px[i];
ar>>py[i];
}
}
}

//draws to first view
void View::OnDraw(CDC* pDC)
{
Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
int x=0,y=0;
int totalclicks=pDoc->clickcount;
for (int i=1;i<=totalclicks;i++)
{
x=pDoc->px[i];
y=pDoc->py[i];
pDC->TextOut(x, y, "x", 1);
}
}

//deals with left buttons click in first view
void View::OnLButtonDown(UINT nFlags, CPoint point )
{
Doc* pDoc = GetDocument();
pDoc->SetNewValue(point.x,point.y);
pDoc->UpdateAllViews(NULL);
}

Doc* View::GetDocument()
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(Doc)));
return (Doc*)m_pDocument;
}

//draws to second view
void View1::OnDraw(CDC* pDC)
{
Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
int x=0,y=0;
int totalclicks=pDoc->clickcount;
for (int i=1;i<=totalclicks;i++)
{
x=pDoc->px[i];
y=pDoc->py[i];
pDC->TextOut(x, y, "*", 1);
}
}
//deals with left buttons click in second view
void View1::OnLButtonDown(UINT nFlags, CPoint point )
{
Doc* pDoc = GetDocument();
pDoc->SetNewValue(point.x,point.y);
pDoc->UpdateAllViews(NULL);
}

Doc* View1::GetDocument()
{
ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(Doc)));
return (Doc*)m_pDocument;
}

Download Code


Home | API | MFC | C++ | C | Previous | Next
Creating a Simple Window | Processing Messages | Device Context | Working with Graphics | Mapping Modes | Text Output | Working with the Mouse | Dealing with Keyboard Input | Drawing Lines and Shapes | Adding Menus | Child Windows | Dialog Windows | Common Dialog Box | Working with Bitmaps | Common Controls | Toolbars | Document View Architecture | Multi Document Interface | Timers | MFC Collections Classes

Last Updated: 16 October 2022