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

一个更好的位图按钮类

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.46/5 (14投票s)

2000年5月17日

viewsIcon

405727

downloadIcon

9223

对 CBitmapButton 类的改进。

目录

概述

我一直不太喜欢 CBitmapButton 类。它有几个主要缺陷。

  • 它需要一种不自然的资源命名范式。
  • 它在多种屏幕分辨率下表现不佳。
  • 它要求程序员花费太多精力来创建按钮图像。

这个类对于“典型”情况来说,通用性和功能性都过于强大。因此,它使得简单情况和最通用情况一样困难。我不需要最通用情况,你可能也不需要。你只是想在按钮中显示一个有趣的位图,而不是一些无聊的文本。至于其他部分,你希望它能够正常工作,而无需你付出任何努力。

CBitmapButton 类确实很奇怪,它名义上要求你创建一到四个按钮图像,所有这些图像都经过精心设计,具有正确的边缘等,并用诸如 "ACTIOND"、"ACTIONU"、"ACTIONF" 和 "ACTIONX" 之类的时髦名称命名。当然,你实际上可以使用 ID,并自己调用 LoadBitmaps 方法,但你仍然必须创建这些位图。

你知道如何创建一个真正好看的 3D 边缘吗?这很棘手。当你必须创建一个在任何分辨率下都能工作的边缘时,就更棘手了。你知道如何创建一个在任何分辨率下都很好看的焦点矩形吗?如果你不想让按钮必须调整大小以适应位图呢?

但为什么要费心去担心这些问题呢?通常,当我创建一个位图按钮时,我想要一个看起来像普通按钮的按钮。它有适当的边缘,按下时有适当的边缘变化,并且有一个漂亮的焦点矩形。我唯一想要不同的就是位图。而且,我只想画一个,谢谢。

本文描述的 bitmap 类解决了所有这些问题。我有一段时间都在使用这段代码,但最近关于位图的问题数量促使我对其进行清理并将其整理成可发布的形式。

CImageButton: 一个智能位图按钮类

当控件获得焦点时,CImageButton 以凸起或按下状态绘制按钮,并在图像周围绘制一个焦点矩形。

这个类只需要一张位图,尽管你可以提供最多三张。你不需要第四张(焦点)位图,因为焦点矩形会自动绘制。

位图仅通过其资源 ID 指定。无需使用非标准命名约定。如果你想为不同的分辨率使用不同的位图,你可以在 OnInitDialog 处理器中调用 LoadBitmaps 并传入所需的位图 ID 来实现。

如果你只提供一张位图(理想情况),那么同一张位图将用于“向上”和“向下”状态,并在禁用时通过交替的灰色像素进行灰显。如果你不喜欢灰显算法,你可以为禁用状态提供一张你自己设计的明确的灰显图像。

同样,由于边缘和焦点都为你提供了,你通常不需要为“向下”状态提供特殊的位图。处理“向下”状态的唯一真正技巧是,焦点矩形和位图图像会向右下方移动,以保持按钮被按下的视觉错觉。

“对话框单位”问题呢?这个困扰着每一个尝试使用位图和对话框的人的噩梦。如果你不知道这个问题是什么,微软巧妙地决定对话框应该根据屏幕分辨率调整大小,所以一个在 640×480 下看起来合理的对话框在 1600×1200 下看起来也同样合理。实际上,这_是_一个好主意,但它的实现还有很多不足之处。最严重的缺陷是,任何使用位图的东西都会遇到大麻烦,因为位图总是以像素分辨率存在的。因此,你在 640×480 下非常漂亮的 16×16 位图在 1600×1200 下几乎看不见。在 640×480 下,它占屏幕的 1/40×1/30。在 1600×1200 下,它占屏幕的 1/100×1/75。它的大小缩小了 2.5 倍。

我通过提供一个“填充”按钮区域的位图图像来解决这个问题。这使用了 StretchBlt 函数来扩展位图。对于某些图像,这种方法效果很好;对于其他图像,它会产生非常难看的伪影,例如锯齿。但对于你用于按钮的大量简单图像,它是令人满意的,并且省去了在多个分辨率下创建位图的麻烦。

如果你想要一个不完全覆盖按钮边缘的图像,但你希望它能调整大小怎么办?只需创建一个更大的位图,并用标称的 3D 表面颜色(RGB(192, 192, 192),即“灰色”)填充位图。我使用 LR_LOADMAP3DCOLORS 加载位图,因此深灰色、灰色和浅灰色的实例都将被用户的当前显示方案设置替换。我使用 COLOR_3DFACE 来“灰显”图像,如果你没有提供自己的灰显图像,这样用户选择的配色方案将得到遵循。

如果你选择不使用“填充”功能,此类别尊重各种按钮样式,例如用于水平对齐的 BS_CENTERBS_LEFTBS_RIGHT,以及用于垂直对齐的 BS_VCENTERBS_TOPBS_BOTTOM

实际上,所有这些代码中唯一真正棘手的部分是处理所有奇怪的情况,例如如果位图大于按钮并且调用者指定了右对齐图像怎么办?(答案:图像在左侧被截断)。你可以阅读代码来发现所有其他奇怪的情况。

要创建图像按钮,您可以在对话框编辑器中创建一个按钮,并将其标记为所有者绘制。您提供的任何标题都将被忽略。然后,您进入类向导并为其创建一个类型为 CImageButton 的控件变量。请注意,在将 CImageButton 添加到项目后,您可能需要重建 _ .clw_ 文件。删除 _ .clw_ 文件并调用 ClassWizard,然后告诉它从现有项目文件重建该文件。

