1
第 10章 在 MFC中创建应
用程序的资源
2
在 Windows的可执行文件中,资源是
独立于代码的,使用单独的 Resource
Compiler进行编译,并嵌入到可执行文
件中。
在编程过程中,代码是可复用的,资
源也是可复用的,通过资源的, 导入,
和, 导出, 功能来实现资源的可复用。
程序的国际化,也是通过资源来实现
的。
3
10.1获取资源的一个样例
查看 Windows( 98/2000) 系统中自带的纸
牌游戏中的图片资源:
c:\Windows\cards.dll 或 c:\WINNT\System32
4
10.2 资源的应用
5
10.2.1 菜单资源的使用
创建一
个“计
算”菜

6
【 例 10-1】 创建一个基于单文档结构的应用程
序,在视图中显示一行字符串, Hello
World!”,通过建立包含, 显示, 和, 颜色选
择, 两个菜单项的, 操作, 菜单来控制字符
串,菜单项, 显示, 用以控制字符串的显示
与否,菜单项, 颜色选择, 中包含一个级连
菜单,内容为, 红,,, 绿, 和, 蓝, 三个
菜单项。
Hello World!
ID_OPER_RED
ID_OPER_GREEN
ID_OPER_BLUE
ID_OPER_SHOW
7
在 My_ResView.h中的
class CMy_ResView, public Cview
中的 public处加入如下声明:
COLORREF m_nColors[3]; //用户可选颜色数组
DWORD m_nColorIndex; // 当前所选颜色索引
CString m_strShow; // 显示的内容
BOOL m_bShow; // 是否显示
8
在 CMy_ResView::CMy_ResView()中初始化成员变量:
m_nColors[0] = RGB(255,0,0);
m_nColors[1] = RGB(0,255,0);
m_nColors[2] = RGB(0,0,255);
m_nColorIndex = 0;
m_strShow = "Hello World!";
m_bShow = TRUE;
在 void CMy_ResView::OnDraw(CDC* pDC)
中加入如下代码绘制字符串:
if(m_bShow)
{ pDC->SetTextColor(m_nColors[m_nColorIndex]);
// 设置输出字符串颜色
pDC->TextOut(100,100,m_strShow); // 输出字符串
}
9
若编译运行程序, 可看到程序输出一行红色
的字符串, 但颜色设置菜单项还没有起作用
下面将介绍如何通过菜单项来控制程序,
在介绍菜单项的响应时, 必须先了解几个消
息响应机制:
? COMMAND消息的响应
? UPDATE_COMMAND_UI消息的响应
? ON_COMMAND_RANGE对 COMMAND消息的响应
? ON_UPDATE_COMMAND_UI_RANGE 对 UPDATE_COMMAND_UI
消息的响应
10
(1) COMMAND消息的响应
添加了对 COMMAND消息的响应之后,My_ResView.h发
生如下变化:
// Generated message map functions
protected:
//{{AFX_MSG(CMy_ResView)
afx_msg void OnOperShow();
//}}AFX_MSG
11
在 My_ResView.cpp文件中,读者会看到 ID_OPER_SHOW
对应的 COMMAND消息的绑定,代码如下,
BEGIN_MESSAGE_MAP(CMy_ResView,CView)
//{{AFX_MSG_MAP(CMy_ResView)
ON_COMMAND(ID_OPER_SHOW,OnOperShow)
//}}AFX_MSG_MAP
// Standard printing commands
ON_COMMAND(ID_FILE_PRINT,CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_DIRECT,CView::OnFilePrint)
ON_COMMAND(ID_FILE_PRINT_PREVIEW,
CView::OnFilePrintPreview)
END_MESSAGE_MAP()
在 My_ResView.cpp文件的最后加入如下代码:
void CMy_ResView::OnOperShow()
{m_bShow = !m_bShow;
Invalidate(); // 强制程序重新窗口
}
重新编译运行程
序,可看到“显
示”菜单项工作
正常
12
(2) UPDATE_COMMAND_UI消息的响应
UPDATE_COMMAND_UI消息是在窗口将要绘制菜单项的时
候产生,上例中,仅仅只是使用“显示”菜单项来控制是否显
示似乎还不够,如果“显示”菜单项能够配合主程序体现出当
前是否显示的状态可能会更好一些。就像一个文本编辑软件,
菜单上是,10号字”、,12号字”的功能,如果不在菜单上标
识出来,那么使用者可能就搞不清当前的字是多大的。
为 ID_OPER_SHOW添加 UPDATE_COMMAND_UI消息。在自动生
成消息处理函数中加入如下代码:
void CMy_ResView::OnUpdateOperShow(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(m_bShow);
}
此时可看到随着 m_bShow的值的改变,显示菜单项的状
态与实际是否显示字符串的状态一致了,通过菜单项前面的
,√”标记来体现。
13
void Enable( BOOL bOn = TRUE )
禁止或者允许该菜单项
void SetCheck( int nCheck = 1 )
设置菜单项 /工具条按钮的 check状态,显示标志为,√”
void SetRadio( BOOL bOn = TRUE )
与 SetCheck功能类似,显示标志为,·”
void SetText( LPCTSTR lpszText )
设置菜单项的 Caption属性 CCmdUI类常用的方法
14
(3) ON_COMMAND_RANGE对 COMMAND消息的响应
ON_COMMAND_RANGE为处理具有连续 Object ID的菜
单项提供了方便的途径。
前面只响应了三种颜色操作,若有 100种颜色可供
选择,是否逐个定义其响应函数?显然工作量很大,
我们可以使用 ON_COMMAND_RANGE。
这涉及到 ID范围的上下界及当前的 ID,若 Resource.h
中若干个 ID不连续,要 手工 修改为连续的。
由于 ClassWizard不支持 ON_COMMAND_RANGE消
息的自动映射,只能手工添加消息的处理。
在 My_ResView.h中声明消息的处理函数
//{{AFX_MSG(CMy_ResView)
afx_msg void OnOperShow();
afx_msg void OnUpdateOperShow(CCmdUI* pCmdUI);
afx_msg void OnOperColorChange(WORD nID);
//}}AFX_MSG 所处理的菜单项的 ID。
15
在 My_ResView.cpp的开头部分加入如下斜体标识的代码,
完成消息映射
BEGIN_MESSAGE_MAP(CMy_ResView,CView)
//{{……
ON_COMMAND_RANGE(
ID_OPER_RED,// ID范围的最小值
ID_OPER_BLUE,// ID范围的最大值
OnOperColorChange) //消息处理函数
END_MESSAGE_MAP()
在 My_ResView.cpp的最后加入消息处理函数:
void CMy_ResView::OnOperColorChange(WORD nID)
{ m_nColorIndex = nID-ID_OPER_RED;
Invalidate(); }
运行程序,可以通过菜单项来改变颜色了
16
(4) ON_UPDATE_COMMAND_UI_RANGE
ON_UPDATE_COMMAND_UI_RANGE与 ON_UPDATE_COMMAND_UI
的关系类似和 ON_COMMAND_RANGE与 ON_COMMAND的关系
下面仿照手工加入 ON_COMMAND_RANGE过程加
入 ON_UPDATE_COMMAND_UI_RANGE宏。
在 My_ResView.h中加入如下代码:
afx_msg void OnUpdateOperColorChange(CCmdUI * pCmdUI);
17
在 My_ResView.cpp中加入如下代码:
ON_UPDATE_COMMAND_UI_RANGE(ID_OPER_RED,ID_OP
ER_BLUE,OnUpdateOperColorChange)

