MFC下按钮自绘的实现

2023-05-16

MFC下按钮自绘的实现

MFC下按钮自绘的实现(一)

       在MFC下编程,很多时候对于标准的按钮控件不是很满意,想要弄的美观点。这就需要按钮自绘。这里简单记录一下方法,以免过个十天半个月的,自己又忘的一干二净了。

       首先给工程添加一个MFC类,基类为CButton。要想让按钮具备自绘功能,就要为按钮添加BS_OWNERDRAW属性。为类CButton重载PreSubclassWindow虚函数。在该函数中添加如下一行代码:

       ModifyStyle(0, BS_OWNERDRAW);

       当按钮控件具有了自绘功能之后,每次控件状态改变都会触发DrawItem函数,在该函数中来绘制按钮的形态外观,所以第二步就要重载DrawItem虚函数。在这个函数中就可以自由发挥了,比如绘制外边框,底色,按钮标题,内边框等等。

       一般都会为按钮定义几种不同状态时的外观,比如光标滑过时的状态,按钮按下时的状态,按钮禁用时的状态,以及按钮的正常状态等等。这就要为新的按钮添加几种重要的消息响应。比如WM_MOUSELEAVE消息,WM_MOUSEHOVER消息和WM_MOUSEMOVE消息等等,值得一提的是前两个消息的响应函数需要自己手动添加,微软提供了一个TrackMouseEvent函数在光标离开一个窗口时投递WM_MOUSELEAVE消息,光标滑过窗口时投递WM_MOUSEHOVER消息。一般来说可以在WM_MOUSEMOVE消息响应函数中调用TrackMouseEvent函数来投递WM_MOUSELEAVE消息和WM_MOUSEHOVER消息。然后在WM_MOUSELEAVE消息的响应函数中标记“光标已经离开按钮”,然后调用InvalidateRect函数让按钮重绘。在WM_MOUSEHOVER消息的响应函数中标记“光标正在按钮上方”,并调用InvalidateRect函数让按钮重绘。

       典型代码:

       if (!m_bTracking)  // 判断此时按钮是否已经按下

       {

              TRACKMOUSEEVENT tme;

              tme.cbSize = sizeof(tme);

              tme.hwndTrack = m_hWnd;

              tme.dwFlags = TME_LEAVE | TME_HOVER;

              tme.dwHoverTime = 1;

              m_bTracking = _TrackMouseEvent(&tme);

       }

这几天都是这么困,不知道是怎么搞的,呆会接着写。


MFC下按钮自绘的实现(二)

       上篇文章中提到使用WM_MOUSELEAVE消息,但是在Windows CE操作系统下,手动添加WM_MOUSELEAVE消息响应函数之后,编译会发现WM_MOUSELEAVE没有定义。之前在Windows XP操作系统下执行的程序没有这个提示。找到原来的程序,发现WM_MOUSELEAVE的定义在\microsoft visual studio 8\vc\platformsdk\include\winuser.h文件中。

#if(WINVER >= 0x0500)

#define WM_NCMOUSEHOVER                 0x02A0

#define WM_NCMOUSELEAVE                 0x02A2

#endif /* WINVER >= 0x0500 */

据此手动添加如下代码:

#ifndef WM_MOUSELEAVE

#define WM_MOUSELEAVE                   0x02A3

#endif

对于WM_MOUSEHOVER消息也是一样:

#ifndef WM_MOUSEHOVER

#define WM_MOUSEHOVER                   0x02A1

#endif

重新编译即可。

另外上篇文章中说道TrackMouseEvent函数用来投递WM_MOUSELEAVE和WM_MOUSEHOVER消息。貌似这个函数在Windows CE操作系统下也找不到。找不到就不用它了,自己直接调用PostMessage投递出去算了。

比如:

::PostMessage(m_hWnd, WM_MOUSELEAVE, 0, 0);

那么当光标滑过按钮时,会触发WM_MOUSEMOVE消息,在这个函数中如何判断光标是在按钮上停留着还是离开了,从而是发送WM_MOUSELEAVE消息还是WM_MOUSEHOVER消息呢?这个不难吧,至少PtInRect函数可以搞定。

自己测试了一下,完全可以。


MFC下按钮自绘的实现(三)

       按钮的绘制主要在DrawItem函数中来完成,下面来简单的绘制一下。

