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

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

2005年1月7日

CPOL

10分钟阅读

viewsIcon

194030

downloadIcon

15931

只读按钮控件,用于显示 LED 状态(活动、多状态等)。

Sample Image

目录

动机

编写此 CLedButton 控件的动机是需要一个 LED 控件,该控件能够

  • 只读 LED 控件。
  • 能够处理多于传统的两种状态。
  • 能够指示“活动”(并在可控的超时后自动关闭)。
  • 能够控制状态之间的过渡。(状态之间的过渡要由外部控制。)
  • 使用从资源文件中加载的 LED 图标。
  • 无闪烁控件。

两年前,我曾在此 CodeProjectSourceForge 上寻找一个静态控件或按钮控件,以满足我的五项要求,但未能找到,于是我决定自己做一个。

我从这个很棒的网站上发布的以下控件中获得了灵感

基于这些背景,我两年前开始定制我自己的 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_STATEBLUE_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”状态。

以下状态机可能提供更好的图景

Glitch Detector

当 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.
    };
    ...

硬件通知将分别以 TRUEFALSE 的形式到来。

在本例中,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_STATEON_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.hLedButton.cpp
LED 图标文件 13 个不同颜色的 LED 图标。(LedButton_src.zip
灯泡文件 LightBulbOff.icoLightBulbOn.icoLightBulbBroken.ico(在演示项目中)

免责声明

此代码及附带文件按*“原样”*提供,不附带任何明示或暗示的保证。不对任何可能的损害或功能上的副作用负责。用户必须承担使用此代码的全部风险。作者对由此对您的计算机造成的任何损害、导致您的宠物生病、增加秃顶或在您启动汽车时导致汽车发出奇怪噪音概不负责。此代码没有 bug,只有未公开的功能!

使用条款

此代码可*免费*用于个人使用或免费应用程序。如果您计划将此代码用于商业或共享软件应用程序,我们礼貌地请求您联系作者以获得许可。

变更日志

日期 版本号 描述
2004/1/16 1.1 无闪烁更新(感谢 Iain Clarke[^])
2004/12/25 1.0 在 CodeProject 上首次发布
2002/8/16 --- Created

结语

希望这个 LED 控件对您有用。

为了帮助 CodeProject 的其他用户,请*评价这篇文章*:)

© . All rights reserved.