Your First DocumentView Application

[Previous] [Next]

Now that you have an idea of what the document/view architecture is all about and a feel for some of the implementation details, let's write a document/view application. If some of the concepts covered in the first part of this chapter seem a little abstract, seeing the code for a working document/view application should help bring things into focus.

The SdiSquares Application

The program shown in Figure 9-3 is an SDI document/view application that displays a grid of squares four rows deep and four columns wide. Initially, each square is colored white. However, you can change a square's color by clicking it with the left mouse button. By default, clicking changes a square's color to red. You can select alternate colors from the Color menu and thereby create a multicolored grid containing squares of up to six different colors.

click to view at full size.

Figure 9-3. The SdiSquares window.

Though SdiSquares is but a rudimentary document/view application, it demonstrates all the basic tenets of the document/view architecture. Four fundamental classes play critical roles in the application's operation:The SdiSquares window.

  • The application class CSquaresApp, which is derived from CWinApp
  • The frame window class CMainFrame, which is derived from CFrameWnd
  • The document class CSquaresDoc, which is derived from CDocument
  • The view class CSquaresView, which is derived from CView

The source code for these classes is reproduced in Figure 9-4.

In SdiSquares, a "document" consists of a two-dimensional array of COLORREF values defining the color of each square, plus an additional COLORREF value that defines the "current color"—the color assigned to a square when the square is clicked. The colors of the squares are stored in a protected CSquaresDoc member variable named m_clrGrid, which is a 4 by 4 array of COLORREF values. The current color is stored in a separate CSquaresDoc member variable named m_clrCurrentColor. In the document's OnNewDocument function, all 16 elements of m_clrGrid are initialized to white and m_clrCurrentColor is initialized to red. These variables are initialized in OnNewDocument instead of CSquaresDoc's constructor to ensure that they are reset whenever a new document is created. If they were initialized in the document's constructor instead, they would be initialized only once—when the application starts up—and would retain their current values when a new document is created since an SDI application constructs a document object just one time and reuses it as documents are created and destroyed.

To expose the document's data to the view, CSquaresDoc implements three public member functions. GetCurrentColor returns the current color (the value of m_clrCurrentColor). GetSquare returns the color of the square at a given row and column address (m_clrGrid[i][j]). SetSquare assigns a color to the square at a specified row and column address. After assigning a color to a square, SetSquare calls the document's SetModifiedFlag to mark the document as dirty and UpdateAllViews to force the view to repaint to show the updated grid:

 m_clrGrid[i][j] = color; SetModifiedFlag (TRUE); UpdateAllViews (NULL); 

GetCurrentColor, GetSquare, and SetSquare serve as a bridge between the document and the view. The view can't access the document's data members directly since they are protected, but it can call the document's accessor functions because they are declared public.

The view's OnDraw function draws the grid on the screen. The colored squares are drawn by a nested for loop that iterates through the grid one row and one column at a time. Each iteration through the loop, the view retrieves the color of the corresponding square by calling the document's GetSquare function through the pDoc pointer that holds the value returned by GetDocument:

 CSquaresDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc);      for (int i=0; i<4; i++) {     for (int j=0; j<4; j++) {         COLORREF color = pDoc->GetSquare (i, j);         CBrush brush (color);         int x1 = (j * 100) + 50;         int y1 = (i * -100) - 50;         int x2 = x1 + 100;         int y2 = y1 - 100;         CRect rect (x1, y1, x2, y2);         pDC->FillRect (rect, &brush);     } } 

AppWizard inserted the calls to GetDocument and ASSERT_VALID; I added all the other statements in OnDraw. OnDraw uses negative y values in its computations because it does its drawing in the MM_LOENGLISH mapping mode, where client-area y coordinates are negative.

