Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

LedButton Status Control (Owner-draw)

0.00/5 (No votes)
17 Jan 2005 1  
Read-Only button control to display LED statuses (activity, multi-state, etc.).

Sample Image

Table of Contents

Motivation

The motivation in writing this CLedButton control was having a LED control that is:

  • Read-Only LED control.
  • Capable of dealing with more than the traditional two-states.
  • Capable of signaling "activity" (to turn off automatically after a controllable timeout).
  • Capable to control the transition from one state to another. (The transition from one state into another to be externally controlled.)
  • Use LED icons loaded from resource files.
  • Flicker-free control.

Two years ago, I was looking for a static or button control here in CodeProject and in SourceForge that could meet my five requirements, but I failed to find one, so I decided to build one by myself.

I got the inspiration from the following controls as presented in this great site:

With this background, I started to tailor my own CLedButton control two years ago. From project to project, the code got enhanced and simplified, until it became mature and robust for being published here.

Environment used

The LED button was initially developed using VC 6.0 (Visual Studio C++ 6.0), and now I'm using VC 7.1 (Visual Studio C++ .NET 2003). I will "try" to provide instructions that are suitable for both versions.

Adding the LED button files and resources to your project

Make sure you got the following files:

  • The sources for this control: LedButton.h and LedButton.cpp, copy them to your project directory.
  • The LED icon files you want to use. (You may use the ones in the demo I'm providing.) Copy them to your project resources directory (res subdirectory).

Open your project in your developing environment and follow these steps:

  • Add the files LedButton.h and LedButton.cpp to your project.
  • Using the resource editor, add a checkbox to your dialog, change its ID to IDC_LED_CHECK. You may change the attributes of this checkbox as well, the relevant attributes are: Left text, Multiline, Horizontal alignment, Vertical alignment, Right aligned text.
  • Import the icon files using the resource editor, name the IDs as IDI_GRAY_ICON, IDI_GREEN_ICON, IDI_YELLOW_ICON, IDI_RED_ICON and IDI_BLUE_ICON, respectively.
  • Use the ClassWizard to add a member variable called m_ledCtrl as a control variable. (The type generated will be as CButton, this will be changed later on).
  • Change the type for the LED button control from CButton to CLedButton in your dialog header file.
  • Add the preprocessor #include "LedButton.h" to the same header file as well.

The dialog header file should contain:

...
#include "LedButton.h"

...
class DemoDlg : public CDialog
{
   ...
private:
   ...
   CLedButton m_ledCtrl;
};

The dialog implementation file should contain:

void CDemoDlg::DoDataExchange(CDataExchange* pDX)
{
   ...
   DDX_Control(pDX, IDC_LED_CHECK, m_ledCtrl);
   ...
}

Now is the time to initialize and configure the LED button.

A Simple LED Button

By default, a CLedButton control is created with two LedStates, you only need to setup the icons, one for each state, and then set the LedState according to your needs.

For your convenience, you may define an enumerated type that will be used to describe each state. The OFF state with the integer value of zero, and the ON state with the value of 1, or use the Afx FALSE and TRUE #defines, or use the LED button own predefined values LED_BUTTON_STATE_OFF or LED_BUTTON_STATE_ON, respectively.

Add the following code to your InitDialog() method:

    ...
    m_ledCtrl.SetIcons(IDI_GRAY_ICON, IDI_YELLOW_ICON);
    ...

The default state of the LED button is LED_BUTTON_STATE_OFF.

If you want to change the default size of the icon, you can't use the SetIcons() instead, call one of the SetIcon() methods for each LED state:

    ...
    m_ledCtrl.SetIcon(LED_BUTTON_STATE_OFF, IDI_GRAY_ICON, 14,14);
    m_ledCtrl.SetIcon(LED_BUTTON_STATE_ON, IDI_YELLOW_ICON, 14,14);
    ...

Every time you want to change the LED color, just call the method SetLedState() with new LED state to display.

    ...
    // Turn the Led ON

    m_ledCtrl.SetLedState(LED_BUTTON_STATE_ON);
    ...

That is all if you don't want a more sophisticated LED button.

No action is done by SetLedState() if this method is called with a LED state that is the same as the one that the LED is currently displaying. (This is done to reduce flickering)

A Multi-State LED Button

A Multi-State LED button is like a Simple LED, but with more than two LED states.

It may be logical to define an enumerated type that will represent each LED state, although this is not mandatory.

In this document, let's define an enumerated type called EMyLedState that will serve for Multi-State LED buttons, as follows:

    ...
    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.

    };
    ...

