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

①当要使用顶层菜单资源、对话框资源、状态栏资源等这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. 面试题:反转字符串(leetcode344)

    编写一个函数,其作用是将输入的字符串反转过来.输入字符串以字符数组 char[] 的形式给出. 不要给另外的数组分配额外的空间,你必须原地修改输入数组.使用 O(1) 的额外空间解决这一问题. 你可以 ...

  2. HBase之行信息简析

    这一节我们简单介绍一下HBase的行信息.文章前半部分会对照源码介绍,后面会有我自己画的图,大家如果对这些信息已经比较了解了,跳过源码对照部分看后面的图,加深一下印象. 下面简单分析一下HBase中对 ...

  3. 侯哥的Python分享

    侯哥语录 我曾经是一个职业教育者,现在是一个自由开发者.我希望我的分享可以和更多人一起进步.分享一段我喜欢的话给大家:"我所理解的自由不是想干什么就干什么,而是想不干什么就不干什么.当你还没 ...

  4. JDBC编程,从入门到精通

    今天突然想多说两句,刚刚在知乎看到一个人说,在当今世界,没有技术型驱动的公司,全都是业务型.即便是表面上看似技术型公司,其本质还是为了服务业务.这段话推翻了我以前关于编程的所有看法,觉得颇有道理.下面 ...

  5. JVM基础系列第14讲:JVM参数之GC日志配置

    说到 Java 虚拟机,不得不提的就是 Java 虚拟机的 GC(Garbage Collection)日志.而对于 GC 日志,我们不仅要学会看懂,而且要学会如何设置对应的 GC 日志参数.今天就让 ...

  6. SpringCloud(2)---SpringCloud入门篇

    SpringCloud理解篇 一.微服务概述 1.什么是微服务 目前的微服务并没有一个统一的标准,一般是以业务来划分将传统的一站式应用,拆分成一个个的服务,彻底去耦合,一个微服务就是单功能业务,只做一 ...

  7. Dubbo(二) —— dubbo配置

      一.配置原则 JVM 启动 -D 参数优先,这样可以使用户在部署和启动时进行参数重写,比如在启动时需改变协议的端口. XML 次之,如果在 XML 中有配置,则 dubbo.properties ...

  8. macOS的OpenCL高性能计算

    随着深度学习.区块链的发展,人类对计算量的需求越来越高,在传统的计算模式下,压榨GPU的计算能力一直是重点. NV系列的显卡在这方面走的比较快,CUDA框架已经普及到了高性能计算的各个方面,比如Goo ...

  9. selenium和webdriver区别

    接触selenium大概半年时间了.从开始的预研,简单的写个流程到后期的自动化框架的开发,因为本人不属于代码方面的大牛,一直的边研究边做.逐步深入学习.近期发现自己对本身selenium的发展还存在困 ...

  10. Spring Boot(十)Logback和Log4j2集成与日志发展史

    一.简介 Java知名的日志有很多,比如:JUL.Log4j.JCL.SLF4J.Logback.Log4j2,那么这些日志框架之间有着怎样的关系?诞生的原因又是解决什么问题?下面一起来看. 1.1 ...