Introduction
If you develop applications for Windows, you surely came across NumericUpDown
control. It is handy in many scenarios, but also somehow limited. There were several attempts to improve it. Few of them have been documented on CodeProject pages. Especially Extended NumericUpDown Control article by Claudio Nicora drew my attention. Claudio enhanced standard NumericUpdown
control with extra events and properties and that is what made it more appealing. This is one way of enhancing NumericUpDown
control.
I decided to go into a somewhat different direction. Why instead of enhancing existing elements of NumericUpDown
control, provide ability to replace edit element of it with any (editable or not) control? This would open an entire new world to how this control could be used.
With flexibility to provide your own editing component to NumericUpDown
, you can show numbers using formatting of your choice or you can display your own graphical controls, elements pictures. There are really no limits to what can go there.
Of course, it's not NumericUpDown
anymore. I had to drop 'Numeric' prefix, and decided to call it UniversalUpDown
control.
NumericUpDown Anatomy
NumericUpDown
control is a composite control. When you look at it, you see two distinct areas. TextBox
like area where numbers are displayed and edited, and squared UpDown
buttons. And that is how NumericUpDown
is composed. It has two internal controls: UpDownEdit
and UpDownButtons
.
Making UniversalUpDown Control
Knowing how NumericUpdown
is structured, our task is easy.
We need to:
- Build
UniversalUpDown
control as UserControl
that inherits from NumericUpDown
- Provide
public
property (I named it UserEditControl
) where user can provide as a parameter any control that will replace UpDownEdit.
The entire framework is drafted below:
public partial class UniversalUpDown : NumericUpDown
{
Control m_UserEditControl = null;
public Control UserEditControl
{
set
{
m_UserEditControl = value;
}
get
{
return m_UserEditControl;
}
}
}
Of course, the control defined as above doesn't do anything more than standard NumericUpDown
. We need to write missing code.
First goes constructor. Besides what every constructor does, it has to do a little housekeeping: find reference to UpDownEdit
and save it in local variable. It will be handy in UserEditControl
property setter where we will be replacing it with user provided control.
Something like this:
Control m_UpDownEdit = null;
public UniversalUpDown()
{
InitializeComponent();
foreach (Control control in this.Controls)
{
Type type = control.GetType();
if (type.Name=="UpDownEdit")
m_UpDownEdit= control;
}
}
When I looked at the internal structure of NumericUpDown
, I always saw that UpDownEdit
has index zero in Controls
collection, so in constructor, I could use line:
m_UpDownEdit = this.Controls[0];
I decided to use loop instead because this appeared to me a safer approach to be 100% sure that value stored in m_UpDownEdit
is truly UpDownEdit
instance not UpdownButtons
.
The next task is to complete coding of UserEditControl
property. Please read comments inside the code section below. I hope they explain everything.
public Control UserEditControl
{
set
{
if (value == null)
{
if (m_UserEditControl != null)
{
this.Controls.Remove(m_UserEditControl);
m_UserEditControl = null;
m_UpDownEdit.Visible = true;
}
return;
}
value.Dock = m_UpDownEdit.Dock;
value.Location = m_UpDownEdit.Location;
value.Size = m_UpDownEdit.Size;
if (m_UserEditControl==null)
m_UpDownEdit.Visible = false;
else
Controls.Remove(m_UserEditControl);
Controls.Add(value);
m_UserEditControl = value;
base.OnValueChanged(EventArgs.Empty);
}
get
{
return m_UserEditControl;
}
}
And that's all. The code for the entire UniversalUpDown
control is listed below:
public partial class UniversalUpDown : NumericUpDown
{
Control m_UserEditControl = null;
Control m_UpDownEdit = null;
public UniversalUpDownControl()
{
InitializeComponent();
foreach (Control control in this.Controls)
{
Type type = control.GetType();
if (type.Name=="UpDownEdit")
m_UpDownEdit= control;
}
}
public Control UserEditControl
{
set
{
if (value == null)
{
if (m_UserEditControl != null)
{
this.Controls.Remove(m_UserEditControl);
m_UserEditControl = null;
m_UpDownEdit.Visible = true;
}
return;
}
value.Dock = m_UpDownEdit.Dock;
value.Location = m_UpDownEdit.Location;
value.Size = m_UpDownEdit.Size;
if (m_UserEditControl==null)
m_UpDownEdit.Visible = false;
else
Controls.Remove(m_UserEditControl);
Controls.Add(value);
m_UserEditControl = value;
base.OnValueChanged(EventArgs.Empty);
}
get
{
return m_UserEditControl;
}
}
}
Using the Control
Simplicity comes at a little extra cost. Since you are providing your own edit control, you need to intercept ValueChaged
event in order to change appearance of UserEditControl
.
If UserEditControl
is editable, you also have to provide the means to set control's value property after value of UserEditControl
change.
The following example shows how to use editable textbox as UserEditEdit
control:
(...)
universalUpDown1.UserEditControl = textBox1;
universalUpDown1.Minimum = 0;
universalUpDown1.Maximum = 255;
universalUpDown1.UserEditControl = textBox1;
(...)
private void universalUpDown1_ValueChanged(object sender, EventArgs e)
{
textBox1.Text = String.Format("{0:X2}", (int)universalUpDown1.Value);
}
private void textBox1_Validating(object sender, CancelEventArgs e)
{
int valueAsInteger=0;
try
{
valueAsInteger = Convert.ToInt32(textBox1.Text, 16);
}
catch (Exception ex)
{
MessageBox.Show("String is not valid representation of hexadecimal number.");
e.Cancel = true;
return;
}
if ((valueAsInteger < 0) || (valueAsInteger > 255))
{
MessageBox.Show(string.Format("Value {0:X2} is not within valid range.", valueAsInteger));
e.Cancel = true;
return;
}
universalUpDown1.Value = valueAsInteger;
}
Demo Program
The attached code contains the source code of the control discussed in this tip and demo program shows few examples of UniversalUpDown
in action.
History
- 07-Sep-2013 - Initial version