Introduction

If you develop applications for Windows, you surely came across NumericUpDown control. It is handy in many scenarios, but also somehow limited. As usual I started with Google search. I found several articles talking about how to improve it, but none of them were sufficient for my needs.

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, images. 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:

  1. Build UniversalUpDown control as UserControl that inherits from NumericUpDown

  2. 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
        {
                //TODO: Code that performs switch of UpDownEdit
                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 element of control 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 will do it:

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 assigned value is null, remove previously assigned UserEditControl
        if (value == null)
        {
            if (m_UserEditControl != null)
            {
                this.Controls.Remove(m_UserEditControl);
                m_UserEditControl = null;
                m_UpDownEdit.Visible = true;
            }
            return;
        }
       //Make sure that UserEditControl has the same size and location as the original one
        value.Dock = m_UpDownEdit.Dock;
        value.Location = m_UpDownEdit.Location;
        value.Size = m_UpDownEdit.Size;
        
        //If we are assigning UserEditControl for the first time we need to make instance
//of UpDownEdit invisible.
//if we are replacing already existing UserEdit control we have to remove 
//the old one first.
        if (m_UserEditControl==null)
            m_UpDownEdit.Visible = false;
         else
            Controls.Remove(m_UserEditControl);
        
//Now we have to add new UserEditControl.    
        Controls.Add(value);
        
//Then store the new UserEditControl in the local variable
        m_UserEditControl = value;
        
//Finally callOnValueChanged
        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:

//Example of UniversalUpDown to display and edit hexadecimal numbers
//with values between 0 and 255.
//As UserEditControl we will be using TextBox control.

(...)
 universalUpDown1.UserEditControl = textBox1;
 universalUpDown1.Minimum = 0;
 universalUpDown1.Maximum = 255;

 universalUpDown1.UserEditControl = textBox1;
 (...)
 
//Intercept ValueChaged event from instance of UniversalUpDown
//and populate instance of UserEditControl with the number
//in hexadecimal format.
private void universalUpDown1_ValueChanged(object sender, EventArgs e)
{
    textBox1.Text = String.Format("{0:X2}",  (int)universalUpDown1.Value);
}

//If UserEditControl is editable provide the way of updating 
//value of UniversalUpDown when editing is done.
private void textBox1_Validating(object sender, CancelEventArgs e)
{
    int valueAsInteger=0;
    //1. Convert string to decimal number
    try
    {
         valueAsInteger = Convert.ToInt32(textBox1.Text, 16);
    }
    catch (Exception ex)
    {
        MessageBox.Show("String is not valid representation of hexadecimal number.");
        e.Cancel = true;
        return;
    }
    //Check if number is within valid range
    if ((valueAsInteger < 0) || (valueAsInteger > 255))
    {
        MessageBox.Show(string.Format("Value {0:X2} is not within valid range.", valueAsInteger));
        e.Cancel = true;
        return;
    }
    //Finally, if everything is OK assign value to underlying
    //universalUpDown1 control
    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.


Home  bullet Products  bullet Downloads  bullet About Us  bullet Contact Us

Credit Card Logo