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

Empty Template Text Box

0.00/5 (No votes)
4 Oct 2012 2  
This guide shows how to extend a standard TextBox to have an empty value template

Introduction

This guide will show you how to create a custom TextBox control that will display a value when empty, used for a label of the field. For instance, in an address form, this would show "Customer Name" in an empty text box. Once the user enters the control it reverts to standard operation. When the control leaves, if the value is still empty it reverts back to its masked mode.

Background

Sometime you need to make a form as compact as possible, and that requires you to have no additional label controls to describe your text boxes. This control will allow the label to be in the control itself when empty, so that the user knows what information to put where.

Using the code 

So the code provided is pretty much ready to use, but the methods involved will give you an idea of how this is done. You can create your own custom control will less customization if you like, or perhaps be inspired to customize it more.

Here is the code for the class.

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

namespace BuckSoft.Controls
{
    public partial class EmptyTemplateTextBox : TextBox
    {
        private String _emptyvalue;
        private Color _emptyforecolor;
        private Color _activeforecolor;
        private bool _preservebindings;
        public EmptyTemplateTextBox()
        {
            _preservebindings = true;
            InitializeComponent();
        }
        public String EmptyValue
        {
            get
            {
                return _emptyvalue;
            }
            set
            {
                if ((Text == _emptyvalue) && (!Focused))
                {
                    Text = value;
                    ForeColor = _emptyforecolor;
                }
                _emptyvalue = value;
            }
        }
        public Color EmptyForeColor
        {
            get
            {
                return _emptyforecolor;
            }
            set
            {
                if (Text == _emptyvalue)
                {
                    ForeColor = value;
                }
                _emptyforecolor = value;
            }
        }
        public Color ActiveForeColor
        {
            get
            {
                return _activeforecolor;
            }
            set
            {
                if (Text != _emptyvalue)
                {
                    ForeColor = value;
                }
                _activeforecolor = value;
            }
        }
        public bool PreserveBindingsOnEmpty 
        { 
            get { return _preservebindings; }
            set { _preservebindings = value; }
        }
        protected override void OnTextChanged(EventArgs e)
        {
            if ((Text == String.Empty) && (!Focused))
            {
                Text = _emptyvalue;
                ForeColor = _emptyforecolor;
            }
            else ForeColor = _activeforecolor;
            base.OnTextChanged(e);
        }
        protected override void OnEnter(EventArgs e)
        {
            if (Text == _emptyvalue)
            {
                Text = String.Empty;
                ForeColor = _activeforecolor;
            }
            base.OnEnter(e);
        }
        protected override void OnGotFocus(EventArgs e)
        {
            if (Text == _emptyvalue)
            {
                Text = String.Empty;
                ForeColor = _activeforecolor;
            }
            base.OnEnter(e);
        }
        protected override void OnLeave(EventArgs e)
        {
            if (Text == String.Empty)
            {
                if (_preservebindings)
                {
                    foreach (Binding b in DataBindings) 
                    { 
                         b.BindingComplete += 
                                  new BindingCompleteEventHandler(binding_BindingComplete); 
                    }
                }
                else
                {
                    Text = _emptyvalue;
                    ForeColor = _emptyforecolor;
                }
            }
            base.OnLeave(e);
        }
        private void binding_BindingComplete(object sender, BindingCompleteEventArgs e)
        {
            if (Text == String.Empty)
            {
                Text = _emptyvalue;
                ForeColor = _emptyforecolor;
            }
        }
    }
}

Note that since we start with a UserControl, we need to change the following line:

public partial class EmptyTemplateTextBox : UserControl

to read:

public partial class EmptyTemplateTextBox : TextBox

Since we started with a UserControl, we need to tweak the EmptyTemplateTextBox.Designer.cs file a bit.

First, in the designer generated code section, remove the line: 

this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;

nd so that the designer will have some initial values for out new properties EmptyValue, EmptyForeColor, and ActiveForeColor, add the lines: 

_preservebindings = false;
_emptyvalue = string.Empty;
_emptyforecolor = System.Drawing.SystemColors.ScrollBar;
_activeforecolor = System.Drawing.SystemColors.ControlText;

You could also put those line sin the constructor before the InitializeCompenents() call. 

The three properties we need to add for this control are:

  • EmptyValue - The value that will show to the user when the TextBox is empty.
  • EmptyForeColor - The color value we want the empty label to display as, preferably some light toned color, I've chosen SystemColors.ScrollBar.
  • ActiveForeColor - The color value we want the user entered text to display as, I've chosen the default SystemColors.ControlText.

When defining these properties, we must be sure that when they are changed, certain things happen to keep the continuity of the desired behavior intact.

EmptyValue

public String EmptyValue
{
    get
    {
        return _emptyvalue;
    }
    set
    {
        if ((Text == _emptyvalue) && (!Focused))
        {
            Text = value;
            ForeColor = _emptyforecolor;
        }
        _emptyvalue = value;
    }
}