The MY_LED_STATES_SENTINEL is the number of LED states that we will use.

The initialization of Multi-State LED buttons require that the method SetLedStatesNumber() should be called for setting the maximum number of LED states, before setting up the Icons for each state.

The code in your InitDialog() becomes:

    ...
    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);
    ...

Setting the state is the same as before, but now the possible values are from GRAY_LED_STATE to BLUE_LED_STATE.

An Activity LED Button

An Activity LED button is a LED button that automatically turns off after a controllable period, removing in this way the user responsibility of managing the turn off time.

The activity time is controlled by an internal timer that is triggered when the LED button is set to any state other than LED_BUTTON_STATE_OFF. When the timer ages, the LED button is turned off. (The LED state the Activity LED will end when the timer ages can also be controlled.)

Because timers are a limited global resource, to activate a LED button, the user should provide a timerId that will identify the timer. In this way, two or more Activity LED buttons can coexist in the same application, each one with a different timer identification.

To deactivate an Activity timer, just provide a NULL timerId. The timer is also released when the LED button window is destroyed.

First, you may want to manage all the timers you use on your code in a single place, therefore, define in some visible header file, the timer identifier:

    ...
    #define MY_ACTIVITY_LED_TIMER_ID         (WM_USER + 0x123)
    ...

Then the code in the InitDialog() becomes:

    ...
    // Simple Led with Activity detection

    m_ledCtrl.SetIcons(IDI_GRAY_ICON, IDI_YELLOW_ICON);
    m_ledCtrl.SetLedActivityTimer(MY_ACTIVITY_LED_TIMER_ID, 123);
    ...

The code above will set the LED as an Activity LED with a duration of 123 milliseconds.

A Simple, Multi-State or Conditional LED button can also be an Activity LED button.

A Conditional LED Button

To understand Conditional LED states, let's look at the following situation:

  • The LED button control is capable of displaying more than two states.
  • The LED button is used as a glitch detector.
  • A thread will be reading some data from the hardware at high speed, waiting for some signal to change. When required, it will send a notification towards the GUI with the change notification.
  • The LED should change its color normally between "ON" and "OFF" when the changes are "slow" changes. But if two consecutive changes occur in less than 50 msec, the LED should show the "ON" or "OFF" states with a different color.

The following state machine may provide a better picture:

Glitch Detector

When a LED button state depends on two (or more) inputs, then the LED button becomes a Conditional LED.

For the "Glitch Detector", the inputs are:

  • Current LED state.
  • Requested new LED state.
  • Elapsed time since the last LED change.

For setting up a Conditional LED, you should create a derived object from a class that is derived from CLedStateCondition (a class defined in the LedButton.h header file).

The subclass should overwrite the ChangeState() virtual method.

The ChangeState() method receives three parameters:

  • newLedState, the requested new LedState.
  • oldLedState, the current LedState before the transition.
  • isForcedChange, a boolean flag that instructs a forced transition to the newLedState. The derived class should fix its private FSM to reflect this forced change, and return this newLedState.

The ChangeState() should contain all the logic for deciding about the new LED state the LED button will go. (I will not provide code here for the Glitch Detector, due that I want to focus on the Conditional LED.)

You may define the following enumerated type for dealing with the Glitch Detector in some header file:

    ...
    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.

    };
    ...

