Introduction
The purpose of this article is to explain how to use MFC menus and dialogs effectively and as such, assumes a basic knowledge of MFC. Anyone who works with MFC knows that the word “resource” appears often: resource editor, resource compiler, resource file, and resource script file. So then, maybe a good place to start is by defining a resource. Advanced MFC developers might find this content a little too basic and should therefore overlook it. In fact I do not write about MFC often for that very reason. If, however, this article can clarify a few foggy concepts about the world of MFC, then it could benefit those who prefer to develop applications on that particular framework. Typically in MFC, the CForm::View
class interacts with the user. While it has limitations when it comes to controls, it still allows controls to be dragged and dropped onto a user interface based on document architecture rather than a dialog box. If you start an MFC project to build an MFC application, you should ensure that you check the “MFC Standard” build style radio button, the “Single Document” check box, and the “Use MFC as a Shared DLL” check box. Continue on with the settings, insert, say, fvd for the application extension, and choose FormView
as the base class. Once you build it, you will find a document interface where you can drag and drop controls onto. To add a form to drag the controls onto, go to the resources tab, click the dialog item, and then click the IDD_FORMVIEW_FORM
. Drag and drop two static text controls, and in the “caption” properties pane, write String&1 and String&2. Drag and drop two edit control boxes along side of the two previously dragged controls, and insert in their ID property pane: IDC_STRING1
and IDC_STRING2
, respectively. Run the application, and note that you have controls on a document-architecture surface. This application has no functionality, but is meant to show how to place controls on a document-oriented surface. Note that this was built with the MFC 2008 Feature pack installed. The items like menus, dialogs, and controls, are called resources.
What is a Resource
A resource is anything other than code that is part of your program. This definition does not include data that your program might process. The two most commonly used resources are menus and dialog boxes. Others include bitmaps and icons that may appear on the face of a static or button control or in a toolbar. A bitmap is not code, but a location, so it should be easy to understand why it is called a resource. But why is menu considered to be a resource. Well, maybe the reason is that the menu seen below is based on a highly structured block of text that looks like this:
IDR_MENU1 MENU DISCARDABLE
BEGIN
POPUP “OptionA”
BEGIN
MENUITEM “A-1”, ID_OPTIONA_A1
MENUITEM “A-2”, ID_OPTIONA_A2
END
POPUP “OptionB”
BEGIN
MENUITEM “B-1”, ID_OPTIONB_B1 ID_OPTIONB_B1
MENUITEM “B-2”, ID_OPTIONB_B2 ID_OPTIONB_B2
MENUITEM “B-3”, ID_OPTIONB_B3 ID_OPTIONB_B3
END
MENUITEM “Clear”, ID_CLEAR
END
The above blocks of text code were created with Visual Studio 2008 Menu Editor, which will be discussed shortly. Consider the image below:
The dialog box appear upon the event of clicking the ShowDialog menu item. When that second dialog box is closed, then we have this image:
A Dialog Resource
IDD_DIALOG1 DIALOG DISCARDABLE 0, 0, 146, 47
STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "Enter information"
FONT 8, "MS Sans Serif"
BEGIN
DEFPUSHBUTTON "OK",IDOK,89,7,50,14
PUSHBUTTON "Cancel",IDCANCEL,89,26,50,14
RTEXT "Name",IDC_STATIC,7,7,22,12,SS_CENTERIMAGE
RTEXT "Age",IDC_STATIC,7,27,21,13,SS_CENTERIMAGE
EDITTEXT IDC_NAME,35,7,39,14,ES_AUTOHSCROLL
EDITTEXT IDC_AGE,35,28,23,12,ES_AUTOHSCROLL
END
The above file is a resource script. The two blocks of text shown would be put in separate files. But usually in MFC, all of the resource files are kept in one file called the Resource Script file. If you want to get a better feel for the Resource Script file, then examine the Resource.h header file. The text blocks describe the resource information – the menu and the dialog. If you examine the header file, you will locate some identifiers like IDC_NAME_CLEAR
. All identifiers must be associated with an integer. Preprocessor defines, like #defines ID 304
, and is maintained in the resource file:
#define IDR_MENU1 101
#define ID_OPTIONA_A1 40001
#define ID_OPTIONA_A2 40002
#define ID_OPTIONB_B1 40003
#define ID_OPTIONB_B2 40004
#define ID_OPTIONB_B3 40005
#define ID_CLEAR 40006
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 103
#define _APS_NEXT_COMMAND_VALUE 40007
#define _APS_NEXT_CONTROL_VALUE 1000
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
To clarify, the first three letters give us an idea of the type of item being identified:
- IDC_NAME “C” stands for control identifier
- IDD_xx “D” stands for dialog identifier
- ID_xx no 3rd letter means menu identifier
- IDR_xx “R” stands for menu resource
When Visual Studio is used to create menu and dialog resources, it creates and then maintains the resource.h file. You may have to examine the list of identifiers simply because you have to use them in program statements. In the Resource View pane of the container, right-click the resource_script.rc file and select “View Symbols”:
The menu is connected to the window when the window is first created. In the CMainFrame
constructor, the 6th parameter of the Create()
function -- MAKEINTRESOURCE (IDR_MENU1)
– makes the connection:
#include "MainFrame.h"
#include "resource.h" // note
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
ON_WM_PAINT()
ON_COMMAND (ID_OPTIONA_A1, DoOptionA1)
ON_COMMAND (ID_OPTIONA_A2, DoOptionA2)
ON_COMMAND_RANGE (ID_OPTIONB_B1,
ID_OPTIONB_B3,
DoOptionB)
ON_COMMAND (ID_CLEAR, DoClear)
END_MESSAGE_MAP()
CMainFrame::CMainFrame()
{
Create ( NULL,"Ex08a First Menu",
WS_OVERLAPPEDWINDOW,
CRect(0,0,280,200),
NULL,
MAKEINTRESOURCE (IDR_MENU1)
);
}
void CMainFrame::OnPaint()
{
CPaintDC dc (this);
}
void CMainFrame::DoOptionA1()
{
CClientDC dc(this);
dc.TextOut (10,10,"Option A1");
}
void CMainFrame::DoOptionA2()
{
CClientDC dc(this);
dc.TextOut (10,10,"Option A2");
}
void CMainFrame::DoOptionB(UINT nID)
{
CClientDC dc(this);
switch (nID)
{
case ID_OPTIONB_B1:
dc.TextOut (10,10,"Option B1");
break;
case ID_OPTIONB_B2:
dc.TextOut (10,10,"Option B2");
break;
case ID_OPTIONB_B3:
dc.TextOut (10,10,"Option B3");
break;
default:
break;
}
}
void CMainFrame::DoClear()
{
Invalidate();
}
Now the menu item requires mouse clicking in order to call a function designed to handle the “menu item selection” message. The task of converting an event message into a function call – to the appropriate function to handle the message – is taken care of by the message map. A message map is a macro that the preprocessor translates. If we want our program to handle the “Left button down” event, the OS will send our window the WM_LBUTTONDOWN
message. Thus we put in the line:
ON_WM_LBUTTON_DOWN
between the BEGIN
and END_MESAGE_MAP()
lines. Now when our program receives a WM_LBUTTON_DOWN
message, MFC will make sure that the:
void OnLButtonDown ( UINT nFlags, .. )
function is called. So what happens when a user presses a character? Well, MFC receives the WM_CHAR
message. So we put ON_WM_CHAR()
on the message map. Examine the image below. We are in the class view, and notice the box to the right of the lightning mark. You'll see that WM_LBUTTON_DOWN
message darkened. Clicking the drop down arrow gives us the function code: this is where we place the handler code. It is similar to double-clicking a control on a Window Forms or ASP.NET 2.0 designer UI.
Having said that again, the clicking of our menu item also calls a function designed to handle the message. So for this to happen, specific entries must be put into the CMainFrame
Message map. In the first form, each menu item invokes a specific function. For example, consider the following two entries:
ON_COMMAND (ID_OPTIONA_A1, DoOptionA1)
ON_COMMAND (ID_OPTIONA_A2, DoOptionA2)
When menu item A-1 is selected, which has the identifier ID_OPTIONA_A1
, the function DoOptionA1()
is invoked. Note that of all of the files shown thus far, “Resource.h” and “MyResScript.rc” were created by Visual Studio. Download the files and extract them into a newly made directory. Double-click the project file and then build the solution. When the solution is built, select “run without debugging”. Below are all of the files involved in this basic dialog box:
#include
class CMainFrame : public CFrameWnd
{
private:
public:
CMainFrame();
CMenu menu;
afx_msg void OnPaint ();
afx_msg void DoOptionA1();
afx_msg void DoOptionA2();
afx_msg void DoOptionB(UINT nID);
afx_msg void DoClear();
DECLARE_MESSAGE_MAP()
};
And:
#include
class CMyWinApp : public CWinApp
{
public:
virtual BOOL InitInstance();
};
Its implementation is as follows:
#include "MyWinApp.h"
#include "MainFrame.h"
BOOL CMyWinApp::InitInstance()
{
CMainFrame* pFrame = new CMainFrame;
m_pMainWnd = pFrame;
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
return TRUE;
}
The Student Information application is meant to show how a dialog box can be invoked in order to obtain user input to then have it displayed on the screen. The structure of the document interface and View is different from that first application, because only the basic principles of MFC were used to build that application. Unzip it to a newly-made folder, double-click the project file, build it, and then use it. Note that these projects are build using MFC "As a Static DLL". When you build an MFC application using the setting "Use MFC as a Static DLL", your application becomes larger than that of using MFC as a Shared DLL because of the fact that you are building the static DLL into the application.
History
- 11th July, 2009: Initial post