The view includes a WM_LBUTTONDOWN handler that converts the click coordinates from device coordinates to MM_LOENGLISH coordinates and then tests to see which, if any, of the squares was clicked. If the click occurred in a square, CSquaresView::OnLButtonDown calls the document's GetCurrentColor function to retrieve the current color:

 CSquaresDoc* pDoc = GetDocument (); COLORREF clrCurrentColor = pDoc->GetCurrentColor (); 

It then calls the document's SetSquare function to assign that color to the square in which the click occurred:

 pDoc->SetSquare (i, j, clrCurrentColor); 

You can clearly see here how the public member functions added to CSquaresDoc are used to bridge the gulf between the document and the view, and why GetDocument is such an important function. And because the view has no notion of how the document's data is physically stored, you could alter the document's internal storage mechanism without affecting the view class one bit.

I placed the command and update handlers for the commands in the Color menu in the document class because m_clrCurrentColor is a member of the document class. The command handlers simply assign an RGB color value to m_clrCurrentColor. The update handlers use CCmdUI::SetRadio to bullet the current color. Rather than write six separate command handlers and six separate update handlers, I could have used MFC's ON_COMMAND_RANGE and ON_UPDATE_COMMAND_UI_RANGE macros to service all six menu items with one command handler and one update handler. Because ClassWizard provides no means for outputting RANGE macros, however, these macros, if used, would have to be added by hand.

When the user saves an SdiSquares document to disk or reads it back, MFC calls the document's Serialize function. CSquaresDoc::Serialize responds by serializing m_clrGrid and m_clrCurrentColor to the archive if the document is being saved or by serializing them from the archive if the document is being loaded. Here's the code that does the work:

 void CSquaresDoc::Serialize(CArchive& ar) {     if (ar.IsStoring())     {         for (int i=0; i<4; i++)             for (int j=0; j<4; j++)                 ar << m_clrGrid[i][j];         ar << m_clrCurrentColor;     }     else     {         for (int i=0; i<4; i++)             for (int j=0; j<4; j++)                 ar >> m_clrGrid[i][j];         ar >> m_clrCurrentColor;     } } 

Because a COLORREF is a DWORD and MFC overloads the << and >> operators for DWORDs, m_clrGrid and m_clrCurrentColor values can be serialized with syntactical ease. AppWizard generates a do-nothing Serialize function that includes the call to IsStoring. You supply the code that streams the document's persistent data in and out. Note that MFC handles the dirty work of displaying an Open or Save As dialog to the user, opening the file for reading or writing, and so on. That's why the process of saving and loading documents is typically much less work in document/view applications than it is in conventional applications.

As you can probably tell from Figure 9-4, I used AppWizard to generate SdiSquares' initial source code and ClassWizard to write message handlers, command handlers, and update handlers. I didn't touch the code that AppWizard generated for the application class and frame window class because I didn't need to. The bulk of my work involved the document and view classes, which is typical of the document/view application development process.

Figure 9-4. The SdiSquares application

SdiSquares.h

// SdiSquares.h : main header file for the SDISQUARES application // #if !defined(     AFX_SDISQUARES_H__00156CE5_BB17_11D2_A2FD_0000861BAE71__INCLUDED_) #define AFX_SDISQUARES_H__00156CE5_BB17_11D2_A2FD_0000861BAE71__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 #ifndef __AFXWIN_H__     #error include `stdafx.h' before including this file for PCH #endif #include "resource.h"       // main symbols /////////////////////////////////////////////////////////////////////////// // CSquaresApp: // See SdiSquares.cpp for the implementation of this class // class CSquaresApp : public CWinApp { public:     CSquaresApp(); // Overrides     // ClassWizard generated virtual function overrides     //{{AFX_VIRTUAL(CSquaresApp)     public:     virtual BOOL InitInstance();     //}}AFX_VIRTUAL // Implementation     //{{AFX_MSG(CSquaresApp)     afx_msg void OnAppAbout();        // NOTE - the ClassWizard will add and remove member functions here.        //    DO NOT EDIT what you see in these blocks of generated code !     //}}AFX_MSG     DECLARE_MESSAGE_MAP() }; /////////////////////////////////////////////////////////////////////////// //{{AFX_INSERT_LOCATION}} // Microsoft Visual C++ will insert additional declarations  // immediately before the previous line. #endif  // !defined( //     AFX_SDISQUARES_H__00156CE5_BB17_11D2_A2FD_0000861BAE71__INCLUDED_) 

