自绘控件问题多多。本文以菜单为例。

①当要使用顶层菜单资源、对话框资源、状态栏资源等这3种资源的任何一种。那么CWinApp::InitInstance函数内部必须使用LoadFrame函数来加载资源。比如以下代码:
BOOL CMyApp::InitInstance()
{
  CMainWindow* pFrame = new CMainWindow;
  m_pMainWnd = pFrame;

  pFrame->LoadFrame(IDR_MAINFRAME);
  pFrame->ShowWindow(m_nCmdShow);
  pFrame->UpdateWindow();

  return TRUE;
}

以下代码适合使用框架窗口但不使用以上三种资源时候使用。
BOOL CMyApp::InitInstance()
{
  m_pMainWnd = new CMainWindow;
  m_pMainWnd->ShowWindow(m_nCmdShow);
  m_pMainWnd->UpdateWindow();

  return TRUE;
}

如果第2种代码被使用在顶层菜单资源、对话框资源、状态栏资源等这3种资源的任何一种,那么可能会有不可预知的错误发生。

②在OnCreate函数里面应该使用以下代码:

int CMainWindow::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
  if(CFrameWnd::OnCreate(lpCreateStruct) == -1)
    return -1;

  m_wndToolBar.Create(this);
  m_wndToolBar.LoadToolBar(IDR_MAINFRAME);

  UINT nIndicator = ID_SEPARATOR;
  m_wndStatusBar.Create(this);
  m_wndStatusBar.SetIndicators(&nIndicator,1);

  CMenu* pMenu = GetMenu()->GetSubMenu(2);
  pMenu->ModifyMenu(ID_VIEW_STATUS_BAR,MF_OWNERDRAW,ID_VIEW_STATUS_BAR);
  return 0;
}

以上代码里面没有加载菜单的语句,这是在CFrameWnd::LoadFrame函数里面已经被MFC自动加载了。但是工具栏却没有,必须使用CToolBar::LoadToolBar函数加载。状态栏无需加载。

③自绘控件的关键在于CFrameWnd::OnMeasureItem函数和CFrameWnd::OnDrawItem函数。必须在CFrameWnd的派生类的消息映射项里面使用ON_WM_MEASUREITEM()和ON_WM_DRAWITEM()才能使用这2个函数。同时也头文件必须在类声明的最后使用以下代码:
afx_msg void OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpdis);
afx_msg void OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpmis);

OnMeasureItem函数比较简单。在每个自绘菜单项第1次显示之前被框架调用,以确定大小。OnDrawItem函数在每个自绘菜单项第2次以及更多次显示之前被调用。目的在于根据第2个参数lpmis具体绘制菜单项内容。在函数中,一般根据以下代码原型绘制菜单。
void CMainWindow::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpdis)
{
  if(lpdis->CtlType == ODT_MENU)
  {
    //别的代码
    CDC dc;
    dc.Attach(lpdis->hDC);
    //dc对象的绘制函数
    dc.Detach();
    //别的代码
  }
}

lpdis指向的结构如下:

typedef struct tagDRAWITEMSTRUCT {
    UINT    CtlType;
    UINT    CtlID;
    UINT    itemID;
    UINT    itemAction;
    UINT    itemState;
    HWND   hwndItem;
    HDC     hDC;
    RECT    rcItem;
    DWORD  itemData;
} DRAWITEMSTRUCT;

CtlType成员可以参考MSDN里面的资料。总共有7种自绘控件类型。
CtlID成员和itemData成员跟菜单无关。
itemID成员就是将要绘制的具体菜单项的ID。
itemAction成员可以是ODA_DRAWENTIRE、ODA_FOCUS、ODA_SELECT的任意组合。ODA_DRAWENTIRE的意思是画出整个菜单项。ODA_FOCUS表示焦点有变化。失去或者获得焦点。ODA_SELECT表示当前菜单项加亮显示的情况。
当itemAction包含ODA_FOCUS且itemState 包含ODS_FOCUS,那么表示获得焦点。
当itemAction包含ODA_FOCUS但itemState 不含ODS_FOCUS,那么表示失去焦点。
当itemAction包含ODA_SELECT且itemState 包含ODS_SELECTED,那么表示加量显示。
当itemAction包含ODA_SELECT但itemState 不含ODS_SELECTED,那么表示正常显示。
itemState成员总共有7个标志。具体参考MSDN里面的资料。
hDC是菜单项所在的DC句柄。
rcItem是限制的可被绘画的矩形。

以上OnMeasureItem函数和OnDrawItem函数的第1个参数int nIDCtl跟自绘菜单无关。只跟非菜单自绘控件有关,是它们的控件ID。

以上信息可以从可以参考《MFC Windows程序设计》的第2版(修订版)里面的第200页。

④自绘控件不是以上内容那么简单,里面有很多不该有的bug。根据提供的代码绘制出下图:

上图是代码运行后得到的结果图。里面告诉你很多MFC本身的bug。

①顶层菜单里面的“查看”下面的下拉菜单第2个是自绘菜单。绘图时候覆盖了上面的“工具栏”的菜单项。这明显是错误的。
②菜单项的北京颜色默认是白色,但是画出来却是黑色。这也是错误的。这个错误不是固定的,只要把光标移动到自绘菜单项,背景就会变成白色的。再移动到“工具栏”项。背景还是白色的,是正确的。
③从下图可知,Top:18是指“工具栏”下面的那像素行所在的垂直坐标。其实应该是把0,0设置在“工具栏”左下角下面的那个像素才是正常的。

