Вы находитесь на странице: 1из 11

MFC (Microsoft Foundation Classes)

MFC is a popular framework for building Windows applications. It is available in Visual


C++ and C++ Builder. Selecting the MFC App Wizard option from the Visual C++ New
Project dialog box (select New from the File menu), starts a series of dialog boxes that
prompt the user for information about the features of the application to be built. After the
last dialog, the App Wizard generates the application's principle class declarations.

MFC applications use a variant of the Model-View-Controller architecture called the


Document-View architecture. Application data is stored in the document component,
which is responsible for updating the data and saving and reading the data from document
files (serialization/deserialization). The view component displays the application's data
in a window. It also may contain handlers for various user input events such as mouse
clicks.

There are two styles of MFC applications: single document interface (SDI) and multi
document interface (MDI). An SDI application only has a single document component.
WordPad is an SDI application. An MDI application can have multiple document
components. Word is an MDI application.

Static Structure

My MDI application (i.e., an application built by me using the App Wizard and literally
called My) consists of five classes. A single instance of the CMyApp class is derived
from the CApp class and contains WinMain(), which perpetually pulls user input
messages from a message queue (put there by the operating system), and dispatches them
to the other components of the application that are able to handle them. A single instance
of the CMainFrame class frames the application's desktop window. The application's
menu bar, toolbar, and status bar are associated with this object. (Note: all MFC classes
have names beginning with the letter C.)

There can be multiple instances of the CMyDocument class. This class is derived from
MFC's CDocument class. Each instance maintains a list of associated instances of the
CMyView class, which is derived from MFC's CView class. A view object displays the
associated document object's application data in a child window of the application's
desktop window. The child window is framed by an instance of the CChildFrame class.

CMyApp CApp
WinMain()

1
1

CMainFrame
menu bar
toolbar CChildFrame
status bar
1 1

*
1
CDocument CView
1 * GetDocument()
UpdataAllViews()

CMyDocument CMyView
app data OnUpdate()
OnDraw()
other handlers
Serialize()

Dynamic Structure

Similar to the MVC architecture using the Publisher-Subscriber pattern, each document
maintains a list of "subscriber" views. The Document::UpdateAllViews() function sends
a message called WM_UPDATE to each view in this list. (The message is delivered by
WinMain.) This function is typically called by MyDocument member functions that alter
the application data.

A view may have a handler function called MyView::OnUpdate(), which will be


automatically called in response to the WM_UPDATE message. Typically, OnUpdate()
uses the View::GetDocument() function to get a pointer to its associated document; it

2
reads the application data, then calls the inherited Invalidate() function, which causes the
OnDraw() function to redraw its associated window.

Example (Brick CAD)1

Brick CAD is a CAD/CAM system for designing bricks. Fortunately, bricks are
uncomplicated objects. Their main attributes are height, width, and length. There are
three ways of viewing a brick: top, side, and front view. Brick CAD users should also
have a control panel that allows them to alter a brick's dimensions. Advanced users will
want to design several bricks simultaneously, so Brick CAD is an MDI application.

Create a workspace called BC (for Brick CAD). Create an MFC App Wizard project
called BC1 within the BC workspace. Accept all the options presented in each of the App
Wizard's dialogs. In the fourth dialog box, press the Advanced button. This displays the
"Advanced Options" dialog box. From the "Window Styles" tab select "use split
window." From the "Document Template Strings" tab, type "brk" in the "File Extension"
edit control. Close the dialog, then press the Finish button on the MFC App Wizard's first
dialog box. Another dialog box appears asking you to verify all of the features you have
selected. Press the OK button.

Add three member variables to the CBC1Document class called m_height, m_width, and
m_length. All three should be public variables of type int. (Hint: Use the shortcut menu
associated with the document icon in the workspace view to add new members.)
class CBC1Doc : public CDocument
{
public:
int m_length;
int m_width;
int m_height;
// etc.

};

Application data is initialized in the OnNewDocument function:

1 Warning: Although I tried to make this example as simple as possible, it's doubtful that
people who have never used MFC or Visual C++ will be able to follow all the details.

3
BOOL CBC1Doc::OnNewDocument()
{
if (!CDocument::OnNewDocument())
return FALSE;

m_height = 100;
m_width = 150;
m_length = 200;

return TRUE;
}