SdiSquares.cpp

// SdiSquares.cpp : Defines the class behaviors for the application. // #include "stdafx.h" #include "SdiSquares.h" #include "MainFrm.h" #include "SquaresDoc.h" #include "SquaresView.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif /////////////////////////////////////////////////////////////////////////// // CSquaresApp BEGIN_MESSAGE_MAP(CSquaresApp, CWinApp)     //{{AFX_MSG_MAP(CSquaresApp)     ON_COMMAND(ID_APP_ABOUT, OnAppAbout)         // NOTE - the ClassWizard will add and remove mapping macros here.         //    DO NOT EDIT what you see in these blocks of generated code!     //}}AFX_MSG_MAP     // Standard file based document commands     ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)     ON_COMMAND(ID_FILE_OPEN, CWinApp::OnFileOpen) END_MESSAGE_MAP() /////////////////////////////////////////////////////////////////////////// // CSquaresApp construction CSquaresApp::CSquaresApp() {     // TODO: add construction code here,     // Place all significant initialization in InitInstance } /////////////////////////////////////////////////////////////////////////// // The one and only CSquaresApp object CSquaresApp theApp; /////////////////////////////////////////////////////////////////////////// // CSquaresApp initialization BOOL CSquaresApp::InitInstance() {     // Standard initialization     // If you are not using these features and wish to reduce the size     //  of your final executable, you should remove from the following     //  the specific initialization routines you do not need.     // Change the registry key under which our settings are stored.     // TODO: You should modify this string to be something appropriate     // such as the name of your company or organization.     SetRegistryKey(_T("Local AppWizard-Generated Applications"));     LoadStdProfileSettings();  // Load standard INI file                                 // options (including MRU)     // Register the application's document templates.  Document templates     //  serve as the connection between documents, frame windows and views.     CSingleDocTemplate* pDocTemplate;     pDocTemplate = new CSingleDocTemplate(         IDR_MAINFRAME,         RUNTIME_CLASS(CSquaresDoc),         RUNTIME_CLASS(CMainFrame),       // main SDI frame window         RUNTIME_CLASS(CSquaresView));     AddDocTemplate(pDocTemplate);     // Enable DDE Execute open     EnableShellOpen();     RegisterShellFileTypes(TRUE);     // 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();     // Enable drag/drop open     m_pMainWnd->DragAcceptFiles();     return TRUE; } /////////////////////////////////////////////////////////////////////////// // CAboutDlg dialog used for App About class CAboutDlg : public CDialog { public:     CAboutDlg(); // Dialog Data     //{{AFX_DATA(CAboutDlg)     enum { IDD = IDD_ABOUTBOX };     //}}AFX_DATA     // ClassWizard generated virtual function overrides     //{{AFX_VIRTUAL(CAboutDlg)     protected:     virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support     //}}AFX_VIRTUAL // Implementation protected:     //{{AFX_MSG(CAboutDlg)         // No message handlers     //}}AFX_MSG     DECLARE_MESSAGE_MAP() }; CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD) {     //{{AFX_DATA_INIT(CAboutDlg)     //}}AFX_DATA_INIT } void CAboutDlg::DoDataExchange(CDataExchange* pDX) {     CDialog::DoDataExchange(pDX);     //{{AFX_DATA_MAP(CAboutDlg)     //}}AFX_DATA_MAP } BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)     //{{AFX_MSG_MAP(CAboutDlg)         // No message handlers     //}}AFX_MSG_MAP END_MESSAGE_MAP() // App command to run the dialog void CSquaresApp::OnAppAbout() {     CAboutDlg aboutDlg;     aboutDlg.DoModal(); } /////////////////////////////////////////////////////////////////////////// // CSquaresApp message handlers 

