65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.93/5 (68投票s)

2008年6月12日

CPOL

5分钟阅读

viewsIcon

415479

downloadIcon

30693

使用此类,您可以将 PNG 图像添加到按钮中,并自动获得高亮和灰度版本。

引言

本网站上有几个用户绘制按钮,但我找不到一个可以轻松支持带透明度的 PNG 文件的按钮,所以我创建了这个类。由于此类使用 GDI+,它实际上支持许多图像格式,但现在更好的高质量按钮是 PNG 而不是 ICO,这就是此处突出显示它的原因。

更新:我在我的 Style Toolkit 中有一个该类的扩展版本。如果您只想在按钮上使用图像,那么这个类可能更简单。

背景

GDI+ 是 Microsoft Windows SDK 的一部分,它需要在应用程序启动时进行初始化。如果您以前没有使用过 GDI+,请查看演示项目的源代码并阅读本文。

特点

  • 灰度图像
    • 该类将自动从加载的资源创建灰度图像。灰度图像将在按钮设置为禁用状态时显示。
  • 高亮图像
    • 该类将自动从加载的资源创建高亮图像。当鼠标悬停在按钮边界上时,将显示高亮图像。
  • 备用图像
    • 您可以选择添加备用图像。当通过函数调用设置时,或者当按钮被点击并且切换模式启用时,将显示备用图像。
  • 切换模式
    • 启用后,按钮在每次按下时会在标准图像和备用图像之间切换。
  • 按下状态
    • 按下按钮时,图像会向下和向右移动 1 像素。
  • 工具提示
    • 可以可选地添加工具提示。

页面顶部的图像显示了播放按钮在三种不同状态下的样子。从左到右分别是:正常、高亮和禁用。另外两个按钮只是更酷炫的例子。

从图片中可能不容易看出第二个按钮处于高亮状态。这是故意的,我不希望它显著改变图像。高亮状态只会稍微增加亮度和对比度。当您将鼠标悬停在其上时,它足够明显。此类优先考虑图像质量,因此它永远不会拉伸或缩小图像(这通常会降低质量),它只会绘制适合的部分。如果您需要调整图像大小,请使用 Photoshop 等图像编辑器。

下图显示了播放按钮在切换状态下的样子,您应该很容易理解为什么您会想要这样的功能。退出按钮处于高亮状态,并且在此图像中显示了工具提示。

GdipButton2.PNG

使用 GDI+ 没有性能损失(假设存在这样的损失),因为它仅在初始化期间使用。创建时,图像会被转换为位图,并在控件需要重绘时使用位图。

使用代码

步骤 1 - 将这些文件添加到您的项目中

步骤 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 日)
    • 首次发布。
© . All rights reserved.