简介
《MFC Windows程序设计》(第2版)中的示例程序都是通过VC++ 6.0生成的,本文基于原书第4章中的Colors示例程序介绍了在VS2008 SP1环境下结合应用程序生成向导编写该程序的方法。
利用向导生成应用程序框架
从菜单中依次打开文件->新建->项目
,弹出新建项目
对话框,选择MFC应用程序
,并填写项目名称及路径,如下图所示。
点击确定
按钮,进入MFC应用程序向导
对话框,点击下一步
。
在应用程序类型
界面选择单个文档
,并取消文档/视图结构支持
,视觉样式和颜色
选择Windows 本机/默认
,其他选项保持默认即可,点击下一步
。
在数据库支持
界面选择无
,点击下一步
。
在用户界面功能
界面,取消初始状态栏
,并选择使用经典菜单
,其他选项保持默认,点击下一步
。
在高级功能
界面,取消ActiveX 控件
和公共控件清单
,点击下一步
。
在生成的类
界面直接点完成
按钮。
至此,MFC应用程序向导
已经生成一个应用程序框架,点击启动调试
按钮可以直接编译并运行程序,程序界面如下图所示。
基于程序框架进行编程
打开ChildView.h
,在类CChildView
中加入一个COLORREF
类型的数组m_clrColors
用于存储颜色值,并加入两个变量m_nShape
和m_nColor
,分别表示形状和颜色。
// 属性
public:
static const COLORREF m_clrColors[5];
protected:
int m_nShape; // 形状
int m_nColor; // 颜色
打开ChildView.cpp
,为m_clrColors
赋初值,由于是静态成员变量所以需要在类外部定义。
const COLORREF CChildView::m_clrColors[5] = {
RGB(255, 0, 0), // 红色
RGB(255, 255, 0), // 黄色
RGB( 0, 255, 0), // 绿色
RGB( 0, 255, 255), // 青色
RGB( 0, 0, 255) // 蓝色
};
然后在类CChildView
的构造函数中为变量m_nShape
和m_nColor
赋予初始值。
m_nShape = 0; // 圆形
m_nColor = 0; // 红色
接着修改ChildView.cpp
中的OnPaint
函数。
void CChildView::OnPaint()
{
CPaintDC dc(this); // 用于绘制的设备上下文
CPoint points[3];
CRect rcClient;
GetClientRect(&rcClient);
int cx = rcClient.Width() / 2;
int cy = rcClient.Height() / 2;
CRect rcShape(cx - 45, cy - 45, cx + 45, cy + 45);
CBrush brush(m_clrColors[m_nColor]);
CBrush* pOldBrush = dc.SelectObject(&brush);
switch (m_nShape) {
case 0: // 圆形
dc.Ellipse(rcShape);
break;
case 1: // 三角形
points[0].x = cx - 45;
points[0].y = cy + 45;
points[1].x = cx;
points[1].y = cy - 45;
points[2].x = cx + 45;
points[2].y = cy + 45;
dc.Polygon(points, 3);
break;
case 2: // 正方形
dc.Rectangle(rcShape);
break;
}
dc.SelectObject(pOldBrush);
}
接下来需要为程序添加顶层菜单,单击工作空间窗口底部的资源视图
标签,依次展开Colors->Colors.rc->Menu
。双击Menu
下的IDR_MAINFRAME
菜单资源,删除编辑
和帮助
菜单。此时,其相关的子菜单也会被自动删除。然后在文件
菜单右边添加一个形状
菜单,并将下表中的三项添加到形状
菜单中。
菜单项正文 | 命令ID |
---|---|
圆形(&C)\tF7 | ID_SHAPE_CIRCLE |
三角形(&T)\tF8 | ID_SHAPE_TRIANGLE |
正方形(&S)\tF9 | ID_SHAPE_SQUARE |
然后在形状
菜单右边添加一个颜色
菜单,并将下表中的各项添加到颜色
菜单中。
菜单项正文 | 命令ID |
---|---|
红色(&R)\tCtrl+R | ID_COLOR_RED |
黄色(&Y)\tCtrl+Y | ID_COLOR_YELLOW |
绿色(&G)\tCtrl+G | ID_COLOR_GREEN |
青色(&C)\tCtrl+C | ID_COLOR_CYAN |
蓝色(&B)\tCtrl+B | ID_COLOR_BLUE |
下图为添加形状
和颜色
菜单后的菜单资源。
接着在ChildView.cpp
中为形状
菜单和颜色
菜单中的各菜单项添加消息映射及相应的命令处理程序和更新处理程序。由于颜色
菜单使用了RANGE宏,因此需要保证各子菜单项的命令ID是连续的,这可以通过手工编辑resource.h
实现。如果是按照本文所述的步骤编写菜单项,其命令ID是自动连续的。
ON_COMMAND(ID_SHAPE_CIRCLE, OnShapeCircle)
ON_COMMAND(ID_SHAPE_TRIANGLE, OnShapeTriangle)
ON_COMMAND(ID_SHAPE_SQUARE, OnShapeSquare)
ON_UPDATE_COMMAND_UI(ID_SHAPE_CIRCLE, OnUpdateShapeCircle)
ON_UPDATE_COMMAND_UI(ID_SHAPE_TRIANGLE, OnUpdateShapeTriangle)
ON_UPDATE_COMMAND_UI(ID_SHAPE_SQUARE, OnUpdateShapeSquare)
ON_COMMAND_RANGE(ID_COLOR_RED, ID_COLOR_BLUE, OnColor)
ON_UPDATE_COMMAND_UI_RANGE(ID_COLOR_RED, ID_COLOR_BLUE, OnUpdateColor)
void CChildView::OnShapeCircle()
{
m_nShape = 0;
Invalidate();
}
void CChildView::OnShapeTriangle()
{
m_nShape = 1;
Invalidate();
}
void CChildView::OnShapeSquare()
{
m_nShape = 2;
Invalidate();
}
void CChildView::OnUpdateShapeCircle(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(m_nShape == 0);
}
void CChildView::OnUpdateShapeTriangle(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(m_nShape == 1);
}
void CChildView::OnUpdateShapeSquare(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(m_nShape == 2);
}
void CChildView::OnColor(UINT nID)
{
m_nColor = nID - ID_COLOR_RED;
Invalidate();
}
void CChildView::OnUpdateColor(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck((int)pCmdUI->m_nID - ID_COLOR_RED == m_nColor);
}
然后在ChildView.h
中为命令处理函数和更新处理函数添加声明。
protected:
afx_msg void OnShapeCircle();
afx_msg void OnShapeTriangle();
afx_msg void OnShapeSquare();
afx_msg void OnUpdateShapeCircle(CCmdUI* pCmdUI);
afx_msg void OnUpdateShapeTriangle(CCmdUI* pCmdUI);
afx_msg void OnUpdateShapeSquare(CCmdUI* pCmdUI);
afx_msg void OnColor(UINT nID);
afx_msg void OnUpdateColor(CCmdUI* pCmdUI);
接下来为形状
菜单中的各菜单项赋予加速键。再次单击工作空间窗口底部的资源视图
标签,依次展开Colors->Colors.rc->Accelerator
。双击Accelerator
下的IDR_MAINFRAME
加速键资源,可以看到右边编辑窗口中已经包含了多个MFC应用程序向导
预先定义的加速键。可以将这些加速键删除,然后为形状
菜单添加下表中加速键。
快捷键 | 命令ID |
---|---|
F7 | ID_SHAPE_CIRCLE |
F8 | ID_SHAPE_TRIANGLE |
F9 | ID_SHAPE_SQUARE |
然后用同样的方法为颜色
菜单中的各菜单项添加下表中的加速键。
快捷键 | 命令ID |
---|---|
Ctrl + R | ID_COLOR_RED |
Ctrl + Y | ID_COLOR_YELLOW |
Ctrl + G | ID_COLOR_GREEN |
Ctrl + C | ID_COLOR_CYAN |
Ctrl + B | ID_COLOR_BLUE |
此时还需要将颜色
菜单中的各菜单项转化为自制菜单项以便能以图形的方式展示颜色
菜单。首先打开Colors.cpp
,在类CColorsApp
的InitInstance
函数中添加转化菜单项的命令。
BOOL CColorsApp::InitInstance()
{
CWinApp::InitInstance();
// 标准初始化
// 如果未使用这些功能并希望减小
// 最终可执行文件的大小,则应移除下列
// 不需要的特定初始化例程
// 更改用于存储设置的注册表项
// TODO: 应适当修改该字符串,
// 例如修改为公司或组织名
SetRegistryKey(_T("应用程序向导生成的本地应用程序"));
// 若要创建主窗口,此代码将创建新的框架窗口
// 对象,然后将其设置为应用程序的主窗口对象
CMainFrame* pFrame = new CMainFrame;
if (!pFrame)
return FALSE;
m_pMainWnd = pFrame;
// 创建并加载框架及其资源
pFrame->LoadFrame(IDR_MAINFRAME,
WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE, NULL,
NULL);
// 将颜色菜单中的菜单项改为自制菜单项
CMenu* pMenu = pFrame->GetMenu();
ASSERT(pMenu != NULL);
for (int i = 0; i < 5; ++i)
pMenu->ModifyMenu(ID_COLOR_RED + i, MF_OWNERDRAW, ID_COLOR_RED + i);
// 唯一的一个窗口已初始化,因此显示它并对其进行更新
pFrame->ShowWindow(SW_SHOW);
pFrame->UpdateWindow();
// 仅当具有后缀时才调用 DragAcceptFiles
// 在 SDI 应用程序中,这应在 ProcessShellCommand 之后发生
return TRUE;
}
然后打开MainFrm.cpp
,为WM_MEASUREITEM和WM_DRAWITEM消息添加消息映射及相应的命令处理程序和更新处理程序。
ON_WM_MEASUREITEM()
ON_WM_DRAWITEM()
void CMainFrame::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
{
lpMeasureItemStruct->itemWidth = ::GetSystemMetrics(SM_CYMENU) * 4;
lpMeasureItemStruct->itemHeight = ::GetSystemMetrics(SM_CYMENU);
}
void CMainFrame::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
{
BITMAP bm;
CBitmap bitmap;
bitmap.LoadOEMBitmap(OBM_CHECK);
bitmap.GetObject(sizeof(bm), &bm);
CDC dc;
dc.Attach(lpDrawItemStruct->hDC);
CBrush* pBrush = new CBrush(::GetSysColor((lpDrawItemStruct->itemState &
ODS_SELECTED) ? COLOR_HIGHLIGHT : COLOR_MENU));
dc.FrameRect(&(lpDrawItemStruct->rcItem), pBrush);
delete pBrush;
if (lpDrawItemStruct->itemState & ODS_CHECKED) {
CDC dcMem;
dcMem.CreateCompatibleDC(&dc);
CBitmap* pOldBitmap = dcMem.SelectObject(&bitmap);
dc.BitBlt(lpDrawItemStruct->rcItem.left + 4, lpDrawItemStruct->rcItem.top +
(((lpDrawItemStruct->rcItem.bottom - lpDrawItemStruct->rcItem.top) -
bm.bmHeight) / 2), bm.bmWidth, bm.bmHeight, &dcMem, 0, 0, SRCCOPY);
dcMem.SelectObject(pOldBitmap);
}
UINT itemID = lpDrawItemStruct->itemID & 0xFFFF;
pBrush = new CBrush(m_wndView.m_clrColors[itemID - ID_COLOR_RED]);
CRect rect = lpDrawItemStruct->rcItem;
rect.DeflateRect(6, 4);
rect.left += bm.bmWidth;
dc.FillRect(rect, pBrush);
delete pBrush;
dc.Detach();
}
同时在MainFrm.h
中为命令处理函数和更新处理函数添加声明。
protected:
afx_msg void OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct);
afx_msg void OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct);
由于OnDrawItem
函数使用了Windows预定义的位图资源,因此程序需要在包含afxwin.h
头文件之前添加以下宏定义语句。
#ifndef OEMRESOURCE
#define OEMRESOURCE
#endif
Colors应用程序中,该定义在stdafx.h
中。完成顶层菜单的制作后,还需要添加上下文菜单。单击工作空间窗口底部的资源视图
标签,依次展开Colors->Colors.rc->Menu
。右键点击Menu
,选择添加资源
,打开添加资源
对话框,如下图所示。
在左边的资源类型
中选择Menu
,点击新建
按钮。在资源视图的Menu
节点下会出现一个名为IDR_MENU1
的菜单资源。将其重命名为IDR_CONTEXTMENU
以表示这是一个上下文菜单资源。与顶层菜单类似,在上下文菜单资源中添加形状
和颜色
菜单中的各菜单项,如下图所示。
然后打开ChildView.cpp
,为WM_CONTEXTMENU消息添加消息映射及相应的命令处理程序。
ON_WM_CONTEXTMENU()
void CChildView::OnContextMenu(CWnd* pWnd, CPoint pos)
{
CRect rcClient;
GetClientRect(&rcClient);
int cx = rcClient.Width() / 2;
int cy = rcClient.Height() / 2;
CRect rcShape(cx - 45, cy - 45, cx + 45, cy + 45);
CPoint point = pos;
ScreenToClient(&point);
CPoint points[3];
BOOL bShapeClicked = FALSE;
int dx, dy;
switch (m_nShape) {
case 0: // 圆形
dx = point.x - cx;
dy = point.y - cy;
if ((dx * dx) + (dy * dy) <= (45 * 45))
bShapeClicked = TRUE;
break;
case 1: // 三角形
if (rcShape.PtInRect(point)) {
dx = min(point.x - rcShape.left, rcShape.right - point.x);
if ((rcShape.bottom - point.y) < (2 * dx))
bShapeClicked = TRUE;
}
break;
case 2: // 正方形
if (rcShape.PtInRect(point))
bShapeClicked = TRUE;
break;
}
if (bShapeClicked) {
CMenu menu;
menu.LoadMenu(IDR_CONTEXTMENU);
CMenu* pContextMenu = menu.GetSubMenu(0);
for (int i = 0; i < 5; ++i)
pContextMenu->ModifyMenu(ID_COLOR_RED + i,
MF_BYCOMMAND | MF_OWNERDRAW, ID_COLOR_RED + i);
pContextMenu->TrackPopupMenu(TPM_LEFTALIGN | TPM_LEFTBUTTON |
TPM_RIGHTBUTTON, pos.x, pos.y, AfxGetMainWnd());
return;
}
CWnd::OnContextMenu(pWnd, pos);
}
并在ChildView.h
中为OnContextMenu
函数添加声明。
protected:
afx_msg void OnContextMenu(CWnd* pWnd, CPoint pos);
至此,Colors应用程序就编写完成了,点击启动调试
运行程序吧!