LedButton 状态控件(拥有者绘制)






4.72/5 (36投票s)
只读按钮控件,用于显示 LED 状态(活动、多状态等)。
目录
- 动机
- 使用的环境
- 将 LED 按钮文件和资源添加到您的项目中
- 一个简单的 LED 按钮
- 一个多状态 LED 按钮
- 一个活动 LED 按钮
- 一个条件 LED 按钮
- 文本属性
- 工具提示
- 代码文档
- 提供的文件
- 免责声明
- 使用条款
- 变更日志
动机
编写此 CLedButton
控件的动机是需要一个 LED 控件,该控件能够
- 只读 LED 控件。
- 能够处理多于传统的两种状态。
- 能够指示“活动”(并在可控的超时后自动关闭)。
- 能够控制状态之间的过渡。(状态之间的过渡要由外部控制。)
- 使用从资源文件中加载的 LED 图标。
- 无闪烁控件。
两年前,我曾在此 CodeProject 和 SourceForge 上寻找一个静态控件或按钮控件,以满足我的五项要求,但未能找到,于是我决定自己做一个。
我从这个很棒的网站上发布的以下控件中获得了灵感
- 第一个候选是 CLedButton [^],由 Benjamin Mayrargue [^] 发布,这给了我使用复选框(以
CBbutton
作为基类)来构建我的控件的想法。 - 然后我看到了 Dynamic LED Control [^],由 VGirish [^] 发布,但其中一些我想要的功能也缺失了。
- Paolo Messina [^] 在他的 COddButton [^] 中给了我使用所有者绘制按钮的想法。
- 然后 Davide Calabro [^] 在他精彩的 CButtonST [^] 中为我提供了实现 LED 按钮控件所需的其余背景信息。
基于这些背景,我两年前开始定制我自己的 CLedButton
控件。从项目到项目,代码不断得到增强和简化,直到它变得成熟而健壮,可以在这里发布。
使用的环境
LED 按钮最初是使用 VC 6.0 (Visual Studio C++ 6.0) 开发的,现在我使用的是 VC 7.1 (Visual Studio C++ .NET 2003)。我将“*尝试*”提供适用于这两个版本的说明。
将 LED 按钮文件和资源添加到您的项目中
请确保您拥有以下文件
- 此控件的源代码:*LedButton.h* 和 *LedButton.cpp*,将它们复制到您的项目目录中。
- 您想使用的 LED 图标文件。(您可以使用我提供的演示中的图标。)将它们复制到您的项目资源目录(*res* 子目录)中。
在您的开发环境中打开您的项目,并按照以下步骤操作
- 将文件 *LedButton.h* 和 *LedButton.cpp* 添加到您的项目中。
- 使用资源编辑器,在您的对话框中添加一个复选框,将其 *ID* 更改为 *IDC_LED_CHECK*。您也可以更改此复选框的属性,相关的属性有:*左侧文本*、*多行*、*水平对齐*、*垂直对齐*、*右对齐文本*。
- 使用资源编辑器导入图标文件,分别将 *ID* 命名为 *IDI_GRAY_ICON*、*IDI_GREEN_ICON*、*IDI_YELLOW_ICON*、*IDI_RED_ICON* 和 *IDI_BLUE_ICON*。
- 使用类向导添加一个名为 *m_ledCtrl* 的成员变量,作为控件变量。(生成的类型将是 *CButton*,稍后将更改。)
- 在您的对话框头文件中,将 LED 按钮控件的类型从 *CButton* 更改为 *CLedButton*。
- 在同一个头文件中添加预处理器指令 *#include "LedButton.h"*。
对话框头文件应包含
... #include "LedButton.h" ... class DemoDlg : public CDialog { ... private: ... CLedButton m_ledCtrl; };
对话框实现文件应包含
void CDemoDlg::DoDataExchange(CDataExchange* pDX)
{
...
DDX_Control(pDX, IDC_LED_CHECK, m_ledCtrl);
...
}
现在是时候初始化和配置 LED 按钮了。
一个简单的 LED 按钮
默认情况下,CLedButton
控件创建时具有两个 LedState
,您只需要设置每个状态的图标,然后根据您的需要设置 LedState
。
为了方便起见,您可以定义一个枚举类型来描述每个状态。*OFF* 状态的整数值为零,*ON* 状态的值为 *1*,或者使用 Afx
的 *FALSE* 和 *TRUE* *#define*,或者使用 LED 按钮自己预定义的 *LED_BUTTON_STATE_OFF* 或 *LED_BUTTON_STATE_ON* 值。
将以下代码添加到您的 InitDialog()
方法中
... m_ledCtrl.SetIcons(IDI_GRAY_ICON, IDI_YELLOW_ICON); ...
LED 按钮的默认状态是 LED_BUTTON_STATE_OFF
。
如果您想更改图标的默认大小,则不能使用 *SetIcons()*,而是为每个 LED 状态调用其中一个 *SetIcon()* 方法
... m_ledCtrl.SetIcon(LED_BUTTON_STATE_OFF, IDI_GRAY_ICON, 14,14); m_ledCtrl.SetIcon(LED_BUTTON_STATE_ON, IDI_YELLOW_ICON, 14,14); ...
每次您想更改 LED 颜色时,只需调用 *SetLedState()* 方法并传入新的 LED 状态即可显示。
...
// Turn the Led ON
m_ledCtrl.SetLedState(LED_BUTTON_STATE_ON);
...
如果您不想要更复杂的 LED 按钮,以上就足够了。
如果 SetLedState()
方法是用与 LED 当前显示的 LED 状态相同的 LED 状态调用,则该方法不执行任何操作。(*这是为了减少闪烁*)
一个多状态 LED 按钮
多状态 LED 按钮与简单 LED 按钮类似,但具有两个以上的 LED 状态。
定义一个枚举类型来表示每个 LED 状态在逻辑上可能很有用,但这不是强制性的。
在本文件中,我们定义一个名为 EMyLedState
的枚举类型,用于多状态 LED 按钮,如下所示
... enum EMyLedState { GRAY_LED_STATE = LED_BUTTON_STATE_ON, // Initial LedState GREEN_LED_STATE, YELLOW_LED_STATE, RED_LED_STATE, BLUE_LED_STATE, MY_LED_STATES_SENTINEL // Not to be used as a EMyLedState. }; ...
MY_LED_STATES_SENTINEL
是我们将使用的 LED 状态的数量。
多状态 LED 按钮的初始化要求在设置每个状态的图标之前,调用 SetLedStatesNumber()
方法来设置 LED 状态的最大数量。
您的 InitDialog()
中的代码将变成
... m_ledCtrl.SetLedStatesNumber(MY_LED_STATES_SENTINEL); m_ledCtrl.SetIcon(GRAY_LED_STATE, IDI_GRAY_ICON); m_ledCtrl.SetIcon(GREEN_LED_STATE, IDI_GREEN_ICON); m_ledCtrl.SetIcon(YELLOW_LED_STATE, IDI_YELLOW_ICON); m_ledCtrl.SetIcon(RED_LED_STATE, IDI_RED_ICON); m_ledCtrl.SetIcon(BLUE_LED_STATE, IDI_BLUE_ICON); ...
设置状态与之前相同,但现在可能的值是从 GRAY_LED_STATE
到 BLUE_LED_STATE
。
一个活动 LED 按钮
活动 LED 按钮是一个 LED 按钮,它会在可控的时间段后自动关闭,从而消除了用户管理关闭时间的责任。
活动时间由一个内部计时器控制,该计时器在 LED 按钮设置为 *LED_BUTTON_STATE_OFF* 以外的任何状态时触发。当计时器超时时,LED 按钮将关闭。(*活动 LED 在计时器超时时结束的状态也可以控制。*)
由于计时器是有限的全局资源,要激活一个 LED 按钮,用户应提供一个 timerId
来标识计时器。这样,两个或多个活动 LED 按钮可以在同一个应用程序中共存,每个都有不同的计时器标识。
要取消激活活动计时器,只需提供一个 NULL
timerId
。计时器在 LED 按钮窗口销毁时也会被释放。
首先,您可能希望在一个地方管理您代码中使用的所有计时器,因此,在某个可见的头文件中定义计时器标识符
...
#define MY_ACTIVITY_LED_TIMER_ID (WM_USER + 0x123)
...
然后 InitDialog()
中的代码将变成
... // Simple Led with Activity detection m_ledCtrl.SetIcons(IDI_GRAY_ICON, IDI_YELLOW_ICON); m_ledCtrl.SetLedActivityTimer(MY_ACTIVITY_LED_TIMER_ID, 123); ...
上面的代码会将 LED 设置为活动 LED,持续时间为 123 毫秒。
一个 简单、多状态 或 条件 LED 按钮也可以是一个 活动 LED 按钮。
一个条件 LED 按钮
要理解条件 LED 状态,让我们看下面的情况
- LED 按钮控件能够显示两种以上状态。
- LED 按钮用作“*毛刺检测器*”。
- 一个线程将高速读取硬件数据,等待某个信号变化。当需要时,它将向 GUI 发送一个通知,告知该变化。
- 当变化“缓慢”时,LED 应该在“ON”和“OFF”之间正常切换。但是,如果两次连续的变化发生在 50 毫秒内,LED 应该以不同的颜色显示“ON”或“OFF”状态。
以下状态机可能提供更好的图景
当 LED 按钮状态依赖于两个(或更多)输入时,LED 按钮就变成了一个条件 LED。
对于“毛刺检测器”,输入是
- 当前的 LED 状态。
- 请求的新 LED 状态。
- 自上次 LED 更改以来的经过时间。
要设置条件 LED,您应该创建一个从 CLedStateCondition
类派生的对象(该类在 *LedButton.h* 头文件中定义)。
子类应重写 ChangeState()
虚拟方法。
ChangeState()
方法接收三个参数
newLedState
,请求的新LedState
。oldLedState
,过渡前的当前LedState
。isForcedChange
,一个布尔标志,指示强制过渡到newLedState
。派生类应固定其私有 FSM 以反映此强制更改,并返回此newLedState
。
ChangeState()
应包含所有决定 LED 按钮将进入的新 LED 状态的逻辑。(*我不会在此提供“毛刺检测器”的代码,因为我想专注于条件 LED。*)
您可以在某个头文件中定义以下枚举类型来处理 *毛刺检测器*
... enum EGlitchDetectorState { OFF_STATE = LED_BUTTON_STATE_ON, // Green LED, ON_STATE, // Yellow LED, GLITCH_OFF_STATE, // Blue LED, GLITCH_ON_STATE, // Red LED, IDLE_STATE, // No LED Icon, GLITCH_DETECTOR_STATES_SENTINEL // Not to be used as a EGlitchDetectorState. }; ...
硬件通知将分别以 TRUE
和 FALSE
的形式到来。
在本例中,CLedStateCondition
派生类的名称将是 CGlitchDetector
。在 CDemoDlg
中放入一个 CGlitchDetector
的实例,并将其命名为 *m_glitchDetector*。
您的对话框 InitDialog()
中的代码可以是
... // // 1) Initialize the m_glitchDetector as required (code not shown here) // 2) Set the led as a multi-state led. // 3) Add the Glitch Detector to the Led Button. // 4) Set the initial state as IDLE_STATE. (Force this state) // m_ledCtrl.SetLedStatesNumber(GLITCH_DETECTOR_STATES_SENTINEL); m_ledCtrl.SetIcon(OFF_STATE, IDI_GREEN_ICON); m_ledCtrl.SetIcon(ON_STATE, IDI_YELLOW_ICON); m_ledCtrl.SetIcon(GLITCH_ON_STATE, IDI_RED_ICON); m_ledCtrl.SetIcon(GLITCH_OFF_STATE, IDI_BLUE_ICON); m_ledCtrl.SetIcon(IDLE_STATE, 0,0,0); // No Icon on purpose. m_ledCtrl.SetLedStateCondition(&m_glitchDetector); m_ledCtrl.SetLedState(IDLE_STATE, true); // Force IDLE_STATE. ...
在接收硬件通知的代码中,我们将分别调用 SetLedState()
方法,传入 OFF_STATE
或 ON_STATE
的值。
afx_msg LONG CDemoDlg::OnHWUpdate(WPARAM wparam, LPARAM /*lparam*/)
{
...
LedState ledState = (TRUE == wparam) ? ON_STATE : OFF_STATE;
m_ledCtrl(ledState);
...
}
一个 简单、多状态 或 活动 LED 按钮也可以是一个 条件 LED 按钮。
文本属性
可以为每个 LED 状态更改文本前景色或/和按钮背景色。默认情况下,文本前景色是 ::GetSysColor(COLOR_BTNTEXT)
,按钮背景色是 ::GetSysColor(COLOR_BTNFACE)
。
以下方法控制 LED 状态的颜色
SetTextForeground()
:设置特定 LED 状态的文本前景色。
GetTextForeground()
:获取特定 LED 状态的文本前景色。
SetTextBackground()
:设置特定 LED 状态的文本背景色。
GetTextBackground()
:获取特定 LED 状态的文本背景色。
SetTextColors()
:设置特定 LED 状态的文本前景色和按钮背景色。
RestoreDefaultColors()
:将所有 LED 状态的默认颜色恢复,请谨慎使用。
工具提示
此 LED 按钮控件可以显示“工具提示”,这是一个小的弹出窗口,显示描述 LED 用途的文本。工具提示大部分时间是隐藏的,只有当用户将鼠标光标放在 LED 按钮上并停留半秒钟时才会出现。工具提示出现在光标附近,当用户单击鼠标按钮或将光标移出工具提示时消失。
以下方法控制工具提示
SetTooltipText()
:设置工具提示文本,可以来自资源字符串或提供的文本。
ActivateTooltip()
:激活/停用工具提示。
在当前版本中,仅支持“矩形”工具提示。
代码文档
代码包含 **Doxygen** 注释,用于从代码生成 HTML 文档。我强烈建议您直接或间接使用此类工具。
Doxygen 是一个用于 C++、C、Java、Objective-C、IDL(CORBA 和 Microsoft 版本)以及在一定程度上 PHP、C# 和 D 的文档系统。您可以在 Doxygen[^] 上找到它。
配套的 帮助 文件是使用 KingsTools [^] Visual Studio .NET 插件由 SteveKing [^] 生成的。这个*插件*包含许多对您可能感兴趣的实用工具,集成在您的开发环境中。(Doxygen、代码统计、语法着色等)
感谢 SteveKing 提供的这个工具,以及他在我的代码中使用的*免责声明*。
提供的文件
在您自己的项目中,可以在源代码和演示项目中提供的以下文件中找到
源文件 | LedButton.h 和 LedButton.cpp |
LED 图标文件 | 13 个不同颜色的 LED 图标。(LedButton_src.zip) |
灯泡文件 | LightBulbOff.ico、LightBulbOn.ico、LightBulbBroken.ico(在演示项目中) |
免责声明
此代码及附带文件按*“原样”*提供,不附带任何明示或暗示的保证。不对任何可能的损害或功能上的副作用负责。用户必须承担使用此代码的全部风险。作者对由此对您的计算机造成的任何损害、导致您的宠物生病、增加秃顶或在您启动汽车时导致汽车发出奇怪噪音概不负责。此代码没有 bug,只有未公开的功能!
使用条款
此代码可*免费*用于个人使用或免费应用程序。如果您计划将此代码用于商业或共享软件应用程序,我们礼貌地请求您联系作者以获得许可。
变更日志
日期 | 版本号 | 描述 |
2004/1/16 | 1.1 | 无闪烁更新(感谢 Iain Clarke[^]) |
2004/12/25 | 1.0 | 在 CodeProject 上首次发布 |
2002/8/16 | --- | Created |
结语
希望这个 LED 控件对您有用。
为了帮助 CodeProject 的其他用户,请*评价这篇文章*:)