Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WPF

WPF LED User Control

2.50/5 (5 votes)
12 Jun 2018CPOL1 min read 20.7K   964  
This article proposes a way to create a WPF LED UserControl which works from any number of bitmaps, each of them corresponding to a LED state (color in fact).

Introduction

I saw many articles about WPF LED Control, but I never found one which is as simple as this one ;-D

LEDControl UserControl Principle

This is the WPF LEDControl code: we can see that the LEDState enum type contains all "states" which corresponds in fact to colors. For each of these states, an image resource should be added: LED-gray.png image for gray LEDState, LED-green.png for green state, etc. You can imagine as many states as you need !

C#
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace CommonGuiWpf.Controls
{
    public enum LEDState
    {
        gray,
        green,
        red,
        yellow,
        // ... Etc ... As you want !

        unknown,
    }

    /// <summary>
    /// Interaction logic for LEDControl.xaml
    /// LEDControl is a WPF UserControl which permits to manage a LED with as many states as needed...
    ///  Each state is represented with a color
    /// </summary>
    public partial class LEDControl : UserControl, INotifyPropertyChanged
    {
        private const string IMAGES_LOCATION = "/CommonGuiWpf;component/Images/";
        private const string LED_IMAGES_PREFIX = "LED-";
        private const string IMAGES_EXTENSION = ".png";

        #region INotifyPropertyChanged

        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged([CallerMemberName] string name = "")
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(name));
            }
        }

        #endregion

        public LEDControl()
        {
            InitializeComponent();
        }

        static private void LEDControl_PropertyChanged
                 (DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            LEDControl lc = d as LEDControl;
            if (lc != null)
                lc.RaisePropertyChanged("ImageResName");
        }

        public static readonly DependencyProperty StateProperty =
           DependencyProperty.Register("State", typeof(LEDState), 
           typeof(LEDControl), new UIPropertyMetadata(LEDState.gray, LEDControl_PropertyChanged));

        public LEDState State
        {
            get { return (LEDState)GetValue(StateProperty); }
            set
            {
                SetValue(StateProperty, value);
                RaisePropertyChanged("ImageResName");
            }
        }

        public string ImageResName
        {
            get
            {
                return IMAGES_LOCATION + LED_IMAGES_PREFIX + State.ToString() + IMAGES_EXTENSION;
            }
        }
    }

And the corresponding XAML code is this one:

XML
<UserControl x:Class="CommonGuiWpf.Controls.LEDControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Simudiag.Common.Controls"
             mc:Ignorable="d" 
             d:DesignHeight="24" d:DesignWidth="24"
             Width="24" Height="24"
             DataContext="{Binding RelativeSource={RelativeSource Self}}">
    <Grid>
        <Image Source="{Binding ImageResName}" />
    </Grid>
</UserControl>

LEDControl Use

This is an example of another XAML UserControl (called SeveralLEDsControl) which is using it (excuse me if it doesn't work as is, I just copied a piece of my real UserControl just to show an example of the use):

XML
<UserControl x:Class="Example.SeveralLEDsControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Simulator.Module.Telecodage"
            xmlns:cguiwpfctrls="clr-namespace:CommonGuiWpf.Controls;assembly=CommonGuiWpf"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="1300"
             x:Name="SeveralLEDsControl">
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <cguiwpfctrls:LEDControl Grid.Row="0" Grid.Column="0" Margin="2" 
        State="{Binding LED1State, ElementName=SeveralLEDsControl}"/>
        <TextBlock Text="{DynamicResource LED1Text}" VerticalAlignment="Center" 
        Grid.Row="0" Grid.Column="1" Margin="2"/>
        <cguiwpfctrls:LEDControl Grid.Row="1" Grid.Column="0" Margin="2" 
        State="{Binding LED2State, ElementName=SeveralLEDsControl}}"/>
        <TextBlock Text="{DynamicResource LED2Text}" VerticalAlignment="Center" 
        Grid.Row="1" Grid.Column="1" Margin="2"/>
        <cguiwpfctrls:LEDControl Grid.Row="2" Grid.Column="0" Margin="2" 
        State="{Binding LED3State, ElementName=SeveralLEDsControl}}"/>
        <TextBlock Text="{DynamicResource LED3Text}" VerticalAlignment="Center" 
        Grid.Row="2" Grid.Column="1" Margin="2"/>
    </Grid>
</UserControl>

In this example, LEDControl State is binded with the original property type (LEDState): Corresponding code:

C#
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using CommonGuiWpf.Controls;

namespace Example
{
    /// <summary>
    /// Interaction logic for TelecodageModuleView.xaml
    /// </summary>
    public partial class SeveralLEDsControl : UserControl, INotifyPropertyChanged
    {
        #region INotifyPropertyChanged

        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged([CallerMemberName] string name = "")
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(name));
            }
        }

        #endregion
        
        public SeveralLEDsControl()
        {
            InitializeComponent();
        }

        protected LEDState _LED1State
        LEDState LED1State
        {
            get { return _LED1State; }
            set
            {
                if (_LED1State != value)
                {
                    _LED1State = value;
                    RaisePropertyChanged();
                }
            }
        }

        protected LEDState _LED2State
        LEDState LED2State
        {
            get { return _LED2State; }
            set
            {
                if (_LED2State != value)
                {
                    _LED2State = value;
                    RaisePropertyChanged();
                }
            }
        }
        
        protected LEDState _LED3State
        LEDState LED3State
        {
            get { return _LED3State; }
            set
            {
                if (_LED3State != value)
                {
                    _LED3State = value;
                    RaisePropertyChanged();
                }
            }
        }
    }

