HomeASP.NET Extending the ASP.NET TextField User Contr...
Extending the ASP.NET TextField User Control
This is an extension to the previous article "Developing a Wonderful ASP.NET TextField Control" for supporting numeric features (something like a Numeric Textbox). Before proceeding with this, I strongly suggest that you go through the previous article, as I am using the same core structure as the previous user control.
The entire solution (source code) for this article is available as a free download (in the form of a zip). The source code in this article has been developed using Microsoft Visual Studio 2005 Professional Edition on Microsoft Windows Server 2003 Standard Edition. I didn't really test any of the code in any other tools/IDEs/servers/editions/versions. If you have any problems, please feel free to post in the discussion area.
I will not be repeating the explanation of user controls in this article as I already introduced the same in my previous article. Along with the introduction, I also developed a sample TextField user control featuring new functions. Now, I would like to extend the same control to support a few more functions.
A textbox which needs to have only numeric input is often required in ASP.NET applications. Numeric input is not only limited to digits. It may also have a decimal point (or period/dot) with decimal places. We may also include commas to separate thousands (say for a value of one billion). We may also want to have Max and Min values for the value being entered. I would like to include all of these features as part of this control.
This control is a superset of my previous control "uctTextField" (developed as part of the previous article). So you need to have "uctTextField.ascx" as part of your solution before proceeding with the new control. Let us start the development now.
Using the Solution Explorer, right click on your solution and choose Add New Item. Within the Add New Item dialog box, select Web User Control as the template, provide the name of the User Control as "uctNumericTextField" and hit Add.
Drag and drop the controls (and other stuff) onto your user control, so that it looks likes the following in the source mode:
<%@ControlLanguage="VB"AutoEventWireup="false"CodeFile="uctNumericTextField.ascx.vb"Inherits="uctNumericTextField"%> <%@RegisterSrc="uctTextField.ascx"TagName="uctTextField"TagPrefix="uc1"%> <asp:PanelID="pnlControl"runat="server"><uc1:uctTextFieldID="utxtControl"runat="server"/> <asp:RegularExpressionValidatorID="regExpValidator"runat="server"ControlToValidate="utxtControl" Display="None"ErrorMessage="[*name*] must be valid."ValidationExpression="^d+(?:.d{0,2})?$"></asp:RegularExpressionValidator> <asp:CustomValidatorControlToValidate="utxtControl"ID="cstmValidator"runat="server"Display="None"ErrorMessage="Value not in range"></asp:CustomValidator></asp:Panel> <asp:LabelID="lblInfo"runat="server"Text="TextField Numeric"></asp:Label>
According to the above source, you can understand that the entire control is again a part of the Panel control. Within the panel, I created two validators (a Regular Expression validator and a Custom Validator), uctTextField and a Label. Make sure that the path of "uctTextField.ascx" is set properly. The layout should look like the following in design mode (Fig 01):
Developing your own flexible NumericTextbox control: the skeleton
Once the design is completed, we need to start coding. In the code, I followed the following skeleton:
I removed the code for clarity. Based on the discussion of the requirements in previous sections, I came up with the above list of properties, methods and events. I removed the TextChanged event from this control. If you need it, you can add it as explained in my previous article.
You could also observe that a few of the properties in the previous user control (uctTextField) also got repeated here. That is necessary as we need to map the same existing functionality of uctTextField to this control. You can modify the properties according to your naming conventions and standards. My intention is just to give you an idea of the list of features of the user control.
The importance of "lblInfo" is worth mentioning at this moment. When a user control simply contains another user control as part of the design, it doesn't show up properly during design time (on ASPX). All the nested user controls look hidden, and the user would not be happy with an empty control. That's why I added "lblInfo" to show at design time that it is a numeric user control. At runtime it gets automatically hidden.
Apart from the events, I used a Focus method. When the control receives the focus, the cursor should be available in the text box. If the control is in read-only mode, it will not be able to receive any focus. It is defined as follows:
PublicOverridesSub Focus() If Editable ThenMe.utxtControl.Focus() EndSub
You can observe that I am calling the focus method of "utxtControl" when this control receives the focus. The validation based on Max and Min values is handled as follows:
ProtectedSub cstmValidator_ServerValidate(ByVal source AsObject, ByVal args As System.Web.UI.WebControls.ServerValidateEventArgs) Handles cstmValidator.ServerValidate Dim value AsDecimal = UnFormattedValue(args.Value) If value > MaxValue Or value < MinValue Then args.IsValid = False cstmValidator.ErrorMessage = Me.ErrorText & " should be in between " & Me.MinValue & " and " & Me.MaxValue Else args.IsValid = True EndIf EndSub
Developing your own flexible NumericTextbox control: dealing with Regular Expressions
Our control needs to deal with Regular Expressions as we would like to have support for decimal places, commas and so forth. The regular expressions are applied as follows based on the properties set by the user:
PrivateSub ApplyRegExpValString() If IncludeThousandsSeparator Then If DecimalPlaces > 0 Then RegExpValString = "(?n:(^$?(?!0,?d)d{1,3}(?=(?<1>,)|(?<1>))(k<1>d{3})*(.d{0," & DecimalPlaces & "})?)$)" Else RegExpValString = "(?n:(^$?(?!0,?d)d{1,3}(?=(?<1>,)|(?<1>))(k<1>d{3})*)$)" EndIf Else If DecimalPlaces > 0 Then RegExpValString = "^d+(?:.d{0," & DecimalPlaces & "})?$" Else RegExpValString = "^d+?$" EndIf EndIf EndSub
PrivateProperty RegExpValString() AsString Get Return m_strRegExp EndGet Set(ByVal value AsString) m_strRegExp = value Me.regExpValidator.ValidationExpression = value EndSet EndProperty
A few of the regular expressions which are most commonly used for these types of scenarios are as follows
(?n:(^$?(?!0,?d)d{1,3}(?=(?<1>,)|(?<1>))(k<1>d{3})*(.dd)?)$) - for $,comma,compulsory 2 dig dec
(?n:(^$?(?!0,?d)d{1,3}(?=(?<1>,)|(?<1>))(k<1>d{3})*)$) - for $, comma, no dig dec
(?n:(^$?(?!0,?d)d{1,3}(?=(?<1>,)|(?<1>))(k<1>d{3})*(.d{0,2})?)$) - for $, comma, option 2 dig dec
^d+(?:.d{0,2})?$ - without $, no comma, optional 2 dig dec
We may need to work with a few more properties apart from the Text property. A few applications need the value to be available with or without formatting. The following are the properties that support all of these scenarios:
Developing your own flexible NumericTextbox control: new properties added
The following are the new properties added to the control (apart from the properties added from previous uctTextField control):
<Category("Common")> _ PublicProperty IncludeThousandsSeparator() AsBoolean Get Return m_blnIncludeThousandsSeparator EndGet Set(ByVal value AsBoolean) m_blnIncludeThousandsSeparator = value ApplyRegExpValString() EndSet EndProperty
<Category("Common")> _ PublicProperty DecimalPlaces() AsInteger Get Return m_intDecimalPlaces EndGet Set(ByVal value AsInteger) If value < 0 Or value > 5 ThenThrowNew ApplicationException("Invalid Decimal specification") If DecimalPlaces <> value Then m_intDecimalPlaces = value ApplyRegExpValString() EndIf EndSet EndProperty
<Category("Common")> _ PublicProperty MaxLength() AsInteger Get ReturnMe.utxtControl.MaxLength EndGet Set(ByVal value AsInteger) Me.utxtControl.MaxLength = value EndSet EndProperty
<Category("Common")> _ PublicProperty MaxValue() AsDecimal Get Return m_decMaxValue EndGet Set(ByVal value AsDecimal) m_decMaxValue = value EndSet EndProperty
<Category("Common")> _ PublicProperty MinValue() AsDecimal Get Return m_decMinValue EndGet Set(ByVal value AsDecimal) m_decMinValue = value EndSet EndProperty
I hope you enjoyed the article and any suggestions, bugs, errors, enhancements etc. are highly appreciated at http://jagchat.spaces.live.com