Login


An LCD Control

By Jonathan Wood on 12/24/2010 (Updated on 12/31/2010)
Language: C#
Technology: WinForms
Platform: Windows
License: CPOL
Views: 14,951
Desktop Development » Controls » User Controls » An LCD Control

Demo Project Screenshot

Download Project Source Code Download Project Source Code

Download Additional LCD Bitmaps Download Additional LCD Bitmaps

Introduction

Sometimes we just want to give our WinForms applications a new look. And sometimes giving our application a new look is complex, and other times it's easy.

This article presents a very simple user control that emulates the look of an LCD display.

The LCDDisplay User Control

Listing 1 shows the code for my LCDDisplay user control. As you can see, it's a pretty simple control.

You call the SetImage() method to provide the control with a bitmap, and a string that maps characters to parts of the bitmap. For example, consider the following bitmap.

A Sample Character Bitmap

This bitmap contains 13 characters. The character list string in this case could be ". 0123456789:". This string tells the control how many characters are in the bitmap, and how to map those characters. Note that each character in the bitmap must be the same width for this to work correctly.

You can then set the Text property and the control will attempt to map the characters in the Text property to the bitmap. If any character does not exist in the character list, that character will be represented by the first character in the bitmap. In the example above, I've made the first character an invalid character that would alert you to the fact that a character could not be mapped. In my character list string, I mapped a period to the first character. This is not really important. I just used a character that is not otherwise supported by the string.

Listing 1: The LCDDisplay User Control

using System;
using System.Drawing;
using System.Windows.Forms;

namespace SoftCircuits
{
    public partial class LCDDisplay : UserControl
    {
        protected Image _image;
        protected string _charList;

        /// <summary>
        /// Text to display in the control. Characters that do not match any in
        /// the character list will be displayed using the first character in
        /// the character list.
        /// </summary>
        public override string Text
        {
            get
            {
                return base.Text;
            }
            set
            {
                base.Text = value;
                Refactor();
            }
        }

        public LCDDisplay()
        {
            InitializeComponent();

            // Initialize members
            _image = null;
            _charList = null;
            Text = String.Empty;
        }

        /// <summary>
        /// Sets the current image and character map. The number of characters
        /// in charList must exactly match the number of digits in the bitmap
        /// for characters to be displayed properly.
        /// </summary>
        /// <param name="image"></param>
        /// <param name="charList"></param>
        public void SetImage(Image image, string charList)
        {
            _image = image;
            _charList = charList;
            Refactor();
        }

        /// <summary>
        /// Redraws and possibly resizes the control based on the current
        /// settings.
        /// </summary>
        private void Refactor()
        {
            // Don't do anything if no character image
            if (_image == null || _charList == null)
                return;

            // Resize control to fit image
            if (_image.Width > 0 && _image.Height > 0 && _charList.Length > 0 && Text.Length > 0)
                SetClientSizeCore(Text.Length * _image.Width / _charList.Length, _image.Height);

            // Redraw
            Invalidate();
        }

        // Paints control
        private void LCDDisplay_Paint(object sender, PaintEventArgs e)
        {
            // Don't do anything if no character image
            if (_image == null || _charList == null)
                return;

            // Determine dimensions of each character cell
            int cellWidth = _image.Width / _charList.Length;
            Rectangle rect = new Rectangle(0, 0, cellWidth, _image.Height);

            // Draw cells one at a time
            foreach (char c in Text)
            {
                // Get offset for this character
                int cx = Math.Max(_charList.IndexOf(c), 0) * cellWidth;

                // Draw character cell
                e.Graphics.DrawImage(_image, rect, cx, 0, cellWidth,
                    _image.Height, GraphicsUnit.Pixel);

                // Bump position
                rect.Offset(cellWidth, 0);
            }
        }
    }
}

The code that brings everything together is in the Paint event handler. This code starts by determining the size of each character in the bitmap. It then loops through each character in the Text property, mapping them to the bitmap. For each character, it displays only the corresponding part of the bitmap to the current location in the control's window.

I should also point out that the control will resize itself so that it will exactly fit the characters displayed. This gives the control a much cleaner look, but it can also cause problems when a control decides what size it wants to be. If this doesn't work for you, just remove the code in the Refactor() method that resizes the control.

I should also note that I set the user control's DoubleBuffered property to true. Although it uses a few more resources, this is a simple way to prevent the paint routine from flickering.

Using the Control

My demo project sets a timer and then updates the Text property of the user control to the current time. The effect is a digital clock that maintains the current time.

Listing 2 shows the bulk of the code from my demo project's main form. You can see the code initializes the LCD control in the Form's Load event. It then updates the control's Text property whenever the Timer control raises the Tick event.

Listing 2: The Demo Projects Form

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        try
        {
            Image img = Image.FromFile("Digits1Grn.bmp");
            lcdDisplay1.SetImage(img, ". 0123456789:");
            UpdateTime();
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }

    private void timer1_Tick(object sender, EventArgs e)
    {
        UpdateTime();
    }

    private void UpdateTime()
    {
        lcdDisplay1.Text = DateTime.Now.ToString("hh:mm:ss");
    }
}

I should point out that the demo project loads the bitmap file from the current directory, which will initially be the Debug subdirectory. So that is where I've placed the sample bitmap. As you modify the project and/or move files around, you'll need to ensure your code can find this file.

Conclusion

That's about all there is to it. I'm including an additional download of several LCD bitmaps that I created. You can use them as-is, or modify them to contain the characters and appearance you need.

Just remember, when you pass the image to the SetImage() method, make sure the character list string has exactly one character for each character in the bitmap. The control uses this information to correctly display the characters.

End-User License

Use of this article and any related source code or other files is governed by the terms and conditions of The Code Project Open License.

Author Information

Jonathan Wood

I'm a software/website developer working out of the greater Salt Lake City area in Utah. I've developed many websites including Black Belt Coder, Insider Articles, and others.