void CMy_ResView::OnUpdateOperColorChange(CCmdUI * pCmdUI)
{
pCmdUI->SetRadio(m_nColorIndex==
(pCmdUI->m_nID - ID_OPER_RED));
}
由于 CCmdUI类的成员 m_nID就是调用
OnUpdateOperColorChange时当前的菜单项 ID,因此
OnUpdateOperColorChange函数没有 nID这个参数
18
10.2.2快捷菜单的创建及其应用
【 例 10-2】 在 【 例 10-1】 的基础上增加快捷菜
单,实现, 操作, 菜单的功能
1,创建菜单资源,在 ResourceView菜单中右击 Menu,
选择 Insert Menu,资源命名为 IDR_MENU_POP
19
在 My_ResView.h中声明快捷菜单中对应的变量。
CMenu m_PopMenu; // Pop-up快捷菜单
CMenu* m_pPop; // Pop-up快捷子菜单
……
afx_msg void OnRButtonDown(UINT nFlags,CPoint point);
在 My_ResView.cpp中添加如下代码:
ON_COMMAND(ID_POP_SHOW,OnOperShow) //消息响应

CMy_ResView::CMy_ResView()
{

m_PopMenu.LoadMenu(IDR_MENU_VIEW);
// 创建并加载菜单资源
}
20
CMy_ResView::~CMy_ResView()
{
m_PopMenu.DestroyMenu(); // 释放菜单资源
}
void CMy_ResView::OnRButtonDown(UINT nFlags,CPoint point)
{m_pPop=m_PopMenu.GetSubMenu(0); //获得第一个子菜单
UINT nCheck = m_bShow?MF_CHECKED:MF_UNCHECKED;
// 更新 【 Show】 的 check状态
m_pPop->CheckMenuItem(ID_POP_SHOW,MF_BYCOMMAND|nCheck);
ClientToScreen(&point); //将坐标由客户坐标转化为屏幕坐标
m_pPop->TrackPopupMenu(TPM_LEFTALIGN,point.x,point.y,this);
// 显示 Pop-up菜单
CView::OnRButtonDown(nFlags,point);
}
21
10.2.3加速键资源的创建及其使用
【 例 10-3】 在 【 例 10-2】 的基础上添加 Ctrl+W来触
发“显示”菜单项的功能。
打开 ResourceView?Accelerator?IDR_MAINFRAME,会看到一张
加速键列表,在列表的最后高亮区域双击,会弹出 Accel
Properties对话框。 ID选择 ID_OPER_SHOW(或直接填写)。然
后单击 Next Key Typed按钮,会弹出一个对话框,提示按下对
应的加速键,这里按下 Ctrl+W,则 VC IDE会自动为你设置好 Key、
Modifiers和 Type三个属性。
22
10.2.4工具条资源的创建及其使用
在 Windows应用程序中,工具条可以看作是
图形化的菜单,是一种更快捷、更有效、更
直观的人机交互方式。
1 工具条类的层次位置及其常用方法 工具条由 CWnd类
派生的, 它们都连
接到一个 Windows
应用程序窗口 。 因
此, CWnd的所有
功能如创建, 移动,
显示和隐藏窗口等
在用控制条工作时
都是可用的 。
23
Create():创建一个工具条并把它附加到 CToolBar对象上
CreateEx():创建一个定义了边界的工具条并附加到
CToolBar对象上
SetSizes():设置按钮及位图大小
SetHeight():设置工具条的高度
LoadToolBar():装载工具条资源
LoadBitmap():装载包含工具按钮图像的位图
SetBitmap():设置位图图像
SetButtons():设置按钮并使每个按钮与位图图像相关
CToolBar的构造方法
24
(1)增加工具条资源
Insert ? Resource ?Toolbar ? New插入新的工
具条资源。
(2)将工具条添加到窗口中
添加后, 需把工具条的对象加入到应用程序框架
窗口中 (CMainFrame) 。 在 应 用 程 序 的
CMainFrame类中加入工具条对象 m_wndToolBar
protected:
CToolBar m_wndToolBar; //自己定义的工具条
2 加入用户自定义的工具条
25
在框架窗口类的 OnCreate()函数中调用工具条类
的 Create()或 CreateEx()成员函数创建该工具条,
并调用 LoadToolBar()成员函数将工具条对象和
前面创建的工具条资源连接在一起 。
if(!m_wndToolBar.Create(this, WS_VISIBLE|CBRS_TOP)
||!m_wndToolBar.LoadToolBar(IDR_TOOLBAR))
//引入资源 IDR_TOOLBAR
{
TRACE0("Failed to create toolbar\n");
return -1; // fail to create
}
26
3 对工具条进行操作
创建完成工具条后,可调用工具条类中的成员函
数对工具条进行操作,例如设定工具条风格,在窗口
中移动工具条,控制工具条的显隐等。
当鼠标光标在按钮上暂停时,显示工具提示和命
令描述,并设定工具条的大小是可变的。
m_wndToolBar.SetBarStyle(CBRS_TOOLTIPS
| CBRS_FLYBY | CBRS_SIZE_DYNAMIC);
可在程序中设置允许用户在程序运行中在框架窗口内
移动工具条。这是通过调用 CToolBar::EnableDocking和
CFrame::EnableDocking来实现的。二函数原型均如下:
void EnableDocking( DWORD dwStyle )
27
【 例 10-4】 在 【 例 10-3】 中添加工具条,工具条中包含
四个按钮,分别对应菜单的“显示”、“红色”、“绿
色”和“蓝色”菜单项。该工具条可以在窗口中任意位
置停靠,当鼠标停留在工具条按钮上时,将显示该按钮
的功能。
28
在 ResourceView中 加入工具条资源 IDR_TOOLBAR_NEW
绘制四个按钮并设置相应 ID。
在 MainFrm.h中添加如下代码, 声明一个 CToolBar变量
CToolBar m_wndToolBarNew;
在 MainFrm.cpp文件的
Int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
函数中添加如下代码:
if (!m_wndToolBarNew.CreateEx(this,TBSTYLE_FLAT,
WS_CHILD | WS_VISIBLE | CBRS_TOP |
CBRS_GRIPPER | CBRS_TOOLTIPS |
CBRS_FLYBY | CBRS_SIZE_DYNAMIC) ||
!m_wndToolBarNew.LoadToolBar(IDR_TOOLBAR_NEW))
{ TRACE0("Failed to create toolbar\n");
return -1; // fail to create }
29
为了使新增的工具条可以在窗口中自由停靠,在
OnCreate函数中,还要增加如下代码:
m_wndToolBarNew.EnableDocking(CBRS_ALIGN_ANY);
//工具条可以在父窗口内任何一边停靠
EnableDocking(CBRS_ALIGN_ANY);
// 父窗口允许子工具条窗口在任何一边停靠
DockControlBar(&m_wndToolBarNew);
// 父窗口内按照前面指定的风格停靠该工具条
30
10.2.5 图标资源的创建及其使用
每个 Windows应用程序在资源管理器中都有
自己的图标, 这个图标就是 ICON资源 。
【 例 10-5】 在 【 例 10-4】 的基础上通过修改光标
资源,使得执行程序的图标变为如图所示的
样子。
31
ResourceView?Icon?IDR_MAINFRAME,就会看到
图标编辑器,在这里可以选择图标的尺寸,默认的是
16× 16和 32× 32。
16× 16的图标用于程序运行时左上角图标、任务条图
标、资源管理器的列表和详细信息模式;
32× 32的图标用于程序运行时默认对话框图标、资源
管理器图标模式;
48× 48的图标用于资源管理器的平铺和缩略图模式。
32
单击 New Device Image按钮对光标资源进行修改
33
10.2.6 字符串资源的使用
字符串资源最主要的用途就是用于程序的多语
言版本 。 如果要想动态切换界面语言, 使用字符串
资源是很好的选择 。
在 MFC中, 可以通过 CString类的 LoadString方法
来从资源载入字符串 。
具体操作是打开 ResourceView?String Table,在表
中的空白高亮处双击, 在弹出的 String Properties对
话框中的 ID编辑框中输入 IDS_STRING_HELLO,
Caption编辑框中输入, Hello VC!”。
34
在 My_ResView.cpp文件的构造函数中, 将原来的
m_strShow = "Hello World!";
改为:
m_strShow.LoadString(IDS_STRING_HELLO);
这样我们的程序的输出就变为, HelloVC!”了
使用字符串资源的好处就是不需
要在整个程序中去寻找某个字符
串, 如果某些字符串可能在将来
会发生变更, 那么最好将它写在
字符串资源中 。
35
10.2.7对话框资源的创建及其应用
【 例 10-6】 在上例的基础上编写一个对话框用
于接收用户输入,然后用这个输入来替换主
程序原来显示的字符串
36
在创建完对话框资源之后,需要生成一个相关的对话
框类。 View?ClassWizard ?Create a New Class ?OK,
在 Name中填入,CInputDlg”即可。
37
将对话框上的 IDC_EDIT_INPUT控件与一个 CString类型的
m_strInput变量绑定,建立一种映射关系。
上面的操作使 MFC在幕后作了些什么?
在 InputDlg.h文件中,MFC加入了如下代码:
// Dialog Data
//{{AFX_DATA(CInputDlg)
enum { IDD = IDD_DIALOG_NEW };
CString m_strInput;
//}}AFX_DATA
38
在 InputDlg.cpp的构造函数中,MFC加入了如下代码:
//{{AFX_DATA_INIT(CInputDlg)
m_strInput = _T("");
//}}AFX_DATA_INIT
在 InputDlg.cpp文件的 DoDataExchange函数中,MFC加
入了如下代码:
//{{AFX_DATA_MAP(CInputDlg)
DDX_Text(pDX,IDC_EDIT_INPUT,m_strInput);
//}}AFX_DATA_MAP
在函数 DDX_Text调用中,完成了控件与变量之间
的数据交换。
对 m_strInput
的初始化
39
下面要在 CMy_ResView中使用新创建的对话框
首先为“操作”菜单增加菜单项“修改字符串”,其
ID为 ID_OPER_STRING。增加 COMMAND消息响应函数
OnOperString。然后在 My_ResView.cpp文件头部 include
部分最后加入:
#include "InputDlg.h"
在 OnOperString中加入如下代码:
void CMy_ResView::OnOperString()
{ CInputDlg dlgInput; // 声明对话框变量
if(dlgInput.DoModal() == IDOK) // 如果用户点击 OK按钮
{ m_strShow = dlgInput.m_strInput; // 更改字符串
Invalidate(); // 强制重绘
} }
40
10.2.8位图资源的创建及其应用
标准控件比较单调, 若能通过一些精美的
图片来点缀, 就活泼了, 这个问题, 可以选择
位图资源来实现 。
位图是一种数字化的图形表示形式,基本
数据结构是象素,一个像素表示一个离散点的
颜色值。
常见位图有 2色,4色,16色,256色,16
位,24位。其中 VC 6的资源编辑器只支持 256
色以下(包括 256色)的位图的编辑,而最新
的 VC7已经支持 24位真彩位图的编辑了。保存
在文件中的位图可以看作是设备无关的,文件
本身的数据用来描述位图的内容。
41
【 例 10-7】 在 【 例 10-6】 的基础上显示两幅图片,
一幅是 256色,另一幅是 24位真彩,两幅图片都
是通过资源来显示
42
在 CMy_ResView.cpp的 OnDraw函数中加入如下代码:
CDC dcMemory; // 创建内存缓冲 DC
dcMemory.CreateCompatibleDC(pDC);
CBitmap bmp1; // 加载 256位图
bmp1.LoadBitmap(IDB_BITMAP_256);
BITMAP bmpInfo1;
bmp1.GetBitmap(&bmpInfo1); // 获得位图的尺寸
CBitmap* pOldBitmap = dcMemory.SelectObject(&bmp1);
// 选择位图到内存缓冲设备中
pDC->BitBlt(200,10,bmpInfo1.bmWidth,bmpInfo1.bmHeight,
&dcMemory,0,0,SRCCOPY); // 绘制到屏幕
43
CBitmap bmp2;
bmp2.LoadBitmap(IDB_BITMAP_24bit); // 加载 24位位图
BITMAP bmpInfo2;
bmp2.GetBitmap(&bmpInfo2);
dcMemory.SelectObject(&bmp2);
pDC->BitBlt(400,10,bmpInfo2.bmWidth,
bmpInfo2.bmHeight,&dcMemory,0,0,SRCCOPY);
dcMemory.SelectObject(pOldBitmap);
// 恢复设备中原来的位图