Click here to Skip to main content
15,880,405 members
Articles / Programming Languages / C#
Article

A C# Numeric Field Control

Rate me:
Please Sign up or sign in to vote.
4.95/5 (38 votes)
29 Apr 2008MIT8 min read 148.5K   2.5K   159   52
An abstract base for a numeric fielded control.
A screenshot of FlexFieldControl in action

Introduction

After creating an IP address control for the .NET Framework, I applied the lessons learned towards creating an abstract control. This control can be easily configured to allow input of any value that is a collection of numeric fields, such as an IP address, IP address with ports, IPv6 address, MAC address, telephone number, or Social Security Number.

Background

FlexFieldControl is an abstract UserControl-based class that aggregates n controls of type FieldControl and n+1 controls of type SeparatorControl. These controls are arranged horizontally in an alternating fashion, beginning with a SeparatorControl. Below is an image of a default FlexFieldControl-based control, with the aggregated controls highlighted:

An exploded view of FlexFieldControl

By default, FlexFieldControl has three FieldControls and four SeparatorControls. Following the API below is an example of how to extend FlexFieldControl to create a new control, MACAddressControl, with just eleven lines of code.

Using the Code

Once the library containing FlexFieldControl (FlexFieldControlLib.dll) is built, add the DLL as a reference to your Windows Forms project. FlexFieldControl is defined as abstract; it will not appear in the Toolbox of the Windows Forms designer. However, any control based on FlexFieldControl will.

Public Instance Properties

  • AutoHeight - Gets or sets a value indicating whether the control is automatically sized vertically according to the current font and border. The default value is true
  • Blank - Gets a value indicating whether all of the fields in the control are empty
  • BorderStyle - Gets or sets the border style of the control. The default value is BorderStyle.Fixed3D
  • FieldCount - Gets or sets the number of fields in the control. The default value is 3 and the minimum value is 1. Setting this value resets every field and separator to its default state
  • ReadOnly - Gets or sets a value indicating whether the contents of the control can be changed
  • Text - Gets or sets the text of the control

Public Instance Methods

  • AddCedeFocusKey - Adds a specific keystroke to a field that cedes focus to the next field in the control. By default, the [Space] key will cede focus for any field that is not blank
  • Clear - Clears all text from all fields in the control
  • ClearCedeFocusKeys - Removes all keystrokes that cede focus from a field
  • GetCasing - Gets the character casing for a field in the control. The default is CharacterCasing.Normal
  • GetFieldText - Gets the text for a field in the control
  • GetLeadingZeros - Gets whether a field that is not blank has leading zeros. The default value is false
  • GetMaxLength - Gets the maximum number of characters allowed for a field. The default value is 3
  • GetRangeHigh - Gets the maximum value allowed for a field. The default value is based on the maximum length and value format of the field
  • GetRangeLow - Gets the minimum value allowed for a field. The default value is 0
  • GetSeparatorText - Gets the text for a separator in the control
  • GetValue - Gets the value for a field in the control. If the field is blank, its value is the same as its low range value
  • GetValueFormat - Gets the value format for a field in the control. The default is ValueFormat.Decimal
  • HasFocus - Gets whether a field in the control has input focus
  • IsBlank - Gets whether a field in the control is blank
  • ResetCedeFocusKeys - Resets a field to its default cede focus key, the [Space] key
  • SetCasing - Sets the character casing for a field in the control CharacterCasing.Lower will minimize the horizontal size for a field that has a value format of ValueFormat.Hexadecimal
  • SetFieldText - Sets the text for a field in the control
  • SetFocus - Sets input focus to a field in the control
  • SetLeadingZeros - Sets whether a field that is not blank has leading zeros
  • SetMaxLength - Sets the maximum number of characters allowed for a field. The minimum value is 1; the maximum value depends on the value format: 9 characters for decimal values and 7 characters for hexadecimal values
  • SetRange - Sets the minimum and maximum values allowed for a field in the control
  • SetSeparatorText - Sets the text for a separator in the control
  • SetValue - Sets the value for a field in the control
  • SetValueFormat - Sets the value format for a field in the control
  • ToString - Gets the text of the control. If any field is blank, that field's text will be its low range value

The client code can register a handler for the public event, FieldChangedEvent, to be notified when the text in any field of the control changes. Another public event, FieldValidatedEvent, notifies when the text of any field is validated. Note that Text and ToString() may not return the same value. If there are any empty fields in the control, Text will return a value that will reflect the empty fields. ToString() will fill in any empty fields with that field's low range value.