MainFrm.h

// MainFrm.h : interface of the CMainFrame class // /////////////////////////////////////////////////////////////////////////// #if !defined(     AFX_MAINFRM_H__00156CE9_BB17_11D2_A2FD_0000861BAE71__INCLUDED_) #define AFX_MAINFRM_H__00156CE9_BB17_11D2_A2FD_0000861BAE71__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 class CMainFrame : public CFrameWnd {      protected: // create from serialization only     CMainFrame();     DECLARE_DYNCREATE(CMainFrame) // Attributes public: // Operations public: // Overrides     // ClassWizard generated virtual function overrides     //{{AFX_VIRTUAL(CMainFrame)     virtual BOOL PreCreateWindow(CREATESTRUCT& cs);     //}}AFX_VIRTUAL // Implementation public:     virtual ~CMainFrame(); #ifdef _DEBUG     virtual void AssertValid() const;     virtual void Dump(CDumpContext& dc) const; #endif // Generated message map functions protected:     //{{AFX_MSG(CMainFrame)        // NOTE - the ClassWizard will add and remove member functions here.        //    DO NOT EDIT what you see in these blocks of generated code!     //}}AFX_MSG     DECLARE_MESSAGE_MAP() }; /////////////////////////////////////////////////////////////////////////// //{{AFX_INSERT_LOCATION}} // Microsoft Visual C++ will insert additional declarations  // immediately before the previous line. #endif  // !defined(AFX_MAINFRM_H__00156CE9_BB17_11D2_A2FD_0000861BAE71__INCLUDED_) 

MainFrm.cpp

// MainFrm.cpp : implementation of the CMainFrame class // #include "stdafx.h" #include "SdiSquares.h" #include "MainFrm.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif /////////////////////////////////////////////////////////////////////////// // CMainFrame IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd) BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)     //{{AFX_MSG_MAP(CMainFrame)         // NOTE - the ClassWizard will add and remove mapping macros here.         //    DO NOT EDIT what you see in these blocks of generated code !     //}}AFX_MSG_MAP END_MESSAGE_MAP() /////////////////////////////////////////////////////////////////////////// // CMainFrame construction/destruction CMainFrame::CMainFrame() {     // TODO: add member initialization code here      } CMainFrame::~CMainFrame() { } BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) {     if( !CFrameWnd::PreCreateWindow(cs) )         return FALSE;     // TODO: Modify the Window class or styles here by modifying     //  the CREATESTRUCT cs     return TRUE; } /////////////////////////////////////////////////////////////////////////// // CMainFrame diagnostics #ifdef _DEBUG void CMainFrame::AssertValid() const {     CFrameWnd::AssertValid(); } void CMainFrame::Dump(CDumpContext& dc) const {     CFrameWnd::Dump(dc); } #endif //_DEBUG /////////////////////////////////////////////////////////////////////////// // CMainFrame message handlers 

SquaresDoc.h

