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

①当要使用顶层菜单资源、对话框资源、状态栏资源等这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. Kubernetes 笔记 06 豌豆荚之旅(一)

    本文首发于我的公众号 Linux云计算网络(id: cloud_dev),专注于干货分享,号内有 10T 书籍和视频资源,后台回复「1024」即可领取,欢迎大家关注,二维码文末可以扫. Hi,大家好, ...

  2. RxJS简介

    函数式编程 1.声明式(Declarativ) 和声明式相对应的编程⽅式叫做命令式编程(ImperativeProgramming),命令式编程也是最常见的⼀种编程⽅式. //命令式编程: funct ...

  3. Go JSON 转化规则

    Go语言内置encoding/json包支持JSON序列化和反序列化,有如下转换规则 基本的数据结构映射关系 bool, for JSON booleans float64, for JSON num ...

  4. 5.Django cookie

    概述 1.获取cookie request.COOKIES['key'] request.COOKIES.get('key') request.get_signed_cookie(key, defau ...

  5. Leetcode 10. 正则表达式匹配 - 题解

    版权声明: 本文为博主Bravo Yeung(知乎UserName同名)的原创文章,欲转载请先私信获博主允许,转载时请附上网址 http://blog.csdn.net/lzuacm. C#版 - L ...

  6. 华为oj之求int型正整数在内存中存储时1的个数

    题目: 求int型正整数在内存中存储时1的个数 热度指数:4427 时间限制:1秒 空间限制:32768K 题目描述 输入一个int型的正整数,计算出该int型数据在内存中存储时1的个数. 输入描述: ...

  7. centos系统安装第三方源EPEL

    epel没安装呗 相当于扩展型软件仓库,EPEL (Extra Packages for Enterprise Linux,企业版Linux的额外软件包) 是Fedora小组维护的一个软件仓库项目,为 ...

  8. [四] java8 函数式编程 收集器浅析 收集器Collector常用方法 运行原理 内部实现

    Collector常见用法     常用形式为:   .collect(Collectors.toList()) collect()是Stream的方法 Collectors  是收集器Collect ...

  9. git第三节----git status与git diff

    @ git status主要检索本地仓库的文件更新状态 @ git diff 主要是查看文件更新的具体内容 首先我们需要了解下文件状态类型,分为以追踪文件和未追踪文件 已追踪文件:在仓库之前的版本快照 ...

  10. [UOJ310] 黎明前的巧克力

    Sol 某比赛搬了这题. 首先选择两个不交非空子集且异或和为0的方案数,等价于选择一个异或和为0的集合,并把它分成两部分的方案数. 这显然可以DP来算,设 \(f[i][j]\) 表示前\(i\)个数 ...