get { } just needs to return the value of EmptyValue.

If EmptyValue is set { } programmatically we want to be sure that the control is updated to show, but before we set the new value, we need to check to see if the old EmptyValue is the current value, and if so, the new EmptyValue needs to be displayed. The (!Focused) ensures that display changes only occur when the TextBox is not in use.

EmptyForeColor

public Color EmptyForeColor
{
    get
    {
        return _emptyforecolor;
    }
    set
    {
        if (Text == _emptyvalue)
        {
            ForeColor = value;
        }
        _emptyforecolor = value;
    }
}

This property needs to check, when set, if the current text is EmptyValue, and if so set the new ForeColor value.

ActiveForeColor

public Color ActiveForeColor
{
    get
    {
        return _activeforecolor;
    }
    set
    {
        if (Text != _emptyvalue)
        {
            ForeColor = value;
        }
        _activeforecolor = value;
    }
}

This property just needs to check, when set, that if the value of the TextBox is anything besides the EmptyValue, the new ForeColor value is set.

One final optional property, a bool, PreserveBindingsOnEmpty, well be sure to keep the text box, if databound, from updating the data source when the empty value present. In fact, this will cause the TextBox to revert back to the original databound value when it loses focus when empty. See below in the Leave event override for how to make this work.

Now we have to hijack a few TextBox events by overriding them.

First we need to override the TextChanged event:

protected override void OnTextChanged(EventArgs e)
{
    if ((Text == String.Empty) && (!Focused))
    {
        Text = _emptyvalue;
        ForeColor = _emptyforecolor;
    }
    else ForeColor = _activeforecolor;
    base.OnTextChanged(e);
}

If the text box text is emptied, programmatically, in other words changed but when not Focused, then we need the EmptyValue to be populated, as well as the EmptyForeColor set. Otherwise, the ForeColor needs to be the ActiveForeColor.

Next we need to override the Entered and GotFocus events, both do the same thing. GotFocus is needed for when the control is the first focused control when the form loads:

protected override void OnEnter(EventArgs e)
{
    if (Text == _emptyvalue)
    {
        Text = String.Empty;
        ForeColor = _activeforecolor;
    }
    base.OnEnter(e);
}
protected override void OnGotFocus(EventArgs e)  
{
     if (Text == _emptyvalue)
     {
         Text = String.Empty;
         ForeColor = _activeforecolor;
     }
     base.OnEnter(e);
}

If the TextBox gains focus when the EmptyValue is displayed, the text is cleared, and the ActiveForeColor is set.

And finally we need to override the Leave and LostFocus events, LostFocus handles tab away actions. Adding an event handler for BingingComplete for each Binding the control has, and a Binding.WriteValue call to ensure that BindingComplete is hit, sometimes just losing focus does not cause a bind. The (!Focused) again ensures we don't set up the control for EmptyValue while its being used: 

protected override void OnLeave(EventArgs e)
{
    if (Text == String.Empty)
    {
        if (_preservebindings)
        {
             foreach (Binding b in DataBindings)
             { 
                 b.BindingComplete += 
                          new bindingCompleteEventHandler(binding_BindingComplete);
                 b.WriteValue();
             }
        }
        else
        {
            Text = _emptyvalue;
            ForeColor = _emptyforecolor;
        }
    }
    base.OnLeave(e);
}
protected override void OnLostFocus(EventArgs e)
{
    if (Text == String.Empty)
    {
        if (_preservebindings)
        {
            foreach (Binding b in DataBindings)
            {
                b.BindingComplete += 
                         new BindingCompleteEventHandler(binding_BindingComplete);
                b.WriteValue();
            }
        }
        else
        {
            Text = _emptyvalue;
            ForeColor = _emptyforecolor;
        }
    }
    base.OnLeave(e);
}
private void binding_BindingComplete(object sender, BindingCompleteEventArgs e)
{
    if ((Text == String.Empty) && (!Focused))
    {
        Text = _emptyvalue;
        ForeColor = _emptyforecolor;
    }
}

If the TextBox loses focus when empty, the data source is updated with the new empty text before the control sets the EmptyValue and EmptyForeColor. This is important because otherwise you would not be able to remove a value from the data source that you set once you set it. This method allows the data to bind while empty, and then when the binding is complete, we handle setting up the EmptyValue and EmptyForeColor values. 

I suggest when using a text box in a form, users have a habit of hitting enter key after completing a field, and that ding can be rather annoying. You can intercept the KeyPress to stop this annoyance with the following handler: 

private void textBox_KeyPress(object sender, KeyPressEventArgs e)
{
    if (e.KeyChar == (char)Keys.Enter) e.Handled = true;
}

Hope this helps someone out there.

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