// SquaresDoc.h : interface of the CSquaresDoc class // /////////////////////////////////////////////////////////////////////////// #if !defined(     AFX_SQUARESDOC_H__00156CEB_BB17_11D2_A2FD_0000861BAE71__INCLUDED_) #define AFX_SQUARESDOC_H__00156CEB_BB17_11D2_A2FD_0000861BAE71__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 class CSquaresDoc : public CDocument { protected: // create from serialization only     CSquaresDoc();     DECLARE_DYNCREATE(CSquaresDoc) // Attributes public: // Operations public: // Overrides     // ClassWizard generated virtual function overrides     //{{AFX_VIRTUAL(CSquaresDoc)     public:     virtual BOOL OnNewDocument();     virtual void Serialize(CArchive& ar);     //}}AFX_VIRTUAL // Implementation public:     void SetSquare (int i, int j, COLORREF color);     COLORREF GetSquare (int i, int j);     COLORREF GetCurrentColor();     virtual ~CSquaresDoc(); #ifdef _DEBUG     virtual void AssertValid() const;     virtual void Dump(CDumpContext& dc) const; #endif protected: // Generated message map functions protected:     COLORREF m_clrCurrentColor;     COLORREF m_clrGrid[4][4];     //{{AFX_MSG(CSquaresDoc)     afx_msg void OnColorRed();     afx_msg void OnColorYellow();     afx_msg void OnColorGreen();     afx_msg void OnColorCyan();     afx_msg void OnColorBlue();     afx_msg void OnColorWhite();     afx_msg void OnUpdateColorRed(CCmdUI* pCmdUI);     afx_msg void OnUpdateColorYellow(CCmdUI* pCmdUI);     afx_msg void OnUpdateColorGreen(CCmdUI* pCmdUI);     afx_msg void OnUpdateColorCyan(CCmdUI* pCmdUI);     afx_msg void OnUpdateColorBlue(CCmdUI* pCmdUI);     afx_msg void OnUpdateColorWhite(CCmdUI* pCmdUI);     //}}AFX_MSG     DECLARE_MESSAGE_MAP() }; /////////////////////////////////////////////////////////////////////////// //{{AFX_INSERT_LOCATION}} // Microsoft Visual C++ will insert additional declarations  // immediately before the previous line. #endif  // !defined( //     AFX_SQUARESDOC_H__00156CEB_BB17_11D2_A2FD_0000861BAE71__INCLUDED_) 

SquaresDoc.cpp