CImageButton 方法

我在 CImageBitmap 类中提供的是一组简单的、扩展了基本 CButton 类的方法。

void CImageBitmap::LoadBitmaps(UINT up, UINT down = 0, UINT disabled = 0)

您调用此方法来告诉按钮使用哪些图像。只需要提供 up 图像。其余是可选的,并根据下表在绘制时隐式提供

提供的位图

使用的位图

-- LoadBitmaps 参数--

-- 按钮状态 --

向上 向下 禁用 向上 向下 禁用
up 0 0 up up grayed(up)
up down 0 up down grayed(up)
up 0 disabled up up disabled
up down disabled up down disabled
void CImageBitmap::GetBitmaps(UINT &up, 
                              UINT &down, 
                              UINT &disabled)

这可用于检索上次 LoadBitmaps 调用设置的位图的资源 ID。

  • DWORD CImageButton::SetVPos(DWORD style)

    这可用于设置图像的垂直位置。它可以是 BS_TOPBS_BOTTOMBS_VCENTER 之一。它返回先前的垂直样式。

  • DWORD CImageButton::SetHPos(DWORD style)

    这可用于设置图像的水平位置。它可以是 BS_LEFT、BS_RIGHTBS_CENTER 之一。它返回先前的水平样式。

  • BOOL CImageButton::m_fill;

    此值由构造函数设置为 FALSE。如果您希望发生图像填充,可以将其显式设置为 TRUE

    请注意,如果您在 OnInitDialog 完成后更改此变量的值,则应在 CImageButton 对象上调用 InvaldiateRect 以强制其重绘。

    注意:你可能已经读过我关于随意使用愚蠢的 m_ 符号的批评;我甚至看到有人被它误导,以至于他们使用这种命名约定声明堆栈上的局部变量!但我使用它的一个地方是命名用于将信息从类外部传递到类实例的成员变量。这便是一个这样的例子。)

CToggleButton: 一个切换按钮类

仅仅因为有几个请求关于如何制作一个“切换”按钮,我创建了一个切换位图类,它也随此项目一起下载。在 _ToggleButton.cpp_ 和 _ToggleButton.h_ 中查找 CToggleButton 类。要创建切换按钮,您可以在对话框编辑器中创建一个按钮,添加一个标题,并将其标记为所有者绘制。然后,您进入类向导并为其创建一个类型为 CToggleButton 的控件变量。请注意,在将 CToggleButton 添加到项目后,您可能需要重建 _ .clw_ 文件。删除 _ .clw_ 文件并调用 ClassWizard,然后告诉它从现有项目文件重建该文件。

CToggleButton 方法

  • BOOL CToggleButton::GetState()

    此方法返回当前的按钮状态,如果按钮处于“开启”状态(图像被按下)则返回 TRUE,如果按钮处于“关闭”状态(图像未被按下)则返回 FALSE

  • BOOL CToggleButton::SetState(BOOL newState)

    这会设置状态。如果 newStateTRUE,则状态为“开”(按下),如果 newStateFALSE,则状态为“关”(未按下)。它返回按钮的先前状态。

  • DWORD CToggleButton::SetVPos(DWORD style)

    这可用于设置文本的垂直位置。它可以是 BS_TOPBS_BOTTOMBS_VCENTER 之一。它返回先前的垂直样式。

  • DWORD CToggleButton::SetHPos(DWORD style)

    这可用于设置文本的水平位置。它可以是 BS_LEFTBS_RIGHTBS_CENTER 之一。它返回先前的水平样式。

这些类的源文件以及我用于测试它们的项目可以通过点击页面顶部的链接直接下载。

Jeff Malett 的更新

  1. CImageButton 类得到了扩展,以支持切换功能,所以现在你可以拥有一个可切换的图像按钮。基本上,我是通过将 CToggleButton 类的代码合并到 CImageButton 类中来实现的,结果称之为 CImageToggleButton。按钮是否切换由一个新的类成员变量 m_toggle 控制,该变量可以随时更改。合并这两个类是 CodeProject 留言板上某人请求的功能。
  2. 我修改了 CImageToggleButtonCToggleButton,使其在切换时,背景会以当前设置的按钮高亮颜色绘制。这是切换按钮更标准的行为。
  3. 我扩展了对话框演示,展示了两个图像按钮,一个带切换功能,一个不带。
  4. 我扩展了对话框演示,展示了一个 MFC 文本切换按钮(你可以通过创建一个 checkbox 并赋予它“Push-like”属性来做到这一点),与 CToggleButton 并排显示。请注意,它们的行为略有不同,例如,当你点击一个已按下的切换按钮时,在你的版本中它在鼠标按下时完全释放,而在 MFC 中则在鼠标抬起时释放。我有点偏爱 MFC 版本的 UI,但 CToggleButton 更优越,因为文本可以以不同的方式对齐(尽管我不确定你为什么要这样做)。

这些文章中表达的观点是作者的观点,不代表,也不被微软认可。

您可以在下方留言,提出关于本文的问题或评论。
版权所有 © 1999。保留所有权利
www.flounder.com/mvp_tips.htm

许可证

本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。

作者可能使用的许可证列表可以在此处找到。

© . All rights reserved.