The hardware notifications will come as TRUE and FALSE respectively.

For this example, the name of the CLedStateCondition derived class will be CGlitchDetector. Put an instance of this CGlitchDetector in the CDemoDlg and call it m_glitchDetector.

The code on your dialog InitDialog() could be:

    ...
    //

    // 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.

    ...

In the code that receives the hardware notifications, we will call the SetLedState() method with the OFF_STATE or ON_STATE values, respectively.

afx_msg LONG CDemoDlg::OnHWUpdate(WPARAM wparam, LPARAM /*lparam*/)
{
    ...
    LedState ledState = (TRUE == wparam) ? ON_STATE : OFF_STATE;
    m_ledCtrl(ledState);
    ...
}

A Simple, Multi-State or Activity LED button can also be a Conditional LED button.

Text Attributes

It is possible to change the text foreground color or/and button background color for each LED state. By default, the text foreground color is ::GetSysColor(COLOR_BTNTEXT) and the button background color is ::GetSysColor(COLOR_BTNFACE).

The following methods control the LED state colors:

  • SetTextForeground():

    Set the text foreground color for a specific LED state.

  • GetTextForeground():

    Get the text foreground color for a specific LED state.

  • SetTextBackground():

    Set the text background color for a specific LED state.

  • GetTextBackground():

    Get the text background color for a specific LED state.

  • SetTextColors():

    Set both text foreground and button background colors for a specific LED state.

  • RestoreDefaultColors():

    Restore the default colors for all LED states, use with care.

ToolTips

This LED button control can show a "tool tip", a small pop-up window that displays some text describing the purpose of the LED. The tooltip is hidden most of the time, appearing only when the user puts the cursor over the LED button and leaves it there for half a second. The tool tip appears near the cursor and disappears when the user clicks a mouse button or moves the cursor off the tool.

The following methods control tooltips:

  • SetTooltipText():

    Set the tooltip text, from a resource string or from the provided text.

  • ActivateTooltip():

    Activate/deactivate the tooltip.

On the current version, only "rectangular" tool tips are supported.

Code Documentation

The code contains Doxygen comments, used to generate HTML documentation out of the code. I strongly advice you to use such tools, directly or indirectly.

Doxygen is a documentation system for C++, C, Java, Objective-C, IDL (CORBA and Microsoft flavors), and to some extent, PHP, C# and D. It can be found at Doxygen[^].

The companion help file was generated using the KingsTools [^] Visual Studio .NET add-in by SteveKing [^]. This add-in contains several useful tools you may like to have integrated in your developing environment. (Doxygen, code-statistics, syntax coloring, etc.)

Thanks to SteveKing for this tool, and his disclaimer note I'm using in my code.

Provided Files

The following files are provided in the sources and demo project that you can use on your own projects:

Source files: LedButton.h and LedButton.cpp
LED icon files: 13 Led Icons with different colors. (LedButton_src.zip)
Light bulb files: LightBulbOff.ico, LightBulbOn.ico, LightBulbBroken.ico (in the demo project)

Disclaimer

This code and the accompanying files are provided "as is" with no expressed or implied warranty. No responsibilities for possible damages, or side effects in its functionality. The user must assume the entire risk of using this code. The author accepts no liability if it causes any damage to your computer, causes your pet to fall ill, increases baldness or makes your car start emitting strange noises when you start it up. This code has no bugs, just undocumented features!.

Terms of use

This code is free for personal use, or freeware applications. If you plan to use this code in a commercial or shareware application, you are politely asked to contact the author for his permission.

ChangeLog

Date Rev. Description
2004/Jan/16 1.1 Flicker-free update (Thanks to Iain Clarke[^])
2004/Dec/25 1.0 Debut in CodeProject
2002/Aug/16 --- Created

Epilogue

Hope that this LED control is useful for you.

To help other users in CodeProject, please Rate this Article :)

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here