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 initialising 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 a 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 initialisation 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 initialised, 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; }