VS2008下编写Colors应用程序

简介

  《MFC Windows程序设计》(第2版)中的示例程序都是通过VC++ 6.0生成的,本文基于原书第4章中的Colors示例程序介绍了在VS2008 SP1环境下结合应用程序生成向导编写该程序的方法。

利用向导生成应用程序框架

  从菜单中依次打开文件->新建->项目,弹出新建项目对话框,选择MFC应用程序,并填写项目名称及路径,如下图所示。
新建项目对话框
  点击确定按钮,进入MFC应用程序向导对话框,点击下一步
MFC应用程序向导
  在应用程序类型界面选择单个文档,并取消文档/视图结构支持视觉样式和颜色选择Windows 本机/默认,其他选项保持默认即可,点击下一步
应用程序类型
  在数据库支持界面选择,点击下一步
数据库支持
  在用户界面功能界面,取消初始状态栏,并选择使用经典菜单,其他选项保持默认,点击下一步
用户界面功能
  在高级功能界面,取消ActiveX 控件公共控件清单,点击下一步
高级功能
  在生成的类界面直接点完成按钮。
这里写图片描述
  至此,MFC应用程序向导已经生成一个应用程序框架,点击启动调试按钮可以直接编译并运行程序,程序界面如下图所示。
应用程序框架

基于程序框架进行编程

  打开ChildView.h,在类CChildView中加入一个COLORREF类型的数组m_clrColors用于存储颜色值,并加入两个变量m_nShapem_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_nShapem_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)\tF7ID_SHAPE_CIRCLE
三角形(&T)\tF8ID_SHAPE_TRIANGLE
正方形(&S)\tF9ID_SHAPE_SQUARE

  然后在形状菜单右边添加一个颜色菜单,并将下表中的各项添加到颜色菜单中。

菜单项正文命令ID
红色(&R)\tCtrl+RID_COLOR_RED
黄色(&Y)\tCtrl+YID_COLOR_YELLOW
绿色(&G)\tCtrl+GID_COLOR_GREEN
青色(&C)\tCtrl+CID_COLOR_CYAN
蓝色(&B)\tCtrl+BID_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
F7ID_SHAPE_CIRCLE
F8ID_SHAPE_TRIANGLE
F9ID_SHAPE_SQUARE

  然后用同样的方法为颜色菜单中的各菜单项添加下表中的加速键。

快捷键命令ID
Ctrl + RID_COLOR_RED
Ctrl + YID_COLOR_YELLOW
Ctrl + GID_COLOR_GREEN
Ctrl + CID_COLOR_CYAN
Ctrl + BID_COLOR_BLUE

  此时还需要将颜色菜单中的各菜单项转化为自制菜单项以便能以图形的方式展示颜色菜单。首先打开Colors.cpp,在类CColorsAppInitInstance函数中添加转化菜单项的命令。

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应用程序就编写完成了,点击启动调试运行程序吧!
Colors应用程序

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值