Microsoft 基础类库: 菜单和对话框






3.29/5 (4投票s)
关于 MFC 资源基础知识的文章
引言
本文旨在解释如何有效地使用 MFC 菜单和对话框,因此,假定您具备 MFC 的基本知识。任何从事 MFC 工作的人都知道,“资源”一词经常出现:资源编辑器、资源编译器、资源文件和资源脚本文件。那么,也许一个好的起点是定义一个资源。高级 MFC 开发人员可能会觉得这些内容过于基础,因此应该忽略。事实上,我之所以不经常写 MFC,正是出于这个原因。然而,如果本文能够澄清一些关于 MFC 世界的模糊概念,那么它可能会使那些倾向于在该特定框架上开发应用程序的人受益。通常在 MFC 中,CForm::View
类与用户交互。虽然它在控件方面存在局限性,但它仍然允许将控件拖放到基于文档体系结构而非对话框的用户界面上。如果您要开始一个 MFC 项目来构建 MFC 应用程序,您应该确保选中“MFC 标准”构建样式单选按钮、“单个文档”复选框以及“将 MFC 用作共享 DLL”复选框。继续进行设置,插入,比如,fvd 作为应用程序扩展名,并选择 FormView
作为基类。构建完成后,您将找到一个文档界面,您可以在其中拖放控件。要添加一个用于拖放控件的表单,请转到“资源”选项卡,单击“对话框项”,然后单击 IDD_FORMVIEW_FORM
。拖放两个静态文本控件,并在“标题”属性窗格中,编写 String&1 和 String&2。拖放两个编辑控件框在之前拖放的两个控件旁边,并在它们的 ID 属性窗格中插入:分别是 IDC_STRING1
和 IDC_STRING2
。运行应用程序,并注意您在文档体系结构表面上拥有控件。此应用程序没有任何功能,但旨在展示如何在面向文档的表面上放置控件。请注意,这是在安装了 MFC 2008 功能包的情况下构建的。菜单、对话框和控件等项目被称为资源。

什么是资源
资源是除了代码之外构成您程序一部分的任何内容。此定义不包括您的程序可能处理的数据。最常用的两种资源是菜单和对话框。其他还包括位图和图标,它们可能出现在静态控件或按钮控件的表面或工具栏中。位图不是代码,而是一个位置,因此很容易理解为什么它被称为资源。但为什么菜单被视为资源呢?嗯,也许是因为下面的菜单是基于高度结构化的文本块,看起来是这样的
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
上面的文本代码块是用 Visual Studio 2008 菜单编辑器创建的,稍后将讨论。请看下面的图像

点击“显示对话框”菜单项时,会出现对话框。当第二个对话框关闭后,我们就会看到这个图像

对话框资源
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
上面的文件是资源脚本。显示的两个文本块将被放入不同的文件中。但通常在 MFC 中,所有资源文件都保存在一个名为“资源脚本文件”的文件中。如果您想更深入地了解资源脚本文件,请查看 Resource.h 头文件。文本块描述了资源信息 – 菜单和对话框。如果您查看头文件,您会找到一些标识符,如 IDC_NAME_CLEAR
。所有标识符都必须与一个整数相关联。预处理器定义,如 #defines ID 304
,并在资源文件中维护
//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by MyResScript.rc
//
#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
// Next default values for new objects
//
#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
为了清楚起见,前三个字母让我们了解被标识项的类型
- IDC_NAME “C” 代表控件标识符
- IDD_xx “D” 代表对话框标识符
- ID_xx 没有第三个字母表示菜单标识符
- IDR_xx “R” 代表菜单资源
当使用 Visual Studio 创建菜单和对话框资源时,它会创建并维护 resource.h 文件。您可能需要检查标识符列表,因为您需要在程序语句中使用它们。在容器的“资源视图”窗格中,右键单击 resource_script.rc 文件并选择“查看符号”

菜单在窗口首次创建时与窗口连接。在 CMainFrame
构造函数中,Create()
函数的第 6 个参数 – MAKEINTRESOURCE (IDR_MENU1)
– 建立了连接
// MainFrame.CPP Ex08a_Menu
#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();
}
现在,菜单项需要鼠标点击才能调用一个函数,该函数用于处理“菜单项选择”消息。将事件消息转换为函数调用(调用适当的函数来处理消息)的任务由消息映射处理。消息映射是一个预处理器翻译的宏。如果我们希望我们的程序处理“鼠标左键按下”事件,操作系统将向我们的窗口发送 WM_LBUTTONDOWN
消息。因此,我们在 BEGIN
和 END_MESAGE_MAP()
行之间插入这一行
ON_WM_LBUTTON_DOWN
现在,当我们的程序收到 WM_LBUTTON_DOWN
消息时,MFC 将确保调用
void OnLButtonDown ( UINT nFlags, .. )
函数。那么当用户按下某个字符时会发生什么?好吧,MFC 会收到 WM_CHAR
消息。所以我们将 ON_WM_CHAR()
放在消息映射上。请查看下面的图像。我们在类视图中,注意闪电标记右边的框。您会看到 WM_LBUTTON_DOWN
消息是加亮的。单击下拉箭头会得到函数代码:这是我们放置处理程序代码的地方。这类似于在 Windows 窗体或 ASP.NET 2.0 设计器 UI 上双击控件。

再次说明,点击我们的菜单项也会调用一个函数来处理消息。因此,为了实现这一点,必须在 CMainFrame
消息映射中添加特定的条目。在第一个窗体中,每个菜单项都会调用一个特定的函数。例如,请看以下两个条目
ON_COMMAND (ID_OPTIONA_A1, DoOptionA1)
ON_COMMAND (ID_OPTIONA_A2, DoOptionA2)
当选择菜单项 A-1 时,其标识符为 ID_OPTIONA_A1
,将调用函数 DoOptionA1()
。请注意,到目前为止显示的所有文件中,“Resource.h”和“MyResScript.rc”都是由 Visual Studio 创建的。下载文件并将它们解压缩到一个新创建的目录中。双击项目文件,然后构建解决方案。构建解决方案后,选择“不调试运行”。下面是此基本对话框涉及的所有文件
// MainFrame.h
#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()
};
并且
// MyWinApp.h
#include
class CMyWinApp : public CWinApp
{
public:
virtual BOOL InitInstance();
};
其实现如下
// MyWinApp.cpp
#include "MyWinApp.h"
#include "MainFrame.h"
BOOL CMyWinApp::InitInstance()
{
CMainFrame* pFrame = new CMainFrame;
m_pMainWnd = pFrame; // a CWinApp member variable
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow(); // generates WM_PAINT msg
return TRUE;
}
学生信息应用程序旨在展示如何调用对话框来获取用户输入,然后在屏幕上显示。文档界面和视图的结构与第一个应用程序不同,因为该应用程序只使用了 MFC 的基本原理来构建。将其解压缩到一个新创建的文件夹中,双击项目文件,构建它,然后使用它。请注意,这些项目是使用 MFC “作为静态 DLL”构建的。当您使用“将 MFC 用作静态 DLL”设置构建 MFC 应用程序时,由于您将静态 DLL 构建到应用程序中,因此您的应用程序会比使用 MFC 作为共享 DLL 的应用程序更大。
历史
- 2009 年 7 月 11 日:首次发布