第一步先绘制按钮的外边框。定义了一个成员变量:

       CPen m_OutBorderPen;

       这是一个用来绘制按钮外边框的画笔,在类的构造函数中创建它,在类的析构函数中销毁之。然后在DrawItem函数中开始绘制按钮的外边框:

       CRect rect = lpDrawItemStruct->rcItem;

       CDC *pDC = CDC::FromHandle(lpDrawItemStruct->hDC);

       int nSavedDC = pDC->SaveDC();

 

       // 绘制按钮的外边框

       POINT pt;

       pt.x = 5;

       pt.y = 5;

       CPen *pOldPen = pDC->SelectObject(&m_OutBorderPen);

       pDC->RoundRect(&rect, pt);

       pDC->SelectObject(pOldPen);

       编译之后执行一下程序,看下效果:

      

       按钮的轮廓出来了。

       第二步绘制按钮的底色。

       // 绘制按钮的底色

       rect.DeflateRect(3, 3, 3, 3);

       CBrush *pOldBrush = pDC->SelectObject(&m_BackgroundBrush);

       pDC->Rectangle(rect);

       pDC->SelectObject(pOldBrush);

       这里只是简单的示范一下而已,画出来的按钮不一定好看。这里将按钮的底色设置为纯白色。程序的执行效果如下:

      

       第三步绘制按钮的文本。

       // 绘制按钮文本

       TCHAR strButtonText[MAX_PATH + 1];

       ::GetWindowText(m_hWnd, strButtonText, MAX_PATH); // 获取按钮文本

       if (strButtonText != NULL)

       {

              CFont *pFont = GetFont();

              CFont *pOldFont = pDC->SelectObject(pFont);

              CSize szExtent = pDC->GetTextExtent(strButtonText, _tcslen(strButtonText));

              CRect rectText = lpDrawItemStruct->rcItem;

              rectText.DeflateRect(rect.CenterPoint().x - szExtent.cx / 2, rect.CenterPoint().y - szExtent.cy / 2, rect.CenterPoint().x - szExtent.cx / 2, rect.CenterPoint().y - szExtent.cy / 2);

              int nOldBkMode = pDC->SetBkMode(TRANSPARENT);

              pDC->DrawText(strButtonText, -1, rectText, DT_WORDBREAK | DT_CENTER);

              pDC->SelectObject(pOldFont);

              pDC->SetBkMode(nOldBkMode);

       }

       重新编译,执行效果如下:

      

       按钮的基本外观已经绘制出来了。接下来还要绘制按钮按下时,光标滑过时,光标离开时等等状态下按钮的外观。当然还要探索一下圆形按钮,三角形按钮,以及不规则图形按钮的绘制。累了,先写到这里。

      

 
MFC下按钮自绘的实现(四)

    接下来为自绘的按钮绘制不同状态下的外观,首先绘制按钮按下时的状态。一般在按钮按下时,按钮的文本会向右下方移动一个微小的距离,使其看起来有被“压下”的视觉。

通过如下代码获取按钮的状态:

UINT state = lpDrawItemStruct->itemState;

然后在DrawItem函数中绘制按钮文本之前(DrawText)添加如下代码:

if (state & ODS_SELECTED)

{

    rectText.OffsetRect(1, 1);

}

    编译运行程序,看下效果。

    按钮正常状态:

   

    按钮被按下时:

 

对比一下,可以看出按钮按下时,按钮上文本向右下方移动一小段距离。测试了一下,效果还不错。

    接下来绘制当光标位于按钮之上但按钮并没有被按下时的状态。这里仅仅大致的介绍一下我的方法。

    首先在OnMouseMove函数中,添加如下代码:

    if (!m_bOver)

    {

        m_nTimerId = SetTimer(1, 50, TimerProc);

        m_bOver = TRUE;

    }  

    其中m_bOver是用来标记光标此时是否在按钮之上的BOOL类型的变量。当光标经过按钮时,触发OnMouseMove函数,在此函数中设置一个定时器,定时时间为50ms,定时时间到了之后将触发TimerProc回调函数。在TimerProc函数中不断的判断此时光标停留在按钮之上,还是已经离开了按钮。

    POINT CursorPos;

    RECT ButtonRect;

    ::GetCursorPos(&CursorPos);

    ::ScreenToClient(hwnd, &CursorPos);

    ::GetClientRect(hwnd, &ButtonRect);

    if (!::PtInRect(&ButtonRect, CursorPos))

    {

        ::PostMessage(hwnd, WM_MOUSELEAVE, 0, 0);

    }

    else

    {

        ::PostMessage(hwnd, WM_MOUSEHOVER, 0, 0);

    }

    如果光标停留在按钮之上,投递出WM_MOUSEHOVER消息之后将触发OnMouseHover函数,在该函数中将m_bOver设置为TURE,并调用InvalidateRect函数更新窗口;如果光标已经离开了按钮,投递出WM_MOUSELEAVE消息,触发OnMouseLeave函数,在该函数中将m_bOver设置为FALSE,然后调用InvalidateRect函数更新窗口,接着关闭定时器,因为此时已经不必再连续判断光标是否还停留在按钮之上了。最后在DrawItem函数中判断如果m_bOver为TURE,则绘制一定的图形进行标记。

    如下图:

    光标不在按钮之上:

   

    光标位于按钮之上:

   

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