// SquaresDoc.cpp : implementation of the CSquaresDoc class // #include "stdafx.h" #include "SdiSquares.h" #include "SquaresDoc.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif /////////////////////////////////////////////////////////////////////////// // CSquaresDoc IMPLEMENT_DYNCREATE(CSquaresDoc, CDocument) BEGIN_MESSAGE_MAP(CSquaresDoc, CDocument)     //{{AFX_MSG_MAP(CSquaresDoc)     ON_COMMAND(ID_COLOR_RED, OnColorRed)     ON_COMMAND(ID_COLOR_YELLOW, OnColorYellow)     ON_COMMAND(ID_COLOR_GREEN, OnColorGreen)     ON_COMMAND(ID_COLOR_CYAN, OnColorCyan)     ON_COMMAND(ID_COLOR_BLUE, OnColorBlue)     ON_COMMAND(ID_COLOR_WHITE, OnColorWhite)     ON_UPDATE_COMMAND_UI(ID_COLOR_RED, OnUpdateColorRed)     ON_UPDATE_COMMAND_UI(ID_COLOR_YELLOW, OnUpdateColorYellow)     ON_UPDATE_COMMAND_UI(ID_COLOR_GREEN, OnUpdateColorGreen)     ON_UPDATE_COMMAND_UI(ID_COLOR_CYAN, OnUpdateColorCyan)     ON_UPDATE_COMMAND_UI(ID_COLOR_BLUE, OnUpdateColorBlue)     ON_UPDATE_COMMAND_UI(ID_COLOR_WHITE, OnUpdateColorWhite)     //}}AFX_MSG_MAP END_MESSAGE_MAP() /////////////////////////////////////////////////////////////////////////// // CSquaresDoc construction/destruction CSquaresDoc::CSquaresDoc() {     // TODO: add one-time construction code here } CSquaresDoc::~CSquaresDoc() { } BOOL CSquaresDoc::OnNewDocument() {     if (!CDocument::OnNewDocument())         return FALSE;     for (int i=0; i<4; i++)         for (int j=0; j<4; j++)             m_clrGrid[i][j] = RGB (255, 255, 255);     m_clrCurrentColor = RGB (255, 0, 0);     return TRUE; } /////////////////////////////////////////////////////////////////////////// // CSquaresDoc serialization void CSquaresDoc::Serialize(CArchive& ar) {     if (ar.IsStoring())     {         for (int i=0; i<4; i++)             for (int j=0; j<4; j++)                 ar << m_clrGrid[i][j];         ar << m_clrCurrentColor;     }     else     {         for (int i=0; i<4; i++)             for (int j=0; j<4; j++)                 ar >> m_clrGrid[i][j];         ar >> m_clrCurrentColor;     } } /////////////////////////////////////////////////////////////////////////// // CSquaresDoc diagnostics #ifdef _DEBUG void CSquaresDoc::AssertValid() const {     CDocument::AssertValid(); } void CSquaresDoc::Dump(CDumpContext& dc) const {     CDocument::Dump(dc); } #endif //_DEBUG /////////////////////////////////////////////////////////////////////////// // CSquaresDoc commands COLORREF CSquaresDoc::GetCurrentColor() {     return m_clrCurrentColor; } COLORREF CSquaresDoc::GetSquare(int i, int j) {     ASSERT (i >= 0 && i <= 3 && j >= 0 && j <= 3);     return m_clrGrid[i][j]; } void CSquaresDoc::SetSquare(int i, int j, COLORREF color) {     ASSERT (i >= 0 && i <= 3 && j >= 0 && j <= 3);     m_clrGrid[i][j] = color;     SetModifiedFlag (TRUE);     UpdateAllViews (NULL); } void CSquaresDoc::OnColorRed()  {     m_clrCurrentColor = RGB (255, 0, 0);     } void CSquaresDoc::OnColorYellow()  {     m_clrCurrentColor = RGB (255, 255, 0);     } void CSquaresDoc::OnColorGreen()  {     m_clrCurrentColor = RGB (0, 255, 0);     } void CSquaresDoc::OnColorCyan()  {     m_clrCurrentColor = RGB (0, 255, 255);     }x void CSquaresDoc::OnColorBlue()  {     m_clrCurrentColor = RGB (0, 0, 255);     } void CSquaresDoc::OnColorWhite()  {     m_clrCurrentColor = RGB (255, 255, 255);     } void CSquaresDoc::OnUpdateColorRed(CCmdUI* pCmdUI)  {     pCmdUI->SetRadio (m_clrCurrentColor == RGB (255, 0, 0));     } void CSquaresDoc::OnUpdateColorYellow(CCmdUI* pCmdUI)  {     pCmdUI->SetRadio (m_clrCurrentColor == RGB (255, 255, 0));     } void CSquaresDoc::OnUpdateColorGreen(CCmdUI* pCmdUI)  {     pCmdUI->SetRadio (m_clrCurrentColor == RGB (0, 255, 0));     } void CSquaresDoc::OnUpdateColorCyan(CCmdUI* pCmdUI)  {     pCmdUI->SetRadio (m_clrCurrentColor == RGB (0, 255, 255));     } void CSquaresDoc::OnUpdateColorBlue(CCmdUI* pCmdUI)  {     pCmdUI->SetRadio (m_clrCurrentColor == RGB (0, 0, 255));     } void CSquaresDoc::OnUpdateColorWhite(CCmdUI* pCmdUI)  {     pCmdUI->SetRadio (m_clrCurrentColor == RGB (255, 255, 255));     } 

SquaresView.h

