支持透明 PNG 文件的用户绘制按钮,适用于 Visual C++ 6.0 和 VS2005






4.93/5 (68投票s)
使用此类,您可以将 PNG 图像添加到按钮中,并自动获得高亮和灰度版本。
引言
本网站上有几个用户绘制按钮,但我找不到一个可以轻松支持带透明度的 PNG 文件的按钮,所以我创建了这个类。由于此类使用 GDI+,它实际上支持许多图像格式,但现在更好的高质量按钮是 PNG 而不是 ICO,这就是此处突出显示它的原因。
更新:我在我的 Style Toolkit 中有一个该类的扩展版本。如果您只想在按钮上使用图像,那么这个类可能更简单。
背景
GDI+ 是 Microsoft Windows SDK 的一部分,它需要在应用程序启动时进行初始化。如果您以前没有使用过 GDI+,请查看演示项目的源代码并阅读本文。
特点
- 灰度图像
- 该类将自动从加载的资源创建灰度图像。灰度图像将在按钮设置为禁用状态时显示。
- 高亮图像
- 该类将自动从加载的资源创建高亮图像。当鼠标悬停在按钮边界上时,将显示高亮图像。
- 备用图像
- 您可以选择添加备用图像。当通过函数调用设置时,或者当按钮被点击并且切换模式启用时,将显示备用图像。
- 切换模式
- 启用后,按钮在每次按下时会在标准图像和备用图像之间切换。
- 按下状态
- 按下按钮时,图像会向下和向右移动 1 像素。
- 工具提示
- 可以可选地添加工具提示。
页面顶部的图像显示了播放按钮在三种不同状态下的样子。从左到右分别是:正常、高亮和禁用。另外两个按钮只是更酷炫的例子。
从图片中可能不容易看出第二个按钮处于高亮状态。这是故意的,我不希望它显著改变图像。高亮状态只会稍微增加亮度和对比度。当您将鼠标悬停在其上时,它足够明显。此类优先考虑图像质量,因此它永远不会拉伸或缩小图像(这通常会降低质量),它只会绘制适合的部分。如果您需要调整图像大小,请使用 Photoshop 等图像编辑器。
下图显示了播放按钮在切换状态下的样子,您应该很容易理解为什么您会想要这样的功能。退出按钮处于高亮状态,并且在此图像中显示了工具提示。
使用 GDI+ 没有性能损失(假设存在这样的损失),因为它仅在初始化期间使用。创建时,图像会被转换为位图,并在控件需要重绘时使用位图。
使用代码
步骤 1 - 将这些文件添加到您的项目中
- GdipButton.h
- GdipButton.cpp
- MemDC.h
- CGdiPlusBitmap.h
- CMemDC 来自 Keith Rule,而 CGdiPlusBitmap.h 来自 Joe Woodbury。这些文件已包含在源代码和演示包中。
步骤 2 - 添加资源、成员变量和图像
使用资源编辑器将一个按钮添加到您的对话框中,设置资源 ID,然后在“Caption”框中删除文本。您可以将样式设置为“Owner Draw”,但您无需这样做,因为代码会自动设置此样式。
使用类向导,为您刚刚创建的 ID 添加一个变量。在此示例中,我将 ID 设置为 IDC_PLAY
,将变量名设置为 m_cPlay
。编辑对话框的 *.h 文件,并将控件从 CButton
更改为 CGdiButton
。不要忘记包含文件“GdipButton.h”。
在资源编辑器中,从 res 文件夹导入 *.png 文件,并将资源类型设置为 PNG。使用 PNG 仅是约定,它可以是任何名称,只要代码与您命名的名称匹配即可。右键单击 IDR_PNG1
,选择“Properties”,然后将其重命名为一个有用的名称,在此示例中为 IDR_PLAY
。
步骤 3 - 添加 LoadStdImage() 函数
现在,我们只需要在初始化时将图像加载到按钮中。在 OnInitDialg()
函数中,在底部附近添加以下代码
BOOL CTestGdipButtonDlg::OnInitDialog() { CDialog::OnInitDialog(); /// a bunch of stuff here m_cPlay.LoadStdImage(IDR_PLAY, _T("PNG")); return TRUE; }
步骤 4 - 构建并运行
您现在应该可以运行它并看到您的新 PNG 按钮了!如果在这里崩溃,很可能是因为您没有初始化 GDI+。回顾本文的“背景”部分。
演示项目
这是在测试程序中创建按钮所需的所有代码
// load the standard image and alternate image m_cPlay.LoadStdImage(IDR_PLAY, _T("PNG")); m_cPlay.LoadAltImage(IDR_PAUSE, _T("PNG")); m_cPlay.EnableToggle(TRUE); // just to show highlight state for article m_cPlayHi.LoadStdImage(IDR_PLAY, _T("PNG")); // set as disabled m_cPlayDis.LoadStdImage(IDR_PLAY, _T("PNG")); m_cPlayDis.EnableButton(FALSE); // show a larger button type m_cGear.LoadStdImage(IDR_GEAR, _T("PNG")); // replace the OK button with something m_cShutDn.LoadStdImage(IDR_EXIT, _T("PNG")); m_cShutDn.SetToolTipText(_T("Close Program"));
VC6 和 VS2005 版本都包含在演示项目中。
透明图像问题
按钮控件根本不知道它下面的背景应该是什么;它从与当前 DC 关联的位图中获取此信息。在大多数情况下,这都可以正常工作,背景是控件绘制之前的屏幕上的内容。但是,应用程序启动时可能不是最顶层的。像任务管理器这样的始终置顶应用程序可能会挡在前面,因此当它获取背景图像时,它就是错误的数据。通过由实际创建背景的代码调用 SetBkGnd()
函数来解决此问题。
在父级的 OnEraseBkgnd()
函数中设置所有按钮的背景。演示程序使用以下代码执行此操作
BOOL CTestGdipButtonDlg::OnEraseBkgnd(CDC* pDC) { CDialog::OnEraseBkgnd(pDC); CRect rect; GetClientRect(rect); CMemDC pDevC(pDC, rect); // fill in the back ground with something SetButtonBackGrounds(pDevC); return TRUE; } void CTestGdipButtonDlg::SetButtonBackGrounds(CDC *pDC) { m_cPlay.SetBkGnd(pDC); m_cPlayHi.SetBkGnd(pDC); m_cPlayDis.SetBkGnd(pDC); m_cShutDn.SetBkGnd(pDC); }
由于传递的 DC 是内存 DC,因此其他应用程序在做什么无关紧要。上面的代码假定您想使用默认的丑陋背景以外的内容;否则,您很可能会只使用默认的丑陋按钮。
VC6 构建问题
VC6 需要一些额外的东西才能正确编译,请将以下内容添加到您的 stdafx.h 文件中。还将 SDK 的 include 和 lib 路径添加到您的环境中。
// VC6 #if defined(_MSC_VER) && _MSC_VER == 1200 #ifndef ULONG_PTR #define ULONG_PTR unsigned long* #endif #include <Specstrings.h> #include <gdiplus.h> #pragma comment(lib, "gdiplus.lib") using namespace Gdiplus; // VS2005 #else #include <gdiplus.h> #pragma comment(lib, "gdiplus.lib") using namespace Gdiplus; #endif
历史
- 版本 1.0 (2008 年 6 月 10 日)
- 首次发布。