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.