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

①当要使用顶层菜单资源、对话框资源、状态栏资源等这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. 分布式事务之如何基于RocketMQ的事务消息特性实现分布式系统的最终一致性?

    导读 在之前的文章中我们介绍了如何基于RocketMQ搭建生产级消息集群,以及2PC.3PC和TCC等与分布式事务相关的基本概念(没有读过的读者详见

  2. JVM基础系列第5讲:字节码文件结构

    温馨提示:此篇文章长达两万字,图片50多张,内容非常多,建议收藏后再看. 前面我们说到 Java 虚拟机使用字节码实现了跨平台的愿景,无论什么系统,我们都可以使用 Java 虚拟机解释执行字节码文件. ...

  3. EF架构~mysql数据库无法创建数据模型

    回到目录 主要是通过vs2017+mysql.Data+Mysql.data.Entity+ef 来进行开始,当我们选择数据模型生成实体时,可以会出现以下问题:  http:// 解决办法: 1.安装 ...

  4. 函数式编程之-bind函数

    Bind函数 Bind函数在函数式编程中是如此重要,以至于函数式编程语言会为bind函数设计语法糖.另一个角度Bind函数非常难以理解,几乎很少有人能通过简单的描述说明白bind函数的由来及原理. 这 ...

  5. Mybatis配置信息浅析 MyBatis简介(二)

    官方文档入门篇中有明确说明 每个基于 MyBatis 的应用都是以一个 SqlSessionFactory 的实例为中心的. SqlSessionFactory 的实例可以通过 SqlSessionF ...

  6. C#___.net经典题

    1 请你说说   .NET  中类和结构的区别? 答:结构和类具有大体的语法,但是结构受到的限制比类要多.结构不能申明有默认的构造函数,为结构的副本是又编译器创建 和销毁的,所以不需要默认的构造函数和 ...

  7. C# 判断用户是否对路径拥有访问权限

    如何获取当前系统用户对文件/文件夹的操作权限? 1.获取安全信息DirectorySecurity DirectorySecurity fileAcl = Directory.GetAccessCon ...

  8. C# 实现对PPT文档加密、解密以及重置密码的操作

    工作中我们会使用到各种各样的文档,其中,PPT起着不可或缺的作用.一份PPT文档里可能包含重要商业计划.企业运营资料或者公司管理资料等.因此,在竞争环境里,企业重要资料的保密工作就显得尤为重要,而对于 ...

  9. 关闭open页面时刷新父页面列表

    var winObjEI = window.open("/Invoice/InvoiceViewEI?invoiceid=" + data.InvoiceId); ; //关闭op ...

  10. CSS3图片翻转动画技术详解

    CSS动画非常的有趣:这种技术的美就在于,通过使用很多简单的属性,你能创建出漂亮的消隐效果.其中代表性的一种就是CSS图片翻转效果,能让你看到一张卡片的正反两面上的内容.本文就是要用最简单的方法向大家 ...