Creating MACAddressControl

Here is the source code for creating a simple MACAddressControl:

C#
using System;
using System.Windows.Forms;
using FlexFieldControlLib;

namespace TestFlexFieldControl
{
    class MACAddressControl : FlexFieldControl
    {
        public MACAddressControl()
        {
            // the format of this control is 'FF:FF:FF:FF:FF:FF'
            // set FieldCount first
            //
            FieldCount = 6;

            // every field is 2 digits max
            //
            SetMaxLength( 2 );

            // every separator is ':'...
            //
            SetSeparatorText( ":" );

            // except for the first and last separators
            //
            SetSeparatorText( 0, String.Empty );
            SetSeparatorText( FieldCount, String.Empty );

            // the value format is hexadecimal
            //
            SetValueFormat( ValueFormat.Hexadecimal );

            // use leading zeros for every field
            //
            SetLeadingZeros( true );

            // use uppercase only
            //
            SetCasing( CharacterCasing.Upper );

            // add ':' key to cede focus for every field
            //
            KeyEventArgs e = new KeyEventArgs( Keys.OemSemicolon );
            AddCedeFocusKey( e );

            // this should be the last thing
            //
            Size = MinimumSize;
        }
    }
}

The first step in the constructor should always be setting the FieldCount for the control. Otherwise, any prior settings will be reset. For instance, if the value format of every field was set to ValueFormat.Hexadecimal and then FieldCount was changed, the value format of each field would be reset to its default, ValueFormat.Decimal.

The next step is to set the maximum number of characters allowed in each field. In this case, every field is set to the same maximum length via SetMaxLength( 2 ). Alternatively, each field could be set individually: SetMaxLength( 0, 2 ), SetMaxLength( 1, 2 ), SetMaxLength( 2, 2 ), etc. The first parameter in SetMaxLength is the zero-based index of a field in the control.

By default, each separator has the text "<", "><", or ">" depending on its location within the control. For this MACAddressControl, each separator except for the first and last has ":" as its text. SetSeparatorText( ":" ) sets the text of every separator to ":". To clear the text of the first and last separators, SetSeparatorText( 0, String.Empty ) and SetSeparatorText( FieldCount, String.Empty ) are called.

The default value format of each field is ValueFormat.Decimal, but this control uses hexadecimal values in each field. Calling SetValueFormat( ValueFormat.Hexadecimal ) sets every field in the control to a hexadecimal value format. Alternatively, SetValueFormat can be called with a field index to set the value format of an individual field.

Since MAC addresses are commonly displayed with leading zeros in each field -- i.e., "08:09:0A:0B:0C:0D" rather than "8:9:A:B:C:D" -- each field is set to show leading zeros with the call SetLeadingZeros( true ). Alternatively, SetLeadingZeros can be called with a field index to set the leading zeros property of an individual field. By default, each field does not display leading zeros.

This control forces hexadecimal letters in every field to be displayed as capitals by calling SetCasing( CharacterCasing.Upper ). If a user enters "a" in any field, the control will display "A" in that field. Alternatively, SetCasing can be called with a field index to set the character casing of each field individually. As a convenience to the user, this control adds the [;] key as a cede focus key to each field, using:

C#
KeyEventArgs e = new KeyEventArgs( Keys.OemSemicolon );
AddCedeFocusKey( e );

If the value of a field can be represented by one character, the user can enter that character and then press the [;] key or the [Space] key to advance the input focus of the control to the next field. Alternatively, AddCedeFocusKey can be called with a field index to set the cede focus keys for each field individually.

The final call in the constructor, Size = MinimumSize, forces the control to be as small as possible. As settings change, the control will only grow to accommodate them; it will not automatically shrink. Once the control is placed on a form, it can be resized as necessary.