Initially, the view only displays a side view of the brick. Add the following lines to the
view's OnDraw function. Note that the brick's dimensions are obtained from the
document through the pDoc pointer.
void CBC1View::OnDraw(CDC* pDC)
{
CBC1Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);

pDC->TextOut(10, 10, "Front View");


pDC->Rectangle(100,
100,
100 + pDoc->m_length,
100 + pDoc->m_height);
}

CDC is MFC's device context class. Any "device" that can accept graphical output is
represented by an instance of this class (a window or a printer for example). TextOut and
Rectangle are member functions of CDC. The numbers represent screen coordinates.

Build and test the application. Select "split" from the application's Window menu. What
happens?

Finishing the Document Class

Finish the document by adding member functions that update the brick's dimensions.
Each of these should call UpdateAllViews with a NULL argument. (A view may call
pDoc->UpdateAllViews(this);

indicating the document should update all other views.) These functions also set the
document's inherited m_bModified flag to true. This causes the "Do you want to save
your data?" dialog box to appear when the user quits the application.

4
void CBC1Doc::UpdateLength(int x)
{
m_length = x;
SetModifiedFlag(true);
UpdateAllViews(NULL);
}

void CBC1Doc::updateHeight(int x)
{
m_height = x;
SetModifiedFlag(true);
UpdateAllViews(NULL);
}

void CBC1Doc::UpdateWidth(int x)
{
m_width = x;
SetModifiedFlag(true);
UpdateAllViews(NULL);
}

The Serialize function is called when users select Save of Open from the application's
File menu. Add th following lines to this function:
void CBC1Doc::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
ar << m_height;
ar << m_width;
ar << m_length;
}
else
{
ar >> m_height;
ar >> m_width;
ar >> m_length;
}
}

Adding Top and Side Views

Select New Class from the Insert menu. Create an MFC class called CBC1TopView
derived from CView. Repeat this procedure creating another CView derived class called
CBC1SideView.

Edit the OnDraw functions for each of these new classes. Here's an example:

5
void CBC1SideView::OnDraw(CDC* pDC)
{
CBC1Doc* pDoc = (CBC1Doc*)GetDocument();
pDC->TextOut(10, 10, "Side View");
pDC->Rectangle(100,
100,
100 + pDoc->m_width,
100 + pDoc->m_height);

Notice the cast of GetDocument to CBC1Doc. Unfortunately, the GetDocument()


function of these new view classes only returns a pointer to a generic CView object.
You'll also need to include the BC1Doc.h file at the top of the BC1TopView.cpp and
BC1SideView.cpp files.

Static Splitters

A static splitter is a window that is created in the split state. A dynamic splitter is split by
the user from the Window menu. Replace the body of the child frame's OnCreateClient
function with the following code:
BOOL CChildFrame::OnCreateClient( LPCREATESTRUCT /*lpcs*/,
CCreateContext* pContext)
{
m_wndSplitter.CreateStatic(this, 2, 2);
m_wndSplitter.CreateView(0, 0, RUNTIME_CLASS(CBC1View),
CSize(100, 100), pContext);
m_wndSplitter.CreateView(1, 0, RUNTIME_CLASS(CBC1View),
CSize(100, 100), pContext);
m_wndSplitter.CreateView(0, 1, RUNTIME_CLASS(CBC1TopView),
CSize(100, 100), pContext);
m_wndSplitter.CreateView(1, 1, RUNTIME_CLASS(CBC1SideView),
CSize(100, 100), pContext);

return TRUE;
}

Of course you'll need to include the document and view header files in the ChildFrm.cpp:
#include "BC1Doc.h"
#include "BC1View.h"
#include "BC1TopView.h"
#include "BC1SideView.h"

Build and test the application.

6
The Control Panel View

Select Resource from the Insert menu. This displays the resource dialog box. Expamd the
Dialog node on the tree control and select IDD_FORMVIEW. Push the New button. The
resource editor should start up. Create the following dialog box:

(You can set the labels from the Properties selection on their short cut menus.)

Select Class Wizard from the dialog's short cut menu. It will ask if you want to create a
new class. Agree. Create a class called BC1PanelView derived from CFormView.

The value of an edit control is the value it displays. For each edit control, we want to
create an associated variable in the BC1PanelView class, then use the inherited
UpdateData function to transfer the values between the dialog box and the view class.
Use the Class Wizard "Add Member Variable" tab to do this. Make the value of each edit
control an int (or unsigned int) with range 0 to 1000, and add a corresponding member
variable to the BC1PanelView class:

7
Use the ClassWizard "Message Map" tab to add a handler for the BN_CLICKED
message fired by IDC_BUTTON1:
void CBC1PanelView::OnModify()
{
CBC1Doc* pDoc = (CBC1Doc*) GetDocument();
UpdateData(TRUE); // transfer edit control values to member vars
pDoc->UpdateHeight(m_height);
pDoc->UpdateWidth(m_width);
pDoc->UpdateLength(m_length);

Also use the Class Wizard to add a handler for the OnInitialUpdate message:
void CBC1PanelView::OnInitialUpdate()
{
CFormView::OnInitialUpdate();
CBC1Doc* pDoc = (CBC1Doc*) GetDocument();
m_height = pDoc->m_height;
m_width = pDoc->m_width;
m_length = pDoc->m_length;
UpdateData(FALSE); // transfer member vars to edit control
}

To make control panels understand what CBC1Doc is, be sure to include BC1Doc.h at
the top of BC1PanelView.h.

8
Use the Class Wizard to add message handlers for the OnUpdate message in each of the
other view classes. (No need to do this for the control panel view, because the user keeps
this updated.) All the handler does is call the inherited Invalidate() function, which causes
a call to the OnDraw() function. Here's an example:
void CBC1TopView::OnUpdate(CView* pSender,
LPARAM lHint, CObject* pHint)
{
Invalidate();
}

Finally, reedit the child frame's OnCreateClient function. Be sure to include


BC1PanelView.h at the top of the child frame's source file:
BOOL CChildFrame::OnCreateClient( LPCREATESTRUCT /*lpcs*/,
CCreateContext* pContext)
{
m_wndSplitter.CreateStatic(this, 2, 2);
m_wndSplitter.CreateView(0, 0, RUNTIME_CLASS(CBC1PanelView),
CSize(100, 100), pContext);
m_wndSplitter.CreateView(1, 0, RUNTIME_CLASS(CBC1View),
CSize(100, 100), pContext);
m_wndSplitter.CreateView(0, 1, RUNTIME_CLASS(CBC1TopView),
CSize(100, 100), pContext);
m_wndSplitter.CreateView(1, 1, RUNTIME_CLASS(CBC1SideView),
CSize(100, 100), pContext);

return TRUE;
}

Build and test the application. Here's what mine looks like:

9
Be sure to experiment with opening new views by selecting New from the File menu.
What happens?

What happens when you select "New Window" from the Window menu?

Change the dimensions of a brick. Notice that all the views are updated when the Modify
button is pressed. What happens when you try to close the last open window of a brick
that has been modified?

Save a brick, then try to open it by selecting Open from the File menu. Notice that only
.brk files are displayed. Notice that the last saved modification of the brick is restored.

Problem (Calc)

Calc is an MDI application that allows users to create simple calculator views. Users type
numbers into the Arg1 and Arg2 edit controls of a calculator view, then press an Add or
Mul button to display the sum or product of the numbers in a third Result edit control.

10
The application's document class stores the arguments and result, and provides functions
for adding and multiplying them. For example:
void CCalcDoc::Add(double arg1, double arg2)
{
m_arg1 = arg1;
m_arg2 = arg2;
m_result = m_arg1 + m_arg2;
SetModifiedFlag(true);
UpdateAllViews(NULL);
}

Saving a calculator saves the last values of m_arg1, m_arg2, and m_result to a file with
a .cal extension.

To start, create another MFC App Wizard MDI application called Calc. In the last dialog,
select CFormView as the base class of CCalcView. A dialog resource is automatically
created for your view. Using the dialog editor, add three edit controls labeled Arg1, Arg2,
and Result. Add two buttons labled Add and Mul. Use the Class Wizard to add three
variables of type double to the CCalcView that correspond to the edit controls.

11

Вам также может понравиться