C# Custom control – TextBox accepting only numbers, decimal numbers, signed numbers

2017/10/05 00:51

The creation of custom control using Visual Studio involves the following steps:
  1. Start Visual Studio;
  2. Create new “Windows Control Library” project;
  3. In the created project delete “User Control” file;
  4. Go to “Project” > “Add User Control” and give it a name;
  5. Change the inherited class to this that you want your control to be;
  6. Create you custom properties for the control;
  7. Compile the project: Go to “Build” > “Build Solution”;
The generated DLL file is located in you projects folder under:
“bin/Debug/UserContolName.dll” or “bin/Release/UserContolName.dll”.
That depends on how your solution is configured: “Debug mode” or “Release mode”.
Once created the DLL file can be added as custom control to Visual Studio toolbox.

This example shows how to create custom text box control which accepts only numbers, decimal separator or negative sign.

By default the text box only accepts numbers: 0123456789, to be entered.

There are two properties that allows the user to enter only numbers with decimal separator or signed numbers.
If both properties are enabled then the text box accepts numbers, decimal separator and negative sign.
A tool tip is shown with information what the user can enter in the text box, if incorrect data is tried to be entered.
A placeholder (watermark) functionality is added using the paint event of the text box.

After creating the project delete the default "User Control" file.

Add new user control to project and name it “TextBoxNumbers”.

You can download the complete TextBoxNumbers project with test project included.
Or download only the TextBoxNumbers.dll control and include it in Visual Studio toolbox to start using it immediately.

Note:
The project is created in Visual Studio 2015.
TextBoxNumber control is targeting .Net Framework v3.5 and above.
// Change the class which is inherited from Control:

public partial class TextBoxNumbers : System.Windows.Forms.Control

// To TextBox:

public partial class TextBoxNumbers : TextBox
// Create the custom properties for the text box:

private bool isSigned = false;
private bool isDecimal = false;
private string toolTipText;  
// Where this is used will be show a little later
private string placeHolder; 
// Set to true to accept negative numbers
public bool IsSigned {   set { isSigned = value; }}
// Set to true to accep decimal numbers
public bool IsDecimal{   set { isDecimal = value; }}
// Set the text of the place holder
public string PlaceHolder{   set { placeHolder = value; }}

// ToolBox control is initialized by this function.

// Function to set parameters of the tool tip and to show it.
void ToolTipInitializer(string toolTipText)
{    
    // Parameters of the tool tip    
    int toolTipPosX = this.Width;
    int toolTipPosY = 0;
    int toolTipDuration = 4000;
    ToolTip toolTip = new ToolTip();
    // Set icon for the tool tip
    toolTip.ToolTipIcon = ToolTipIcon.Warning;
    // Show the tool tip
    toolTip.Show(toolTipText, this, toolTipPosX, toolTipPosY, toolTipDuration);
}

// The function to enter only numbers:

// By default this is active.
// Allow only numbers: 0123456789
private void Numbers(KeyPressEventArgs e)
{    
    // If the input is different from control key and digit key
    if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar))
    {
        // Show the tool tip
        ToolTipInitializer(toolTipText);
        // Set Handled method to true to cancel the button press.
        e.Handled = true;
    }
}

// The function to enter only signed numbers:

// Allow numbers: 0123456789
// and negative sign
private void SignedNumbers(KeyPressEventArgs e)
{
    // Get negative sign according your computer settings
    char negativeSign = Convert.ToChar(CultureInfo.CurrentCulture.NumberFormat.NegativeSign);
    if (e.KeyChar == negativeSign) // If the negative sign key is pressed
    {
        if ((this.SelectionLength > 0) && this.Text.Contains(negativeSign)) // When the whole number is selected and contains negative sign allow to enter negative sign
            e.Handled = false;
        else if ((this.Text.Length > 0) && (this.SelectionStart != 0)) // Forbids entering negative sign when not in the very beginning
            e.Handled = true;
        else if (this.Text.Contains(negativeSign)) // If text box contains decimal separator, then
            e.Handled = true;  // cancel the key press
        else
             e.Handled = false; // Allows entering negative sign in the very beginning
    }
    else if (!char.IsDigit(e.KeyChar) && !char.IsControl(e.KeyChar)) // If the input is different from control key and digit key
    {
        ToolTipInitializer(toolTipText); // Show tool tip
        e.Handled = true; // Cancel the key press
    }
}

// The function to enter only decimal numbers:

// Allows numbers: 0123456789
// and decimal separator
private void DecimalNumbers(KeyPressEventArgs e)
{
    // Get decimal separator according your computer settings
    char decimalSeparator = Convert.ToChar(CultureInfo.CurrentCulture. NumberFormat.NumberDecimalSeparator);
    if (e.KeyChar == decimalSeparator) // If the decimal separator key is pressed
    {
        // When the whore number is selected and contains decimal separator
        // allow to enter decimal separator
        if((this.SelectionLength > 0) && this.Text.Contains(decimalSeparator)) 
        {
            this.Text = "0";
            e.Handled = false;
            this.SelectionStart = this.TextLength;
        }
        else if (this.Text.Length == 0) 
        {
         // If decimal separator key is pressed in the very beginning
         // then a zero is inserted before it
            this.Text = "0" + decimalSeparator;
            e.Handled = true;
            this.SelectionStart = this.TextLength;
        }
        // If text box contains decimal separator, then
        else if (this.Text.Contains(decimalSeparator)) 
            e.Handled = true;     // cancel the key press
    }
     // If the input is different from control key and digit key
    else if (!char.IsDigit(e.KeyChar) && !char.IsControl(e.KeyChar)) 
    {
        ToolTipInitializer(toolTipText); // Show tooltip
        e.Handled = true; // Cancel the key press
    }
}