History

  • 29 April 2008
    • Added propagation for PreviewKeyDown, KeyDown, and KeyUp events
  • 20 November 2007
    • Added Dispose() calls when recreating subcontrols on FieldCount change. Thanks to Glenn for reporting this problem
  • 23 October 2007
    • ReadOnly should now really be read-only
  • 4 October 2007
    • Added proper event propagation for some mouse events
    • Added the AnyBlank property
    • Removed superfluous code
    • Addressed a potential resource leak when calculating text size
    • Compliant with FxCop 1.35
  • 23 July 2007
    • Fixed bug related to setting text of fields with leading zeros. Thanks once again to zeno_nz for reporting this problem
  • 15 July 2007
    • GotFocus and LostFocus events are consistently raised
    • The AllowInternalTab property was added to allow tabbing within the control. The default value is false
  • 27 June 2007
    • Changes to Text property are persisted in design mode
    • The KeyPress event can be trapped by clients of the library. Thanks to zeno_nz for requesting this enhancement
  • 5 June 2007
    • Initial release

License

This article, along with any associated source code and files, is licensed under The MIT License


Written By
Canada Canada
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionSome would say it's theft... Pin
John M. Baughman26-May-23 8:57
John M. Baughman26-May-23 8:57 
QuestionGreat control but losing digits in IPAddress control Pin
Ken Gunther2-Jun-16 19:01
Ken Gunther2-Jun-16 19:01 
GeneralMy vote of 5 Pin
Paolo Bashir Argenton11-May-13 5:21
Paolo Bashir Argenton11-May-13 5:21 
Generalsingle control for ipv4 and ipv6? Pin
I'm Chris16-Jun-11 3:11
professionalI'm Chris16-Jun-11 3:11 
GeneralSmall suggestion to make your control run under mono/linux [modified] Pin
I'm Chris15-Jun-11 23:23
professionalI'm Chris15-Jun-11 23:23 
GeneralShift+Tab Pin
Huisheng Chen31-Jan-09 3:10
Huisheng Chen31-Jan-09 3:10 
GeneralRe: Shift+Tab Pin
mid=574131-Jan-09 9:02
mid=574131-Jan-09 9:02 
GeneralRe: Shift+Tab Pin
Huisheng Chen31-Jan-09 16:07
Huisheng Chen31-Jan-09 16:07 
GeneralVisual Studio error Pin
Glenn Hopson16-Nov-07 2:29
Glenn Hopson16-Nov-07 2:29 
GeneralRe: Visual Studio error Pin
mid=574116-Nov-07 7:15
mid=574116-Nov-07 7:15 
GeneralRe: Visual Studio error Pin
Glenn Hopson16-Nov-07 9:42
Glenn Hopson16-Nov-07 9:42 
GeneralRe: Visual Studio error Pin
mid=574116-Nov-07 10:40
mid=574116-Nov-07 10:40 
AnswerRe: Visual Studio error Pin
mid=574116-Nov-07 12:59
mid=574116-Nov-07 12:59 
GeneralRe: Visual Studio error Pin
Glenn Hopson19-Nov-07 2:55
Glenn Hopson19-Nov-07 2:55 
GeneralRe: Visual Studio error Pin
mid=574119-Nov-07 18:56
mid=574119-Nov-07 18:56 
QuestionA slight bug Pin
zeno_nz31-Oct-07 0:15
zeno_nz31-Oct-07 0:15 
AnswerRe: A slight bug Pin
mid=574131-Oct-07 5:38
mid=574131-Oct-07 5:38 
AnswerRe: A slight bug Pin
mid=574131-Oct-07 19:39
mid=574131-Oct-07 19:39 
GeneralVery Nice Pin
Paul Conrad29-Jul-07 7:02
professionalPaul Conrad29-Jul-07 7:02 
GeneralHello me again - Text returned suggestion [modified] Pin
zeno_nz26-Jul-07 22:24
zeno_nz26-Jul-07 22:24 
GeneralRe: Hello me again - Text returned suggestion Pin
mid=574127-Jul-07 4:16
mid=574127-Jul-07 4:16 
GeneralRe: Hello me again - Text returned suggestion Pin
zeno_nz27-Jul-07 13:37
zeno_nz27-Jul-07 13:37 
I chose to modify you base code for pure simplicity, ease of program structure and it seemed a like a good idea to have the control return text only when all fields have been entered. It also makes your control much easier to error check and the text field can be bound directly to a data source knowing that all fields have values in them.
GeneralLeadingZero Question Pin
zeno_nz23-Jul-07 1:14
zeno_nz23-Jul-07 1:14 
GeneralRe: LeadingZero Question Pin
mid=574123-Jul-07 5:24
mid=574123-Jul-07 5:24 
GeneralRe: LeadingZero Question Pin
zeno_nz23-Jul-07 13:09
zeno_nz23-Jul-07 13:09 

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.