[ Home | Syllabus | Course Notes | Assignments | Search]
SdiSquares Example Chapter 9. 4 x 4 grid of squares, can change color of each square using menu to select color and mouse to select square. Can save/restore from file.
Document/View divides responsibility among the underlying objects.
Start with the Document. What information about the application must be remembered?
In SquaresDoc.h
COLORREF m_clrCurrentColor; // current selected color COLORREF m_clrGrid[4][4]; // color for each square
public: void SetSquare (int i, int j, COLORREF color); COLORREF GetSquare (int i, int j); COLORREF GetCurrentColor();
In SquaresDoc.cpp
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); // initially
"white"
m_clrCurrentColor = RGB (255, 0, 0); // initially
"red"
return TRUE;
}
F1: Because SDI, new documents reuse same Document object - So this is called to set/reset the values of a "new" document instead of using the constructor
Now look at how view uses this information to draw the screen
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.
//
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; // do some arithmetic to define 1
inch squares
int y1 = (i * -100) - 50;
int x2 = x1 + 100;
int y2 = y1 - 100;
CRect rect (x1, y1, x2, y2);
pDC->FillRect (rect, &brush); // fill rectangle
with colored 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);
}
}
F2: Framework associates document with this view and this function returns a pointer to that object.
F3: use English inch measurement (low resolution 1 unit is 0.01inch)
F4: get information from the document by calling a member function for each squares color
Important point is that view asked document for information needed to draw a view of the document.
NOTE: there are other ways to "view" this data - for example, a list of the colors of each square (white,red, red, green, ....) or put the color names in a grid. The document's data is the color of each of the 16 squares and the current selected color.
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); // convert from device to logical
point
//
// 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; // compute row and column
int j = (pos.x - 50) / 100;
CSquaresDoc* pDoc = GetDocument ();
COLORREF clrCurrentColor = pDoc->GetCurrentColor
();
pDoc->SetSquare (i, j, clrCurrentColor);
}
}
F5: This updates the document's information. But how is the view updated? Document must call UpdateView - see below.
void CSquaresDoc::SetSquare(int
i, int j, COLORREF color)
{
ASSERT (i >= 0 && i <= 3 && j >= 0 && j <=
3); // robust programming
m_clrGrid[i][j] = color; // update state of document
object
SetModifiedFlag (TRUE); // so we are reminded before
quitting
UpdateAllViews (NULL); // send message to view to update
}
Why call UpdateAllViews - why not let view do update directly since it calls SetSquare? because this allows the document to control when expensive updates need to be done.
Document handles all menu/menu update command messages (see routing). For example
void CSquaresDoc::OnUpdateColorRed(CCmdUI* pCmdUI)
{
pCmdUI->SetRadio (m_clrCurrentColor == RGB (255, 0, 0));
}
Document/View has a lot of support for saving/restoring documents to file. Has file new/open/save/save as menu items - supplies file selection dialog boxes. The only thing the document class must do is code which data members to save and how - and most MFC objects know how to save/restore themselves using overloads to the "<<" and ">>" operators. Here is the code here
void CSquaresDoc::Serialize(CArchive&
ar) // CArchive is a file
{
if (ar.IsStoring()) // ask if saving or restoring
{ // save data
for (int i=0; i<4; i++)
for (int j=0; j<4; j++)
ar << m_clrGrid[i][j]; // COLOREF object knows
how to save itself
ar << m_clrCurrentColor;
}
else
{ // restore data
for (int i=0; i<4; i++)
for (int j=0; j<4; j++)
ar >> m_clrGrid[i][j];
ar >> m_clrCurrentColor;
}
}