// The function to enter both decimal and signed numbers:

// Allows numbers: 0123456789, decimal separator and negative sign
private void DecimalSignedNumbers(KeyPressEventArgs e)
{
    char decimalSeparator = Convert.ToChar(CultureInfo.CurrentCulture. 
                               NumberFormat.NumberDecimalSeparator);
    char negativeSign = Convert.ToChar(CultureInfo.CurrentCulture. 
                   NumberFormat.NegativeSign);
    if (!char.IsControl(e.KeyChar) && !char.IsDigit(e.KeyChar) 
         && (e.KeyChar != decimalSeparator) && (e.KeyChar != negativeSign))
    {
        ToolTipInitializer(toolTipText);
        e.Handled = true;
    }
    if (e.KeyChar == decimalSeparator)
    {
        if((this.SelectionLength > 0) && this.Text.Contains(decimalSeparator))
        {
            this.Text = "0";
            e.Handled = false;
            this.SelectionStart = this.TextLength;
        }
        else if (this.Text.Length == 0)
        {
            this.Text = "0" + decimalSeparator;
            e.Handled = true;
            this.SelectionStart = this.TextLength;
        }
        else if (this.Text.Contains(decimalSeparator))
            e.Handled = true;
    }
    else if (!char.IsDigit(e.KeyChar) && !char.IsControl(e.KeyChar))
    {
        ToolTipInitializer(toolTipText);
        e.Handled = true;
    }
    if (e.KeyChar == negativeSign)
    {
        if ((this.SelectionLength > 0) && this.Text.Contains(negativeSign))
            e.Handled = false;
        else if ((this.Text.Length > 0) && (this.SelectionStart != 0))
            e.Handled = true;
        else if (this.Text.Contains(negativeSign))
            e.Handled = true;
        else
             e.Handled = false;
    }
    else if (!char.IsDigit(e.KeyChar) && !char.IsControl(e.KeyChar))
    {
        ToolTipInitializer(toolTipText);
        e.Handled = true;
    }
}

Overriding the OnKeyPress event of the text box.
Here the toolTipText property is initialized.
protected override void OnKeyPress(KeyPressEventArgs e)
{
    base.OnKeyPress(e);
        // Check what numbers is accepting the text box
    // and set tool tip message according that
    if (isSigned && isDecimal)
    {
        toolTipText = "Input field can only contain the following characters:"
                + "\n\t- Numbers: 0123456789"
                + "\n\t- Decimal Separator"
                + "\n\t- Negative Sign";
        DecimalSignedNumbers(e);
    }
    else if (isDecimal)
    {
        toolTipText = "Input field can only contain the following characters:"
                + "\n\t- Numbers: 0123456789"
                + "\n\t- Decimal Separator";
        DecimalNumbers(e);
    }
    else if (isSigned)
    {
        toolTipText = "Input field can only contain the following characters:"
                + "\n\t- Numbers: 0123456789"
                + "\n\t- Negative sign";
        SignedNumbers(e);
    }
    else
    {
        toolTipText = "Input field can only contain the following characters:"
                + "\n\t- Numbers: 0123456789";
        Numbers(e);
    }
}
Realization of the placeholder (watermark) functionality
The best way to create placeholder is to override the OnPaint event of the text box and to draw the desired text like this.
protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);
    SolidBrush myBrushDrawText = new SolidBrush(Color.DarkGray); // Set brush color
    Font drawFont = new Font("Microsoft Sans Serif", 8.25F); // Set font family and font size
    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; // Set quality of the graphic
        // Draw the placeholder text
    e.Graphics.DrawString(placeHolder, drawFont, myBrushDrawText, new Point(0, 0));
}
The OnPaint event is ignored by default until modifying the control style UserPaint.
Create a function for toggling the placeholder on and off:


// Function to toggle the placeholder on/off

private void PlaceHolder_Toggle(object sender, EventArgs e)
{
    if (this.Text.Length <= 0)
        this.SetStyle(ControlStyles.UserPaint, true); // Placeholder - on
    else
        this.SetStyle(ControlStyles.UserPaint, false); // Placeholder - off
}
Override OnCreateControl and call the PlaceHolder_Toggle function inside. This will guarantee that the toggle function of the placeholder will be called after the control is totally created.
protected override void OnCreateControl()
{
    base.OnCreateControl();
    PlaceHolder_Toggle(null, null);
}

The following events must be subscribed in the constructor so the placeholder could be toggled according to the text box property “Text”.

public TextBoxNumbers()
{
    InitializeComponent();
    // No one of theese events will start, because
    // TextBox control is still in construction
    this.TextChanged += new EventHandler(this.PlaceHolder_Toggle);
    this.LostFocus += new EventHandler(this.PlaceHolder_Toggle);
    this.FontChanged += new EventHandler(this.PlaceHolder_Toggle);
    this.GotFocus += new EventHandler(this.PlaceHolder_Toggle);
    this.LostFocus += new EventHandler(this.PlaceHolder_Toggle);
}