MFC下按钮自绘的实现 的相关文章

  • 如何将 INT64 写入 CString

    我正在 c windows 中编码 INT64 dirID 1 CString querySQLStr T querySQLStr Format L select from ImageInfo where FolderPath 64d di
  • 如何在基于 MFC 对话框的应用程序中捕获复选框的 MouseMove 事件?

    我的应用程序是一个基于 VC6 MFC 对话框的应用程序 具有多个属性页 我必须捕获控件上的鼠标移动事件 例如复选框 如何捕获 MFC 中复选框上的 mousemove 事件 复选框是一个按钮控件 例如 CWnd 从 CCheckBox 派
  • 在 Visual Studio 2013 中显示带有偏移量的控件

    最近 我将源代码从 Visual Studio 2010 迁移到 Visual Studio 2013 在 Visual Studio 2013 中构建后 控件将显示有偏移 单击下面的链接查看图像 链接到图像 https i stack i
  • 在Windows服务中使用MFC?

    我开始开发 Windows 服务 我想使用我自己的一些类 它们对某些 MFC 类 如 CString CSocket CArchive CMemFile 和 CObject 几乎没有依赖性 MSDN http msdn microsoft
  • Mfc CComboBoxEx - 如何更改背景颜色

    我有一个派生自 CComboBoxEx 的类 我正在尝试更改背景颜色 我认为它会像 ComboBox 一样工作 使用 SetBkColor 函数 但它不会改变背景颜色 这是我尝试过的 BEGIN MESSAGE MAP CMyComboBo
  • Visual Studio 无法识别我的网络摄像头激光测距仪代码的 MFC 库

    我尝试直接从互联网复制源代码 但由于下面发现的错误 我无法构建 调试整个文件 请帮忙 Error occurred while restoring NuGet packages System ArgumentException The pa
  • 如果我没有为其相应的命令声明消息映射条目,如何阻止 MFC 禁用我的控件?

    我有以下问题 如果我没有相应消息的消息映射条目 假设 ID MYBUTTON1 MFC 将禁用我的工具栏 CToolbar 控件 有没有解决的办法 我对菜单也有同样的问题 但我发现您可以通过将 CFrameWnd m bAutoMenuEn
  • MFC中Tree(CTreeCtrl)中添加特定树项的图标

    我们可以为特定的树项目添加图标吗 我使用以下功能添加带有图标的项目 HTREEITEM InsertItem LPCTSTR lpszItem int nImage int nSelectedImage HTREEITEM hParent
  • 有效的 MFC ID 范围

    这让我很困惑 我正在读这个技术说明 https learn microsoft com en gb cpp mfc tn020 id naming and numbering conventions它指出 Prefix Resource t
  • CloseWindow和WM_CLOSE有什么关系

    我现在有点困惑 是吗 WM CLOSE http msdn microsoft com en us library windows desktop ms632617 28v vs 85 29 aspx and CloseWindow htt
  • 使用 CSplitterWnd 在 CChildFrame 中创建多个视图

    我正在使用 MFC MDI 我需要创建如下视图 我的 ChildWnd 分为两部分 它们是LeftView CView 和RightView CScrollView LeftView 分为两部分 TreeView 和 FormView 我怎
  • MFC 控件上的文本 - Unicode 字符(例如日语)被截断

    背景 我正在开发一个 C MFC 应用程序 我们已经将其转换为显示 unicode 字符以支持外语 在大多数情况下 这是成功的并且 unicode 字符显示正确 但我遇到了一个问题 某些控件上的某些文本被截断 Example 在这里 您可以
  • 默认情况下启用或禁用菜单项。为什么?

    我有一些遗留代码 由于某种原因 菜单项在启动时被启用或禁用 我的问题是 如何 有没有办法在不调用 EnableMenuItem 函数的情况下执行此操作 MFC 有没有办法做与资源设置所说相反的事情 我也不明白为什么当最后一个子窗口关闭时 当
  • 当窗口未最大化时缺少 WM_NCLBUTTONUP 消息的奇怪问题

    我有一个处理 WM NCLBUTTONUP 消息的窗口 以便处理标题栏中自定义按钮的点击 当窗口最大化时 这非常有效 但当窗口未最大化时 WM NCLBUTTONUP 消息永远不会到达 不过我确实收到了 WM NCLBUTTONDOWN 消
  • 如何在现有 Windows 应用程序中获得 ATL 支持

    我正在 Visual Studio 2012 中使用 Qt 5 3 1 构建一个应用程序 我还想使用一个硬件库 这需要我向项目添加一个简单的 ATL 对象 这可以通过使用 Visual Studio 向导来完成 该向导抱怨我的项目既不是 M
  • 与 UltraHD 兼容的 CHtmlView

    CHtmlView与 UltraHD 分辨率不兼容 实现 UltraHD 感知并不仅仅在于使用正确的 HTML CSS 打印预览机制失败并裁剪页面 许多个月前 微软承认这是一个问题 但没有解决它 我的应用程序大量使用CHtmlView用于显
  • 在Linux上运行MFC程序

    我有一个相当大的基于 MFC 的程序 我的任务是让它在 Linux 上运行 我已经解释过 这需要将程序重新编写为带有 STL 的直接 C 更多工作 或者重新编写为 Qt C 更少工作 现在我被告知 我需要编写包装器以使每个 MFC 类在 L
  • 想要将 ColeDateTime 转换为 CTime

    我正在从数据库中读取日期时间ColeDateTime格式 我想将其转换为CTime获取日期 月份 年份和时间 CString repDt this will hold the datetime which i read from Datab
  • 获取正在运行的程序的属性

    我想开发一个程序 其 ID 是一张牌 因为它在另一个正在运行的程序 例如扑克或红心游戏或其他程序 中播放 我首先尝试获取有关已运行的游戏程序的所需信息 但我从一开始就遇到了问题 我正在运行 MSVC 2013 并开发 MFC 应用程序 现在
  • 发送WM_SETTEXT时如何避免EN_CHANGE通知?

    我有一个 CEdit 派生控件 当基本数据为空时 该控件显示字符串 N A 我最近添加了代码 以在控件获得焦点时清空控件 SetWindowText 并在用户离开焦点时将其设置回 N A SetWindowText N A 控空 唯一的问题

