Click here to Skip to main content
14,689,513 members
Articles » Desktop Development » Edit Controls » General
Tip/Trick
Posted 7 Sep 2013

Tagged as

Stats

10.5K views
306 downloads
8 bookmarked

GenericUpDown Control

Rate me:
Please Sign up or sign in to vote.
5.00/5 (5 votes)
7 Sep 2013CPOL
WinForms extension of NumericUpDown control to allow user provided edit control

Introduction

Image 1

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.

Image 2

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 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 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.

History

  • 07-Sep-2013 - Initial version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Adam Zgagacz
Software Developer (Senior)
United States United States
Software Developer for many years. Still enjoying it a lot. I started with Assembly coding for PDP-11, then had phases of Fortran, Pascal, C, C++, VisualBasic (1 -6), some Web Development (ASP) and finally I landed in C# world (since very beginning of C# life span).

Comments and Discussions

 
GeneralMy vote of 5 Pin
Gun Gun Febrianza7-Sep-13 18:07
Member Gun Gun Febrianza7-Sep-13 18:07 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.