④以下代码设置了绘图区域的大小。表示高度的lpmis->itemHeight为100。实际是上图黑色部分的高度(Bottom-Top=100)。不含“工具栏”本身的高度。可见这是正确的。但是宽度就是有问题的。Right-Left=131,而不是lpmis->itemWidth 的100。里面多出来的有15像素是为最前面的“单选”的“黑点”标记或“复选”的“打勾”标记。再来15像素是自定义“图像”。最后1像素是多出来的。

void CMainWindow::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpmis)
{
  lpmis->itemHeight = 100;
  lpmis->itemWidth = 100;
}

⑤::GetSystemMetrics(SM_CYMENU)函数返回值是20,这是不对的。应该是18才对。

MFC自绘菜单的更多相关文章

  1. VC++的菜单控制和自绘菜单

    菜单控制为什么即使调用EnableMenuItem菜单项后,菜单项还处于禁止状态 需要将CFrameWnd:: m_bAutomenuEnable设置为FALSE,如果该数据成员为TRUE(缺省值), ...

  2. 第十二篇 -- 如何向MFC对话框添加菜单

    1.如何在基于对话框的MFC中添加菜单:https://blog.csdn.net/u012273127/article/details/71293088 步骤: 资源文件处右击Add Resourc ...

  3. MFC自绘控件学习总结

    前言:从这学期开始就一直在学习自绘控件(mfc),目标是做出一款播放器界面,主要是为了打好基础,因为我基础实在是很烂....说说我自己心得体会以及自绘控件的方法吧,算是吐槽吧,说的不对和不全的地方,或 ...

  4. MFC基础,MFC自绘控件学习总结.---转

    前言:从这学期开始就一直在学习自绘控件(mfc),目标是做出一款播放器界面,主要是为了打好基础,因为我基础实在是很烂....说说我自己心得体会以及自绘控件的方法吧,算是吐槽吧,说的不对和不全的地方,或 ...

  5. MFC自绘框架窗口客户区

    利用MFC开发用户界面往往需要需要根据要求进行界面美化,界面的美化包括很多内容,比如说界面各功能模块空间布局,控件位置选择,各功能模块区域的字体.背景颜色选择.添加位图,标题栏.菜单栏.状态栏等的重绘 ...

  6. MFC快速入门 - 菜单

    本文仅用于学习交流,商业用途请支持正版!转载请注明:http://www.cnblogs.com/mxbs/p/6231104.html 打开VS2010,依次打开File – New – Proje ...

  7. 图文详解MFC程序设置菜单快捷键

    原来觉得添加个快捷键就只要几分钟,上网搜索文章都写得很模糊, 只有这边文章不错. http://www.cplusplus.me/1263.html http://blog.sina.com.cn/s ...

  8. MFC重绘函数:InvalidateRect(), Invalidate()和UpdateWindow()

    1. 重绘消息 当需要更新或者重绘窗口时,一般系统会发出两个消息WM_PAINT(通知客户区有变化)和WM_NCPAINT(通知非客户区有变化) WM_NCPAINT系统会自己搞定 WM_PAINT消 ...

  9. MFC自绘控件学习总结第二贴---转

    首先感谢大家对第一帖的支持,应一些网友烈要求下面我在关于上一贴的一些补充和说明(老鸟可以无视)这一贴是实战+理论不知道第一帖的先看第一帖:http://topic.csdn.net/u/2011071 ...

随机推荐

  1. 记录eclipse安装SpringBoot插件及搭建SpringBoot项目

    刚学习了下SpringBoot 插件安装 创建项目在此记录下 在spring官网上下载相关的插件,然后导入到eclipse中,以下是下载步骤: 1.首先查看自己eclipse版本号 help--> ...

  2. BBS论坛(二十九)

    29.帖子详情页布局 (1)front/hooks.py @bp.errorhandler def page_not_found(): return render_template('front/fr ...

  3. BBS论坛(二十二)

    22.1.七牛js上传轮播图图片 (1)common/zlqiniu.js 'use strict'; var zlqiniu = { 'setup': function (args) { var d ...

  4. 学生选课系统 c语言

    /********************************* *主题:学生选修课程系统设计 * *设计要求: *1.添加功能:程序能够任意添加课程和学生记录,可提供选择界面供用户选择所需要添加 ...

  5. 【Git】(1)---工作区、暂存区、版本库、远程仓库

    工作区.暂存区.版本库.远程仓库 一.概念 1.四个工作区域 Git本地有四个工作区域:工作目录(Working Directory).暂存区(Stage/Index).资源库(Repository或 ...

  6. java基础(三)-----java的三大特性之多态

    面向对象编程有三大特性:封装.继承.多态. 封装隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据.对外界而已它的内部细节是隐藏的,暴露给外界的只是它的访问方法. 继承 ...

  7. Python爬虫入门教程 9-100 河北阳光理政投诉板块

    河北阳光理政投诉板块-写在前面 之前几篇文章都是在写图片相关的爬虫,今天写个留言板爬出,为另一套数据分析案例的教程做做准备,作为一个河北人,遵纪守法,有事投诉是必备的技能,那么咱看看我们大河北人都因为 ...

  8. PHP中的反射

    PHP中的反射 PHP5 具有完整的反射 API,添加了对类.接口.函数.方法和扩展进行反向工程的能力. 此外,反射 API 提供了方法来取出函数.类和方法中的文档注释. 请注意部分内部 API 丢失 ...

  9. npm 包的 发布 流程

    npm 包的发布流程 本文主要是针对 还未曾发布过自己的 npm 的同学,阐述一下 npm 的发布流程 熟悉的同学,可以绕道了. 首先你得有一个 自己的 npmjs.com 的账号 (没有的话,就到 ...

  10. leetcode — sum-root-to-leaf-numbers

    import java.util.Stack; /** * * Source : https://oj.leetcode.com/problems/sum-root-to-leaf-numbers/ ...