随机推荐

  • C# 连接 SqlServer 数据库

    目录 一 创建表 二 给表添加数据 三 新建 C 项目 四 SqlServerHelper 五 连接数据库 一 创建表 首先 xff0c 新建一个数据库 Test xff0c 然后新建一个表 Users xff0c 字段名如下图 xff0c
  • org.xml.sax.SAXParseException的错误解决 2020-11-20

    span class token number 2020 span span class token operator span span class token number 11 span span class token operat
  • JS如何优雅的删除对象中的指定属性?

    要优雅的话 xff0c 使用 Lodash 的 omit 方法移除不要的属性 xff1a const object 61 a 1 b 2 c 3 const result 61 omit object a c 61 gt b 2 或者用 p
  • python 使用 isdigit 判断字符串中是否只由数字组成

    span class token operator span span class token operator span span class token operator span span class token operator s
  • 快速排序详解(Java实现)

    一 快速排序的基本思想 每一轮的排序都会将区域分割成两个独立的分区 xff0c 其中左分区的序列的所有值均会比右分区的所有值小 然后对子分区进行同样的分割操作 xff0c 最后达到整体有序 在排序的过程中 xff0c 由于已经分开的两部分的
  • A*算法路径规划之Matlab实现

    A 算法路径规划之matlab实现 A 算法是一种传统的路径规划算法 xff0c 相较于Dijkstra算法 xff0c 其引入了启发式算子 xff0c 有效的提高了路径的搜索效率 主要步骤包括 xff1a 1 xff09 设置起始点 目标
  • C语言中‘a‘和“a“有什么区别?

    1 本质区别 双引号里面的是字符串 xff0c 而单引号里面的代表字符 2 输出区别 str 61 a 输出的就是a这个字母 xff1b str 61 a 输出的测试65 3 底层区别 用单引号引起的一个字符实际上代表一个整数 xff0c
  • linux VNC客户端登陆失败

    vnc登陆出现 Unknown authentication scheme from VNC server 解决办法 xff08 建议在做操作之前重启vnc server xff0c 密码输错过多可能导致一直连接失败 xff09 https
  • win 10 mstsc连接 RemoteApp

    本文是关于mstsc客户端的配置 xff08 服务端的配置本文不描述 xff09 xff0c 前提是服务端配好 xff0c 知道RemoteApp怎么玩的 windows 2008 的mstsc有个配置 xff0c 关于程序 的tab页 但
  • 贪心算法的改进

    关于贪心算法 xff0c 请看我的上一篇博客 解决贪心算法的复杂度 为解决贪心算法的复杂度 本文提出 xff1a 通过分解极大联通子图去寻找影响力最大的节点的算法 强连通 xff1a 在有向图G中 xff0c 如果任意两个不同的顶点相互可达
  • 非关系型数据库-redis应用场景

    关系型数据库与非关系型数据库 redis的应用场景 xff1a 1 redis由于数据的读取和操作都在内存当中操作 xff0c 读写的效率较高 xff0c 所以经常被用来做数据的缓存 把一些需要频繁访问的数据 xff0c 而且在短时间之内不
  • Linux创建用户后,登录报错/usr/bin/xauth: file /home/user/.Xauthority does not exist

    错误信息如下 usr bin xauth span class token function file span home user Xauthority does not exist 错误原因 是因为添加用户时没有授权对应的目录 xff0
  • VSCode测试ES6语法

    一 VsCode使用 1 1 VsCode基本操作 Alt 43 Shift 43 F格式化代码 二 ES6新特性 2 1 let声明常量 2 2 const声明常量 只读变量 2 3 解析表达式 2 3 1 数组解构 2 3 2 对象解构
  • VNC远程连接树莓派报错问题解决(文末有惊喜呦)

    用VNC远程连接树莓派 xff0c 出现拒绝连接 问题原因 xff1a 只设置了PC端的服务器 xff0c 但是没有在树莓派端开端口 解决办法 xff1a 用putty或者xshell连接树莓派 xff0c 输入命令 xff1a vncse
  • ReadFile函数

    函数功能 xff1a 该函数从文件指针指示的位置开始从文件读数据 xff0c 在读操作完成后 xff0c 如果文件句柄不是用重叠属性来创建 xff0c 则文件指针用实际读的字数来调整 如果文件句柄为重叠的输入和输出 xff08 I O xf
  • error MSB8041: 此项目需要 MFC 库。从 Visual Studio 安装程序(单个组件选项卡)为正在使用的任何工具集和体系结构安装它们。

    最近新安装了vs2019企业版 xff0c 用vs打开vs2015的工程出现如下错误 xff1a error MSB8041 此项目需要 MFC 库 从 Visual Studio 安装程序 单个组件选项卡 为正在使用的任何工具集和体系结构
  • PC和Android模拟器之间虚拟串口通信

    前言 很多时候我们在做串口通信的时候需要去模拟串口来调试 xff0c 这样可以极大的方便我们的工作 xff0c 不然的话可能需要连接硬件设备 xff0c 比较麻烦 一般情况下我们先在电脑上模拟串口后初步调试 xff0c 最后阶段在硬件设备上
  • 【C语言】将正数转成负数,将负数转成正数

    将正数转成负数 xff0c 将负数转成正数 include lt stdio h gt int change int num if num lt 0 num 61 num 1 else if num gt 0 num 61 num 43 1
  • C语言中的输入输出流和缓冲区(重点)详解

    导读 xff1a C语言中我们用到的最频繁的输入输出方式就是scanf 与printf scanf xff1a 从标准输入设备 键盘 读取数据 xff0c 并将值存放在变量中 printf xff1a 将指定的文字 字符串输出到标准输出设备
  • MFC下按钮自绘的实现

    MFC下按钮自绘的实现 MFC下按钮自绘的实现 xff08 一 xff09 在MFC下编程 xff0c 很多时候对于标准的按钮控件不是很满意 xff0c 想要弄的美观点 这就需要按钮自绘 这里简单记录一下方法 xff0c 以免过个十天半个月