简介:这是一套面向MFC开发者的CButtonST V3.9修正版资源包,专门解决原始代码升级到Visual Studio 2012时出现的编译错误与兼容性问题。作为经典按钮控件扩展库的最终免费版本,它支持多种按钮样式、自定义鼠标与键盘事件、状态反馈、图文自由布局以及多语言文本,修正后可在VS2012下正常编译运行。压缩包共含93个文件、约617KB,其中16个cpp源文件与18个h头文件构成核心代码,另外35个ico图标、7个bmp位图、2个wav音效为界面资源,并附有完整的Demo工程和解决方案、过滤器等文件,适合直接打开参照或集成到现有项目。已有366人学习下载,对于仍在使用VS2012的MFC开发者而言,该修正版免去了自行排查旧代码兼容问题的麻烦,可继续利用CButtonST丰富的界面特性增强Windows应用交互体验。
1. CButtonST V3.9 到底是个啥:老 MFC 项目里最好用的按钮自绘类
接手一个老 MFC 工程,界面还是 Windows 2000 那种灰底硬按钮,客户提了一嘴“能不能把按钮做得跟现在 App 一样”。你第一个想到的往往不是去重写界面,而是去找一个能直接塞进对话框的按钮类。CButtonST 就是这类需求里最常被翻出来的那个名字。它是一个基于 CButton 派生的自绘按钮类,解决了普通按钮不能随意改背景色、不能同时显示图标和文字、不能做多状态切换的问题。V3.9 是流传得比较广的版本,而这个“修正版”解决的问题很具体:让它在 VS2012 的编译器底下能一口气编过,不再需要为了一个老控件去降级工具链。如果你正在维护 C/C++ 写的桌面客户端,又不想为了好看的按钮引入整个 UI 框架,这个类依然是值得花半小时接进来的方案。
2. 为什么原版 CButtonST 在 VS2012 下编译费劲:先搞清它的工作方式
2.1 CButtonST 的自绘机制:不是贴图,是接管了整条绘制链路
普通 CButton 的绘制由系统完成,你顶多设置一个背景色,但控制不了按钮内部的细节。CButtonST 的思路是重写按钮的绘制流程:它对外提供几十个设置方法,内部把这些设置翻译成画笔、画刷、字体和几何计算,然后一次性画到按钮客户区上。核心动作在于 MFC 的消息反射机制。按钮收到 WM_DRAWITEM 时,消息会先回到父窗口,再反射给按钮自己。CButtonST 在派生类里处理了 OnDrawItem,并调用自己内部的 Draw 系列函数,把普通按钮变成了一个完全可控的绘图表面。
这个机制有两个直接后果。第一,按钮必须拥有“自绘”属性,否则系统根本不发 WM_DRAWITEM 给你。你在对话框资源里拖上去的普通按钮,默认没有这个属性,所以要么在资源编辑器里勾选 Owner Draw,要么在代码里用 ModifyStyle 把 BS_OWNERDRAW 加上去。第二,自绘之后,按钮的边框、背景、文字颜色、焦点框全部由类自己负责,任何一个绘制分支没处理干净,按钮的显示就会缺一块。网上很多“按钮消失”“只有文字没背景”的问题,本质上都是自绘代码里某个绘制分支在特定条件下没走到。
2.2 原版 V3.9 在 VS2012 编译不过的三类典型原因
VS2012 使用的是 Visual C++ 11 编译器,它对 C++ 标准的支持比 VC6、VS2008 严格不少,同时对 Windows SDK 里的宏定义也做了很多清理。原版 CButtonST V3.9 之所以编译不过,集中在三类问题上。
第一类是字符串处理。老代码里很多地方写的是 char* 或字符串字面量直接参与拼接,而工程一旦设置了 Unicode 字符集,LPCTSTR 展开为 const wchar_t*,类型不匹配的报错会密集出现。常见的报错是“无法从 const char [N] 转换为 LPCWSTR”。这不是某一处的问题,而是整个文件需要按 Unicode 习惯改写。
第二类是新版 SDK 不再容忍某些旧写法。例如对 MessageBox 返回值与 IDYES、IDNO 的比较,在新 SDK 里宏展开结果类型变化后会产生“有符号/无符号不匹配”的警告,严重时直接报错。还有一些遗留 API 被标记为 deprecated,编译器按错误级别处理时就会卡住。
第三类与代码页有关。原版 V3.9 源码里带了不少非英文注释和字符串资源,如果它们是用 ANSI 编码保存的,在简体中文 Windows 上打开,VS2012 经常报 C4819 警告。这个警告默认不中断编译,但它往往铺满整个输出窗口,把真正有用的错误信息全部淹没,这也是“编译不过”的第一印象来源。
2.3 选型判断:为什么还用 CButtonST,而不是自己画或者换 UI 库
你可能想问,既然要改代码,为什么不干脆自己写一个自绘按钮,或者直接引入某个现代 UI 库。这要分场景看。
如果是新项目,确实不应该用 CButtonST,EWindow、DirectUI 那一套或者 Qt 都更稳妥。但绝大多数接触 CButtonST 的开发者,手上是一个已经运行了多年的 MFC 项目,对话框、消息映射、资源脚本都是现成的。这种情况下,把一个按钮类接进来,只改控件类型和少量映射代码,风险远小于重写界面。CButtonST 的接口设计又是围绕“按钮”这个词展开的:一个按钮一个对象,方法名直观,不需要理解复杂的视图树。
我自己在这样的老项目里接过几次 CButtonST,基本路径都是:把源文件加到工程、包含头文件、把控件变量类型从 CButton 换成 CButtonST、在 OnInitDialog 里调用几个设置方法,编译运行。整个过程通常在半小时内完成,出问题的概率也集中在字符串和字符集上。这种可控性正是老项目愿意继续使用它的主要原因。
3. 把修正版接进 VS2012 工程:从添加文件到第一个自绘按钮
3.1 文件放入和工程设置:先让源码能干净地编译
拿到修正版源码后,通常会看到这样几个文件:CButtonST.h、CButtonST.cpp,部分版本还带一个消息映射文件或者位图资源。先把它们拷贝到工程目录下的一个独立子目录,比如Libs/CButtonST,不要让源文件跟对话框代码混在一起,方便后续维护。然后在 VS2012 里右键工程,选择“添加 > 现有项目”,把两个文件加进工程。这一步做完,先直接按 F7 编译一次,不要急着写业务代码。
如果编译立刻报错,十有八九是字符集问题。你需要在工程属性里确认“配置属性 > 常规 > 字符集”这一项。修正版一般在 VS2012 下默认按 Unicode 处理,如果你的工程用的是多字节字符集,可能会反过来出现 LPCWSTR 转换到 LPCSTR 的错误。这里没有标准答案,以你的工程现有设置为主,CButtonST 自身要做的就是跟随工程的字符集。如果修正版的代码里已经全面使用_T()宏和TCHAR,这种跟随是能做到的。
完成基础编译后,建议顺手把 CButtonST.cpp 里的警告级别调低一点。在文件属性(注意是文件属性,不是工程属性)里,把“警告等级”改成 Level3,C4819 这类代码页警告可以单独屏蔽掉。这不影响业务代码的警告输出,只会让 CButtonST 编译时不再刷屏。这一步很实用,能让你在后续排查问题时不至于迷失在一堆警告里。
3.2 第一个接口调用:把对话框里的按钮替换成自绘按钮
假设你在一张登录对话框上放了一个 ID 为IDC_BTN_LOGIN的按钮。原来的代码可能是这样:
// Dialog.h 中的声明 CButton m_btnLogin; // Dialog.cpp 中的绑定 DDX_Control(pDX, IDC_BTN_LOGIN, m_btnLogin);现在要做两件事。第一,在头文件里把CButton改为CButtonST,同时包含头文件:
#include "CButtonST.h" // 类成员声明 CButtonST m_btnLogin;第二,在 OnInitDialog 里,调用修改样式的代码,让按钮开启自绘属性,并设置一个简单的平面样式:
BOOL CLoginDialog::OnInitDialog() { CDialogEx::OnInitDialog(); // 设置按钮为自绘模式 m_btnLogin.ModifyStyle(0, BS_OWNERDRAW); // 使用平面样式,去掉系统默认的立体边框 m_btnLogin.SetFlat(TRUE); // 设置文字与背景色 m_btnLogin.SetColor(CButtonST::BTNST_COLOR_BK_OUT, RGB(20, 120, 210)); m_btnLogin.SetColor(CButtonST::BTNST_COLOR_TEXT_OUT, RGB(255, 255, 255)); return TRUE; }ModifyStyle那行不是可选项,它负责把按钮变成自绘类型。少了这行,SetFlat、SetColor这些设置全部无效,控件看起来跟普通按钮没有任何区别,而且不会有任何报错。SetFlat(TRUE)让按钮以平面风格绘制,鼠标悬停时才显示立体边缘,登录界面通常会这样处理。SetColor的第一个参数指定颜色类型,BTNST_COLOR_BK_OUT表示按钮普通状态下的背景色,后面跟着的_OVER、_DOWN、_DISABLED分别控制悬停、按下和禁用状态的独立颜色。
这里要强调一点:以上代码适用于你在对话框上预先放好了按钮的场景。如果你完全通过代码创建按钮,不依赖对话框资源,那就需要在创建时指定BS_OWNERDRAW样式。用Create函数创建时,把样式参数写成这样即可:
// 运行时动态创建自绘按钮 m_btnLogin.Create(_T("登 录"), WS_CHILD | WS_VISIBLE | BS_OWNERDRAW | WS_TABSTOP, CRect(200, 100, 280, 135), this, IDC_BTN_LOGIN);WS_TABSTOP让按钮能通过 Tab 键聚焦,自绘按钮如果不处理焦点状态,视觉上看不出来聚焦,但键盘用户会困惑。建议所有动态创建的按钮都保留这个样式。
3.3 加一个图标试试:SetIcon 与资源加载的坑
CButtonST 的一个特色是支持在同一个按钮里同时显示图标和文字。给登录按钮加一个钥匙图标,代码很简单:
// 加载图标资源,IDI_KEY 是你在资源里加的图标 HICON hIcon = (HICON)::LoadImage( AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_KEY), IMAGE_ICON, 16, 16, // 小图标,16x16 LR_DEFAULTCOLOR ); // 设置图标,位置在文字左边 m_btnLogin.SetIcon(hIcon, 0);SetIcon的第二个参数是图标位置,0 表示放在按钮左侧,常用于标准图标按钮。这里的坑在于LR_DEFAULTCOLOR。如果你的图标是 256 色或更多色,LoadImage在加载时会按系统当前显示模式做颜色匹配,可能出现颜色偏暗或透明区域变黑的情况。更稳妥的做法是加LR_SHARED标志,让系统使用共享图标资源,避免重复加载时产生 GDI 泄漏。改写如下:
HICON hIcon = (HICON)::LoadImage( AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_KEY), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR | LR_SHARED );使用LR_SHARED后,图标资源归系统管理,你不需要手动销毁。反之,如果不加这个标志,hIcon变成了你手上的私有句柄,在按钮销毁时需要调用::DestroyIcon释放,否则每次对话框关闭都会泄漏一点 GDI 资源。运行时的表现就是程序长时间开关窗口后,按钮图标逐渐消失,甚至整个界面绘制异常。很多老项目在 Windows 7 上用得好好的,换到 Windows 10 后频繁出现界面刷新问题,其实不是 CButtonST 的错,而是资源句柄用完了。
4. 常用接口与样式组合:把按钮调整到你要的观感
4.1 SetColor、SetFlat、SetFont:三个最常用的设置器
CButtonST 的接口很多,但日常项目里高频使用的往往只有几个。SetColor负责颜色,它的颜色类型参数决定你设置的是哪一部分。常见的状态包括:
| 颜色类型 | 作用对象 | 典型使用时机 |
|---|---|---|
| BTNST_COLOR_BK_OUT | 普通状态背景色 | 默认按钮背景,用于品牌色 |
| BTNST_COLOR_BK_OVER | 鼠标悬停时背景色 | 增加交互反馈 |
| BTNST_COLOR_BK_DOWN | 鼠标按下时背景色 | 模拟按钮被按压 |
| BTNST_COLOR_BK_FOCUS | 键盘聚焦时背景色 | 无障碍场景 |
| BTNST_COLOR_BK_DISABLED | 禁用时背景色 | 灰色置灰 |
| BTNST_COLOR_TEXT_OUT | 普通状态文字颜色 | 保证文字可读性 |
SetFlat控制的是边框行为。SetFlat(TRUE)时,按钮默认没有边框,悬停时才画出边框;SetFlat(FALSE)时,按钮始终有一个平面边框,但不会像老式按钮那样有立体凸起。两者在视觉差异上比较微妙,我一般把前者用于工具栏按钮,后者用于对话框主操作按钮。
SetFont用于修改按钮文字字体。CButtonST 的字体设置会影响所有状态的文字,但不会影响图标位置。一个常见的问题是,设置了漂亮的文字字体后,按钮高度不够,文字被截断。这是因为设置字体后,按钮的客户区不会自动调整。你需要自己用MoveWindow或SetWindowPos把按钮稍微加高几个像素,或者在设计对话框时预留足够高度。这个问题在 XP 时代还不明显,因为默认字体比较小;到了现代 Windows 上,系统字体变大,按钮高度不足的问题会频繁冒出来。
4.2 位图按钮的透明处理:普通 BMP 与 PNG 的差别
很多教程会提到SetBitmaps函数,用它给按钮设置三张位图,分别对应普通、按下和禁用状态。这个方法在旧项目里很常见,但它有天然的局限:位图只能是 BMP 格式,BMP 不支持 alpha 通道透明。结果就是按钮背景图如果不和对话框背景完全一致,就会出现一块方形的底色边界。
修正版在 VS2012 下编译通过的场景里,如果你要处理的是带透明效果的图标按钮,我的建议是放弃SetBitmaps,改用SetIcon配合透明的 ICO 文件,或者直接在按钮上贴 PNG 并用自绘代码处理 alpha 混合。ICO 可以包含 alpha 通道,透明度能完美呈现,而且LoadImage加载 ICO 比解析 PNG 简单得多,不依赖额外库。
如果必须用位图,注意SetBitmaps的三个句柄参数,它们分别是默认位图、按下位图和禁用位图。所有位图都应该是同一尺寸,否则按钮会按照第一张位图的大小进行拉伸,第二、第三张会被缩放,观感不一致。位图句柄的生命周期跟图标一样,要么用LR_SHARED加载,要么在按钮销毁时手动释放。
4.3 与 VS2012 MFC 功能包控件的取舍
VS2012 自带 MFC 功能包,里面也有按钮美化能力:CMFCButton 支持直接设置颜色、图标、平面样式,而且集成度更高,不需要手动处理 BS_OWNERDRAW。既然微软官方给了方案,为什么还要用 CButtonST?
差别在于两点。CMFCButton 的样式变化虽然方便,但它依赖 MFC 功能包的整体初始化环境。如果项目里还有其他地方禁用了视觉管理器,CMFCButton 的外观会退化得很难看。而 CButtonST 是纯 GDI 绘制,不依赖 visual manager,在任何 Windows 版本上的表现都相对稳定。
另一层是代码迁移成本。老项目里已经有大量基于 CButtonST 的代码,方法名和参数格式都是现成的,换成 CMFCButton 需要重写所有调用。对于修正版已经解决了编译问题的场景,继续用 CButtonST 是最小改动方案。但如果全新开始写一个对话框,我会直接上 CMFCButton,毕竟是官方维护的控件,未来升级编译器时的兼容成本更低。这个选择不关乎技术优劣,纯粹看工程历史包袱。
5. CButtonST 修正版避坑指南:VS2012 编译与运行期的踩坑记录
5.1 编译时报 C4819 警告刷屏,真正的错误被淹没
现象:一编译 CButtonST.cpp,输出窗口瞬间几百条 C4819 警告,都是同一个文件,内容指向“无法将 ANSI 字符转换为 Unicode”,但程序本身还能编译通过,只是输出窗口没法看了。
原因:源码文件以 ANSI 编码保存,且包含非英文字符。VS2012 默认按当前系统代码页解析源文件,当代码页切换或文件里的字符不在系统代码页内时,编译器逐字符报警告。
解决:最干净的做法是让源码文件带 BOM 的 UTF-8 格式。在 VS2012 里打开 CButtonST.cpp,选择“文件 > 高级保存选项”,把编码改为“Unicode (UTF-8 带签名) - 代码页 65001”。改完后再编译,C4819 会消失。如果不方便改文件编码,也可以在文件开头加一行:
#pragma warning(disable: 4819)把它放在包含其他头文件之前。注意这个 pragma 只管当前编译单元,不影响工程的其它文件。
5.2 错误 C2664:无法将“const char *”转换为“LPCTSTR”
现象:编译到代码里出现大量 C2664 错误,都是字符串参数不匹配。明明原版代码在 VC6 下跑得好好的,到 VS2012 直接编不过。
原因:工程字符集是 Unicode,LPCTSTR 展开为const wchar_t*,而源码里直接写了中文字符串字面量,类型是const char[]。老版编译器对隐式转换比较宽容,VS2012 不再容忍。
解决:把 CButtonST.cpp 里所有的字符串字面量包上_T()宏。例如SetWindowText("Login")改成SetWindowText(_T("Login"))。如果修改范围太大,可以临时把工程字符集改成“多字节字符集”,但这会导致界面上的宽字符处理变得麻烦,不是长久之计。最稳妥的做法是一处处替换干净。修正版源码如果已经处理过这个问题,你拿到手后直接编译应该不会遇到,遇到的话就要逐一排查。
5.3 链接错误 LNK2005:一个变量被定义了两次
现象:编译通过,但链接时报 LNK2005,通常指向某个 buffer 或字符串变量,提示存在多个定义。
原因:CButtonST.cpp 里某个全局变量被定义在头文件中,而且这个头文件被多处包含。老版工程里这种事很常见,因为早期编译器允许一定程度上的重复定义延后处理,VS2012 的链接器按严格规则报错。
解决:检查头文件中的全局变量声明,改为extern声明,在 .cpp 中集中定义。如果变量是 const 字符串,可以用static const代替,给每个编译单元一份内部副本,避免跨文件共享。修改后重新编译整个工程,链接错误会消失。这个问题在修正版里一般已经处理掉了,但在你拿到任意一个基于 V3.9 魔改的版本时仍需警惕。
5.4 按钮销毁时程序崩溃或 GDI 泄漏
现象:程序正常使用,按钮也能显示,但关闭对话框时偶尔崩溃,或者长时间运行后按钮图标消失、界面刷新异常。
原因:按钮设置过的图标或位图句柄没有被释放。CButtonST 的析构函数默认会释放它自己创建的一些内部资源,但通过SetIcon从外部传入的句柄,它不会擅自帮你销毁。加没加LR_SHARED直接决定这个句柄能否由系统回收。
解决:使用SetIcon时,统一用LR_SHARED加载图标,这样句柄由系统管理,无需手动释放,也不会 GDI 泄漏。如果你明确知道某个 hIcon 需要自己释放,那就在按钮销毁前,也就是对话框的OnDestroy中调用::DestroyIcon,然后置空句柄。这里没有中间选项,两种方式选一种必须执行到底,否则都是隐患。
5.5 按钮看起来还是老样子,没有任何自绘效果
现象:所有设置代码都写了,编译运行都正常,但界面上按钮跟普通按钮一样,没有颜色变化,也没有平面效果。
原因:典型的BS_OWNERDRAW没设置成功。可能有几种情况:你在代码里调用了ModifyStyle,但按钮控件变量绑定错误,实际拿到的是另一个控件的句柄;或者按钮在资源脚本里已经被设置了BS_OWNERDRAW,但类变量没有正确执行DDX_Control,导致 SetFlat 的代码作用在一个无效对象上。
解决:先在OnInitDialog里加一个断言验证控件句柄:
ASSERT(m_btnLogin.GetSafeHwnd() != NULL); // 确认按钮样式包含自绘标志 UINT nStyle = m_btnLogin.GetButtonStyle(); TRACE(_T("Button style = 0x%X\n"), nStyle);运行后在输出窗口确认nStyle是否包含BS_OWNERDRAW(数值上对应 0x0000000B 与其它样式位的组合)。如果样式正确依然没有自绘效果,检查是不是按钮类变量没有在DoDataExchange里绑定,直接用GetDlgItem获取指针或改用SubclassDlgItem绑定,问题一般就会解决。
6. 进阶用法:继承修正版写自己的按钮类,避开最后一处暗伤
6.1 继承 CButtonST:把交互逻辑封装起来
CButtonST 本身是一个可继承的类。在项目里如果有多处地方用到同一种按钮外观,比如统一的主题色背景、统一的悬停加深效果,与其在每个对话框里写一堆SetColor,不如定义一个派生类,一次性搞定外观。
#pragma once #include "CButtonST.h" // 自定义主题按钮:统一品牌色背景与悬停反馈 class CThemeButton : public CButtonST { public: CThemeButton() { // 普通状态:深蓝色背景、白字 SetColor(BTNST_COLOR_BK_OUT, RGB(20, 100, 200)); SetColor(BTNST_COLOR_TEXT_OUT, RGB(255, 255, 255)); // 悬停状态:比普通状态亮的蓝 SetColor(BTNST_COLOR_BK_OVER, RGB(40, 120, 220)); // 按下状态:更深的蓝 SetColor(BTNST_COLOR_BK_DOWN, RGB(10, 70, 160)); // 禁用状态:灰色,文字淡一些 SetColor(BTNST_COLOR_BK_DISABLED, RGB(200, 200, 200)); SetColor(BTNST_COLOR_TEXT_DISABLED, RGB(120, 120, 120)); // 全部按钮统一使用平面样式 SetFlat(TRUE); } };在构造函数里直接调用 SetColor 这样的方法是可以的,因为 CButtonST 的颜色值存在内部成员变量中,不依赖窗口创建。但要注意,SetFont不要放在构造函数里,因为字体设置需要窗口句柄。放到OnInitDialog阶段或PreSubclassWindow中处理更合适。
派生类的好处是,你在对话框里只需要把控件变量声明为CThemeButton,代码立刻从繁琐的设置序列变成一行声明。如果后续要改整个产品的主题色,只需要改这一个类,配合全局搜索按钮变量类型,改动量可以压缩到很小。
6.2 处理键盘焦点:自绘按钮容易忽略的可访问性细节
自绘按钮在视觉上如果处理不好焦点框,用户按 Tab 键时根本分不清焦点落在哪个按钮上。CButtonST 默认会画一个焦点虚线框,但这个框有时不太明显,尤其当按钮是平面样式且背景色较深时。
可以重写OnDrawFocusBorder(部分版本叫DrawFocusRect)来强化它:
void CThemeButton::OnDrawFocusBorder(CDC* pDC, CRect rcClient) { // 用一个浅色虚线框替代默认样式 CBrush br(RGB(255, 255, 0)); pDC->FrameRect(rcClient, &br); }这样焦点状态会非常醒目,视觉障碍用户在使用 Tab 键操作时能清晰看到当前聚焦位置。这块代码在老项目的审计要求里经常被点名,因为很多自绘按钮完全忽略了焦点可视化。你的按钮不画焦点框不算 bug,但算体验缺陷,客户验收时容易挑刺。
6.3 验证自绘是否真生效:Spy++ 看窗口类名不算,按钮消息才是关键
界面效果跑出来了,但怎么确认代码逻辑确实执行了?两个办法。第一,在 OnDrawItem 里加一个临时断点,调试运行时如果断点命中,说明消息反射链路是通的。第二,用 Spy++ 或者 VS2012 自带的消息监视工具,查看鼠标移动到按钮上时,控件窗口是否收到 WM_MOUSEHOVER,以及父窗口是否收到 WM_DRAWITEM。
这里有一个我自己的习惯:不在 OnDrawItem 里做太重的计算。自绘按钮的 OnDrawItem 在鼠标移动、窗口重绘、遮挡变化时都会触发,频率很高。如果你在绘制代码里做了 GDI 对象创建、字体创建、字符串格式化,性能会肉眼可见地下降。CButtonST 修正版里很多绘制优化做得不错,但你自己额外加的绘制逻辑要遵循同一个原则:能预计算的不要在绘制函数里做。
实际项目里我曾遇到过一个离奇问题:按钮绘制偶尔闪烁,后来排查发现是在 OnDrawItem 里调用了RedrawWindow,等于绘制过程触发自我重绘,直接死循环。后来把这个调用去掉,改为在状态变化时请求重绘,问题消失。这个教训比较典型,写在这里给你提个醒:自绘代码里不要主动触发跟当前绘制同一窗口的重绘函数,否则表现就是间歇性闪烁,时好时坏,很难排查。
CButtonST 这套东西说不上新,但它在一个相当长的周期里帮老项目解决按钮观感的问题,而且修正版能在 VS2012 下顺利编译,至少省去了你为老控件更换编译器的折腾。用它的核心不是看懂每一行绘制代码,而是掌握那几个设置接口、理解句柄生命周期、知道自绘依赖 BS_OWNERDRAW 样式。把这三点做好,按钮自绘这条路基本就通了。希望帮到你。
本文还有配套的精品资源,点击获取