My attempts to find a suitable control for displaying and editing correctly formatted currency values that could be bound to a decimal value were in vain, so I set about creating my first custom web control.
Note: The download also includes a
NumberBox control which use the same principles.
The first step was to define the essential requirements for the control.
- Allow databinding to a decimal value.
- Display the value as currency text, formatted in accordance with the current culture.
- Display negative values in a different colour.
- Allow only valid data entry.
- Perform the work associated with validating input and formatting currency values on the client side.
While the control renders as a text box
<INPUT type="text" ...>, inheriting from
System.Web.UI.WebControls.TextBox would have required many properties to be hidden, so the control inherits from
System.Web.UI.WebControls.WebControl and implements
Eleven public properties are added to the base class to control its appearance:
Alignment: Sets the alignment of text in the control (the default is
Amount: The decimal value to be bound to the control. The value is stored in ViewState and the implementation of
IPostBackDataHandler ensures the
AmountChanged event is raised when appropriate.
Description("The amount displayed in the control")
public decimal Amount
object amount = ViewState["amount"];
if (amount == null)
ViewState["amount"] = value;
if (value < 0)
base.ForeColor = NegativeColor;
base.ForeColor = PositiveColor;
MinAmount: Sets the minimum amount which can be entered by the user.
MaxAmount: Sets the maximum amount which can be entered by the user.
NegativeColor: Sets the colour to display if the
Amount is negative (<0).
Description("The colour of negative currency values")
public Color NegativeColor
_NegativeColor = value;
if (Amount < 0)
base.ForeColor = value;
PositiveColor: Sets the colour to display if the
Amount is positive.
Precision: The number of decimal places that the formatted amount will display.
readonly property which returns the
Amount formatted in accordance with the current culture.
In addition, the base class
ForeColor property is hidden to implement it as
readonly (because its colour is set using the
Rendering the Control
A WebControl, by default, renders as
<SPAN>. To render as a textbox
<INPUT>, we must first override
protected override HtmlTextWriterTag TagKey
and then add attributes by overriding
protected override void AddAttributesToRender(HtmlTextWriter writer)
if (PositiveColor != Color.Empty)
Two user defined attributes are added to the rendered control,
negativeColor. These two attributes are read by the client-side script to set the colour of the text depending on its value. In addition, three client side events are assigned to script functions as discussed below.
Client Side Scripts
The base class
OnPreRender method is overridden to get the current culture's
NumberFormat. Then it is checked if the scripts have been registered, and if not, code is called to construct them using the
NumberFormat, and finally register them.
protected override void OnPreRender(EventArgs e)
NumberFormatInfo format = Thread.CurrentThread.CurrentCulture.NumberFormat;
Five script functions are registered.
EnsureNumeric() is assigned to the
OnKeyPress event and limits keyboard entry to valid input.
FormatCurrencyAsDecimal() is assigned to the
OnFocus event. It calls the
CurrencyToDecimal() function and formats its text based on the returned value.
FormatDecimalAsCurrency() is assigned to the
OnBlur event. It calls the
DecimalToCurrency() function and formats its text based on the returned value.
CurrencyToDecimal() parses a currency string to a decimal value.
DecimalToCurrency() formats a decimal value as a currency string.
The code used to generate these scripts is too lengthy to be included here but the download includes a htm file explaining the methodology I used.
Using the Control
Note that when entering data, only numbers (0-9), the minus sign and the decimal separator (if appropriate) can be entered. Some languages such as Indonesian do not have decimal digits, in which case a decimal separator cannot be entered. Note also that the decimal separator for many languages (European) is a comma, not a decimal point.
- v1.0 - 08 June 2005 - Created.
- v1.1 - 04 August 2005
- Additional properties for
- Scripts amended.
PercentBox added to the project.
- v1.2 - 20 September 2005
- Scripts amended to validate minimum and maximum values.