// SquaresView.h : interface of the CSquaresView class // /////////////////////////////////////////////////////////////////////////// #if !defined(     AFX_SQUARESVIEW_H__00156CED_BB17_11D2_A2FD_0000861BAE71__INCLUDED_) #define AFX_SQUARESVIEW_H__00156CED_BB17_11D2_A2FD_0000861BAE71__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 class CSquaresView : public CView { protected: // create from serialization only     CSquaresView();     DECLARE_DYNCREATE(CSquaresView) // Attributes public:     CSquaresDoc* GetDocument(); // Operations public: // Overrides     // ClassWizard generated virtual function overrides     //{{AFX_VIRTUAL(CSquaresView)     public:     virtual void OnDraw(CDC* pDC);  // overridden to draw this view     virtual BOOL PreCreateWindow(CREATESTRUCT& cs);     protected:     //}}AFX_VIRTUAL // Implementation public:     virtual ~CSquaresView(); #ifdef _DEBUG     virtual void AssertValid() const;     virtual void Dump(CDumpContext& dc) const; #endif protected: // Generated message map functions protected:     //{{AFX_MSG(CSquaresView)     afx_msg void OnLButtonDown(UINT nFlags, CPoint point);     //}}AFX_MSG     DECLARE_MESSAGE_MAP() }; #ifndef _DEBUG  // debug version in SquaresView.cpp inline CSquaresDoc* CSquaresView::GetDocument()    { return (CSquaresDoc*)m_pDocument; } #endif /////////////////////////////////////////////////////////////////////////// //{{AFX_INSERT_LOCATION}} // Microsoft Visual C++ will insert additional declarations  // immediately before the previous line. #endif  // !defined( //     AFX_SQUARESVIEW_H__00156CED_BB17_11D2_A2FD_0000861BAE71__INCLUDED_) 

SquaresView.cpp

// SquaresView.cpp : implementation of the CSquaresView class // #include "stdafx.h" #include "SdiSquares.h" #include "SquaresDoc.h" #include "SquaresView.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif /////////////////////////////////////////////////////////////////////////// // CSquaresView IMPLEMENT_DYNCREATE(CSquaresView, CView) BEGIN_MESSAGE_MAP(CSquaresView, CView)     //{{AFX_MSG_MAP(CSquaresView)     ON_WM_LBUTTONDOWN()     //}}AFX_MSG_MAP END_MESSAGE_MAP() /////////////////////////////////////////////////////////////////////////// // CSquaresView construction/destruction CSquaresView::CSquaresView() {     // TODO: add construction code here } CSquaresView::~CSquaresView() { } BOOL CSquaresView::PreCreateWindow(CREATESTRUCT& cs) {     // TODO: Modify the Window class or styles here by modifying     //  the CREATESTRUCT cs     return CView::PreCreateWindow(cs); } /////////////////////////////////////////////////////////////////////////// // CSquaresView drawing void CSquaresView::OnDraw(CDC* pDC) {     CSquaresDoc* pDoc = GetDocument();     ASSERT_VALID(pDoc);     //     // Set the mapping mode to MM_LOENGLISH.     //     pDC->SetMapMode (MM_LOENGLISH);     //     // Draw the 16 squares.     //x     for (int i=0; i<4; i++) {         for (int j=0; j<4; j++) {             COLORREF color = pDoc->GetSquare (i, j);             CBrush brush (color);             int x1 = (j * 100) + 50;             int y1 = (i * -100) - 50;             int x2 = x1 + 100;             int y2 = y1 - 100;             CRect rect (x1, y1, x2, y2);             pDC->FillRect (rect, &brush);         }     }     //     // Then the draw the grid lines surrounding them.     //     for (int x=50; x<=450; x+=100) {         pDC->MoveTo (x, -50);         pDC->LineTo (x, -450);     }     for (int y=-50; y>=-450; y-=100) {         pDC->MoveTo (50, y);         pDC->LineTo (450, y);     } } /////////////////////////////////////////////////////////////////////////// // CSquaresView diagnostics #ifdef _DEBUG void CSquaresView::AssertValid() const {     CView::AssertValid(); } void CSquaresView::Dump(CDumpContext& dc) const {     CView::Dump(dc); } CSquaresDoc* CSquaresView::GetDocument() // non-debug version is inline {     ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CSquaresDoc)));     return (CSquaresDoc*)m_pDocument; } #endif //_DEBUG /////////////////////////////////////////////////////////////////////////// // CSquaresView message handlers void CSquaresView::OnLButtonDown(UINT nFlags, CPoint point)  {     CView::OnLButtonDown(nFlags, point);     //     // Convert to click coordinates to MM_LOENGLISH units.     //     CClientDC dc (this);     dc.SetMapMode (MM_LOENGLISH);     CPoint pos = point;     dc.DPtoLP (&pos);     //     // If a square was clicked, set its color to the current color.     //     if (pos.x >= 50 && pos.x <= 450 && pos.y <= -50 && pos.y >= -450) {         int i = (-pos.y - 50) / 100;         int j = (pos.x - 50) / 100;         CSquaresDoc* pDoc = GetDocument ();         COLORREF clrCurrentColor = pDoc->GetCurrentColor ();         pDoc->SetSquare (i, j, clrCurrentColor);     } } 

