Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

NumberBox Class for Number Entry/Display

0.00/5 (No votes)
7 Apr 2020 1  
A NumberBox class for number entry, display, range limits and keystroke validation including binary
This NumberBox component code provides an easy method for number entry, display, range limiting and keystroke validation using SNFS definitions with a new binary definition added.

Introduction

This NumberBox control provides a convenient single control class to enter and display numbers in multiple formats. This class validates key strokes depending on the SNFS specified. This class also does a range limit check when the 'Leave' event fires. It works with SNFS types 'E', 'F', 'D', 'N' and 'X'. A new type of 'B' for binary is introduced. Values can be written in one format by the user and retrieved by the program in another format.

After the first compile, the NumberBox control will show up in your toolbox and can be dragged into your GUI design just as any other control.

Background

I program a lot of GUIs designed to connect directly to chips. These GUIs require numbers to be entered or displayed in multiple formats from doubles to hex and binaries all of various sizes and widths. Often, numbers will be read from the target in binary but I wanted to display them in decimal to the user. I got tired of writing these conversions over and over again so the lazy programmer in me decided to create one class that does it all.

Using the Code

Demo Window

Below is the demo window for this class. The left panel is where you enter numbers with keystroke validation depending on SNFS specified. When the control leave event fires, the value will be written back to the left panel with the display matching the SNFS and right panel controls will be updated with the new value range limited and presented in their respective SNFS format.

Image 1

The main code behind this window looks like this:

using System;
using System.Windows.Forms;

namespace NumberBoxDemo
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void numberBox_Leave(object sender, EventArgs e)
        {
            var nb = (NumberBox)sender;

            foreach (var c in panel2.Controls)
                if (c.GetType() == typeof(NumberBox))
                {
                    var box = (NumberBox)c;
                    if (box.Snfs.StartsWith("D")) box.ValueAsInt64 = nb.ValueAsInt64;
                    if (box.Snfs.StartsWith("F")) box.ValueAsFloat = nb.ValueAsFloat;
                    if (box.Snfs.StartsWith("E")) box.ValueAsDouble = nb.ValueAsDouble;
                    if (box.Snfs.StartsWith("N")) box.ValueAsDouble = nb.ValueAsDouble;
                    if (box.Snfs.StartsWith("X")) box.ValueAsInt64 = nb.ValueAsInt64;
                    if (box.Snfs.StartsWith("B")) box.ValueAsByte = nb.ValueAsByte;
                }
        }
    }
}

Being a lazy programmer, I like to write only one event handler for multiple controls of the same type. The numberBox_Leave event handler is connected to all the NumberBox controls in the left panel. When the event fires, the event handler sweeps through all the controls in the right hand panel and if the control is a NumberBox, it then updates the control based on its SNFS type.

NumberBox

The NumberBox class is derived from TextBox. The text in the Text field of the TextBox is the real storage location of the value. There no other internal or private locations. What you see is what you get.

The following properties have been added:

  • ValueAsByte
  • ValueAsDouble
  • ValueAsFloat
  • ValueAsInt16
  • ValueAsInt32
  • ValueAsInt64
  • ValueAsSByte
  • ValueAsUInt16
  • ValueAsUInt32
  • ValueAsUInt64
  • MaxValue
  • MinValue
  • Prefix
  • Suffix
  • Snfs

The ValueAs... properties get/set the Text string using the proper casting.

The Min/Max properties get/set the range limits applied to the numbers.

The Snfs property is the Standard Numeric Format String defined in Microsoft documentation. I've added a new format 'B' for binary numbers.

NumberBox Code Flow

After a NumberBox control is instantiated and when an SNFS is set, a regular expression is created for the specific SNFS. This regular expression '_pattern' is used to valid key strokes validation during the '_KeyDown' and '_KeyPress' events within the control.

All the magic happens when the user leaves the control:

  • The control reads the text from the NumberBox
  • Removes any prefixes or suffixes
  • Converts it to a double based on the SNFS format
  • Range limits to the Min/Max values
  • Writes it back to the control in the correct format.

As the main demo program shows, a 'Leave' event can be fired to perform any other actions required. In this demo, the 'Leave' event updates the 8 NumberBoxes in the right panel. The 2 leave events are called sequentially with the control firing first and the main program event firing last (standard C# event chaining).

Regular Expression Builder

The key section of the control is building the regular expression to valid the key strokes as they are typed in the control. This code is shown below. One key point is the 'B' lines for adding binary to the SNFS family.

private string BuildRegularExpression()
{
    if (_snfsEmpty) return string.Empty;

    // start of line anchor
    var str = "^";
    var len = LengthParameter(_snfs);

    // add in prefix
    if (!_prefixEmpty)
        str = Prefix.Aggregate(str, (current, c) => current + ("[" + c + "]"));

    // D4 => -?\d\d?\d?\d?\d?
    if (_snfs.StartsWith("D", StringComparison.OrdinalIgnoreCase))
    {
        str += "-?";
        if (len == 0) len = 4;
        for (var i = 0; i < len; ++i) str += @"\d?";
    }
    // E => ^-?\d*[.,]?\d?\d?\d?\d?\d?\d?[Ee]?[+-]?\d?\d?\d?
    if (_snfs.StartsWith("E", StringComparison.OrdinalIgnoreCase))
    {
        str += @"-?\d*[.,]?";
        if (len == 0) len = 6;  // SNFS default length
        for (var i = 0; i < len; ++i) str += @"\d?";
        str += @"[Ee]?[+-]?\d?\d?\d?";
    }
    if (_snfs.StartsWith("F", StringComparison.OrdinalIgnoreCase))
    {
        str += @"-?\d*[.,]?";
        if (len == 0) len = 2;  //SNFS default length
        for (var i = 0; i < len; ++i) str += @"\d?";
    }
    if (_snfs.StartsWith("N", StringComparison.OrdinalIgnoreCase))
    {
        str += @"-?\d*[., ]?";
        if (len == 0) len = 2;  //SNFS default length
        for (var i = 0; i < len; ++i) str += @"\d?";
    }
    if (_snfs.StartsWith("X", StringComparison.OrdinalIgnoreCase))
    {
        if (len == 0) len = 2;  //SNFS default length
        for (var i = 0; i < len; ++i) str += @"[0-9A-Fa-f]?";
    }
    if (_snfs.StartsWith("B", StringComparison.OrdinalIgnoreCase))
    {
        if (len == 0) len = 1;  //SNFS default length
        for (var i = 0; i < len; ++i) str += @"[0-1]?";
    }

    // add suffix
    if (!_suffixEmpty)
        str = _suffix.Aggregate(str, (current, c) => current + ("[" + c + "]"));

    // and the end of line anchor
    str += "$";

    return str;
}

Points of Interest

Being a lazy programmer and working with bit fields, I will often have dozens of NumberBoxes to update at once which would require dozens of lines of code to access each NumberBox separately. Instead, when I see all the NumberBoxes in one control like the right panel above, I find it easier just to sweep through all the controls in the panel and update them as I go. Even though here, I reduced 8 lines to code down to 11 lines, I often work with controls lists with up to 100 items.

For this demo, I swept the control list looking for a control of a specific type. You can also sweep the control list for a specific name. Or if you assign unique Tags to the controls, you can look for the unique tag. Also the shown sweep is only one layer deep, you can also build code to sweep in depth. Any container control has a 'Controls' list you can work with this way.

Credits

I am far from the original person to come up with this type of control class. There were many articles I've read in the past that implement similar functionality in this forum. Unfortunately, I have lost their links so I cannot give them credit directly. I can only stand on their shoulders.

History

  • 7th May, 2019 Initial version

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here