SP Numeric Edit Control

Introduction

When programming a GUI, quite often you use edit boxes to enter numeric values. A standard edit box has the ES_NUMBER style that lets you restrict a user’s input, allowing only digits to be entered. It’s a useful option, but it does not cover all cases met in practice. For example, when you need to input floating-point numbers, you should let the user enter not only digits but also a decimal separator and exponent symbol. Moreover, the floating-point format is more complex than a simple sequence of digits, so entered text has to be parsed to make sure it can be converted to a number and possibly let the user know about detected errors. So, I’ve made a special ActiveX control based on the standard edit box that extends its functionality and offers additional options for handling numbers.

Features

First of all, I’d like to note that this control deals directly with numeric data types, but not text strings. Internally, it performs a conversion of number values to their textual representation and back. The conversion is performed according to the certain format defined by a mask and an additional set of parameters (format properties). The mask is a text string that defines a format expression that matches certain syntaxes. You can specify your own mask or use the default one that is automatically generated according to system/locale settings. Format properties are used during formatting, scanning, and generation of the default masks.

The control operates in two modes: display and editing. Editing mode is turned on when the control gets keyboard input focus; otherwise, it stays in display mode. Each mode has its own format parameters, such as mask and format properties. Therefore, the user can see two different textual representations of the same numeric value. In display mode, the value is only converted in text, but in editing mode two-way conversion is performed. Usually, for editing mode, you should use a simplified format while, for display mode, you can enable a full set of features. Consider that you want to handle currency values. It would be convenient for the user to see number like $4,499.98, but at the same time, during editing, the user should not be forced to enter monetary symbol and separate groups with commas; he or she just has to enter essential data: 4499.98. This is the reason why two modes are provided.

Mask

A mask consists of the patterns, separated with semicolons. Every pattern corresponds to a certain value range or state. The number of the mask’s patterns and their purpose depend on the data type handled by the control. See Table 1 for more information.

Table 1: Mask patterns corresponding to data types.

Data
Type
Pattern
1
Pattern
2
Pattern
3
Pattern
4
Pattern
5
Pattern
6
Pattern
7
Pattern
8
Pattern
9
vtInt8 (VT_I1) positive number negative number zero null
vtInt16 (VT_I2) positive number negative number zero null
vtInt32 (VT_I4) positive number negative number zero null
vtInt64 (VT_I8) positive number negative number zero null
vtUInt8 (VT_UI1) non-zero number zero null
vtUInt16 (VT_UI2) non-zero number zero null
vtUInt32 (VT_UI4) non-zero number zero null
vtUInt64 (VT_UI8) non-zero number zero null
vtFloat (VT_R4) positive number negative number positive zero negative zero positive infinity negative infinity quiet NaN signaling NaN null
vtDouble (VT_R8) positive number negative number positive zero negative zero positive infinity negative infinity quiet NaN signaling NaN null

Any numeric value is formatted according to a certain pattern. For example, a mask for double values (vtDouble) consists of nine patterns; a negative value will be formatted with pattern 2; positive infinity, with pattern 5, and so on.

There are two types of patterns: value and literal. Value patterns are used to format definite numeric values. These are “number” and “zero” patterns (patterns related to positive number, negative number, non-zero number, positive zero, negative zero and zero). The rest are literal patterns. Literal patterns are used to represent special state of the value; for example when value is NULL or floating point value is negative or positive infinity.

In turn, a pattern consists of segments. All literal patterns have only one segment, but value patterns have at least one segment corresponding to the integer part of a number and can have two additional ones: prefix and suffix. Floating-point patterns additionally have segments for fraction and exponent parts. An exponent part exists only in E-format (exponential) patterns.

  • Literal pattern schema:
    { literal }

  • Integer value pattern schema:
    { prefix } | { integer } | { suffix }

  • Floating-point value F-format pattern schema:
    { prefix } | { integer } { . fraction } | { suffix }

  • Floating-point value E-format pattern schema:
    { prefix } | { integer } { . fraction } { e exponent } | { suffix }

Segments are always placed in the order listed above. Prefixes and suffixes are separated with a “|” symbol from the “value” part. They are optional, but when used, they both must be present; however, you may specify an empty prefix or suffix. An integer segment begins right after the “|” prefix delimiter if the one is preset or from the pattern’s beginning otherwise. Generally, a fraction starts from a “.” symbol and exponent from the “e” symbol. However, when the segment is completely included in an “optional” block (discussed below), it starts from the token opening that block. Integer fractions and exponent segments are mandatory for corresponding patterns.

During formatting and scanning, the digits of a number are handled sequentially in a definite order, depending on what segment is being processed. Integer and exponent parts are processed from right to left, but the fraction part is processed from left to right.

Segments are composed of tokens. Every token specifies a definite instruction for the formatting procedure. There are three types of the tokens that can be used inside segments: control tokens, placeholders, and literals.

Table 2: Tokens

Delimiters
; end of pattern
| prefix/suffix delimiter
Control Tokens
( open repeatable block
) close repeatable block
[ open optional block
] close optional block
Placeholders
0 digit placeholder with default zero value
_ digit placeholder with default space value
# digit placeholder
negative sign
+ positive sign
$ currency symbol
% percent symbol
per mile symbol
, thousand (group) separator
. decimal separator
e exponent
Reserved
{, }, <, > Reserved for future extensions.
Literals
\ Escape symbol.
any character Any character can be used as a literal. Characters corresponding to the reserved symbols should be preceded with a back slash “\”.
In addition, there are three special escape symbols:
\r – generates carriage return (CR)
\n – generates line feed (LF)
\t – generates tab

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read