Bind the LEDControl on nullable bool Properties to Simulate OK, KO or OFF

Often, a LED is used to show "OK" (green), "KO" (red), or "OFF" (gray)... That was my need, and so I wrote a WPF Converter to be able to bind a nullable bool on my LEDControl UserControl. This is the code of the converter:

C#
/// <summary>
/// This converter can be used to bing a LEDControl on a nullable bool (bool?)
///   - When the nullable bool is set to null, LED will stay gray
///   - When the nullable bool is set to true, LED will become green
///   - When the nullable bool is set to false, LED will become red
/// </summary>
public class NullableBool2LEDStateConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter,
            System.Globalization.CultureInfo culture)
    {
        if (value == null)
            return LEDState.gray;
        if (value is bool?)
        {
            if (((bool?)value).Value == true)
                return LEDState.green;
            else
                return LEDState.red;
        }
        return LEDState.unknown;
    }

    public object ConvertBack(object value, Type targetType, object parameter,
            System.Globalization.CultureInfo culture)
    {
        if (value is LEDState)
        {
            if ((LEDState)value == LEDState.gray)
                return null;
            else if ((LEDState)value == LEDState.green)
                return true;
            else if ((LEDState)value == LEDState.red)
                return false;
        }
        return null;
    }
}

That way, the use of the LED control can be realized by this new UserControl (binding is using the converter):

XML
<UserControl x:Class="Example.SeveralLEDsNullableBoolControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:Simulator.Module.Telecodage"
            xmlns:cguiwpfctrls="clr-namespace:CommonGuiWpf.Controls;assembly=CommonGuiWpf"
             mc:Ignorable="d" 
             d:DesignHeight="300" d:DesignWidth="1300"
             x:Name="SeveralLEDsControl">
    <Grid>
        <Grid.Resources>
            <cguiwpfctrls:NullableBool2LEDStateConverter x:Key="NullableBool2LEDStateConverter" />
        </Grid.Resources>
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
            <RowDefinition Height="Auto" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="Auto" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>
        <cguiwpfctrls:LEDControl Grid.Row="0" Grid.Column="0" Margin="2" 
        State="{Binding LED1State, ElementName=SeveralLEDsControl, 
        Converter={StaticResource NullableBool2LEDStateConverter}}"/>
        <TextBlock Text="{DynamicResource LED1Text}" VerticalAlignment="Center" 
        Grid.Row="0" Grid.Column="1" Margin="2"/>
        <cguiwpfctrls:LEDControl Grid.Row="1" Grid.Column="0" Margin="2" 
        State="{Binding LED2State, ElementName=SeveralLEDsControl, 
        Converter={StaticResource NullableBool2LEDStateConverter}}"/>
        <TextBlock Text="{DynamicResource LED2Text}" VerticalAlignment="Center" 
        Grid.Row="1" Grid.Column="1" Margin="2"/>
        <cguiwpfctrls:LEDControl Grid.Row="2" Grid.Column="0" Margin="2" 
        State="{Binding LED3State, ElementName=SeveralLEDsControl, 
        Converter={StaticResource NullableBool2LEDStateConverter}}"/>
        <TextBlock Text="{DynamicResource LED3Text}" VerticalAlignment="Center" 
        Grid.Row="2" Grid.Column="1" Margin="2"/>
    </Grid>
</UserControl>

With this code (property types are nullable bool) :

C#
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using CommonGuiWpf.Controls;

namespace Example
{
    /// <summary>
    /// Interaction logic for SeveralLEDsNullableBoolControl.xaml
    /// </summary>
    public partial class SeveralLEDsNullableBoolControl : UserControl, INotifyPropertyChanged
    {
        #region INotifyPropertyChanged

        public event PropertyChangedEventHandler PropertyChanged;

        private void RaisePropertyChanged([CallerMemberName] string name = "")
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(name));
            }
        }

        #endregion
        
        public SeveralLEDsControl()
        {
            InitializeComponent();
        }

        protected bool? _LED1State
        bool? LED1State
        {
            get { return _LED1State; }
            set
            {
                if (_LED1State != value)
                {
                    _LED1State = value;
                    RaisePropertyChanged();
                }
            }
        }

        protected bool? _LED2State
        bool? LED2State
        {
            get { return _LED2State; }
            set
            {
                if (_LED2State != value)
                {
                    _LED2State = value;
                    RaisePropertyChanged();
                }
            }
        }
        
        protected bool? _LED3State
        bool? LED3State
        {
            get { return _LED3State; }
            set
            {
                if (_LED3State != value)
                {
                    _LED3State = value;
                    RaisePropertyChanged();
                }
            }
        }
    }

Demo Screenshot

This is what you obtain when using the example with the 3 LEDs:

History

  • 12th June, 2018: Article creation

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)