SdiSquares Step by Step

It's important to understand how SdiSquares works, but it's also important to understand how it was created. When you use AppWizard and ClassWizard to craft MFC applications, the wizards write part of the code and you write the rest. Moreover, there's a process involved. Although I don't intend to document every single button click required to create SdiSquares, I would be remiss if I didn't provide at least an overview of the process. Here, then, is a step-by-step account of how SdiSquares came together and how you can create the application yourself.

  1. Use AppWizard to create a new project named SdiSquares. In AppWizard's Step 1 dialog box, choose Single Document as the application type and check the Document/View Architecture Support box, as shown in Figure 9-5. In the Step 3 dialog box, uncheck the ActiveX Controls box. In Step 4, uncheck Docking Toolbar, Initial Status Bar, Printing And Print Preview, and 3D Controls. Also in the Step 4 dialog box, click the Advanced button and type the letters sqr into the File Extension box (as shown in Figure 9-6) to define the default file name extension for SdiSquares documents. In the Step 6 dialog box, manually edit the class names to match the ones in Figure 9-4. Everywhere else, accept the AppWizard defaults.
  2. click to view at full size.

    Figure 9-5. Creating an SDI document/view application with AppWizard.

    Figure 9-6. Specifying the default file name extension for SdiSquares documents.

  3. Add the member variables m_clrGrid and m_clrCurrentColor to the document class, and add code to initialize them to OnNewDocument. AppWizard overrides OnNewDocument, so all you have to do is add the statements that initialize the data members.
  4. Add the member functions GetCurrentColor, GetSquare, and SetSquare to the document class. Be sure to make them public member functions, since they must be accessible to the view.
  5. Modify the Serialize function that AppWizard included in the document class to serialize m_clrGrid and m_clrCurrentColor.
  6. Implement the view's OnDraw function. AppWizard generates a do-nothing OnDraw function; you write the code to perform application-specific duties.
  7. Add the WM_LBUTTONDOWN handler (OnLButtonDown) to the view. You can add the message handler by hand or use ClassWizard to add it. I used ClassWizard.
  8. Open the AppWizard-generated application menu for editing, delete the Edit menu, and add the Color menu. Then write command and update handlers for the new menu items. As with message handlers, you can add command and update handlers manually or you can add them with ClassWizard's help. Once again, I used ClassWizard.

You can add a nice finishing touch by editing the application's icons. AppWizard generated two icons when it created the project. IDR_MAINFRAME is the application icon—the one that appears in the frame window's title bar. IDR_SDISQUTYPE is the application's document icon, which is used to represent SdiSquares document files in the operating system shell. The document icon is registered with the system when InitInstance calls RegisterShellFileTypes.



Programming Windows with MFC
Programming Windows with MFC, Second Edition
ISBN: 1572316950
EAN: 2147483647
Year: 1999
Pages: 101
Authors: Jeff Prosise

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net