asp tutorials, asp.net tutorials, sample code, and Microsoft news from 15Seconds
Data Access  |   Troubleshooting  |   Security  |   Performance  |   ADSI  |   Upload  |   Email  |   Control Building  |   Component Building  |   Forms  |   XML  |   Web Services  |   ASP.NET  |   .NET Features  |   .NET 2.0  |   App Development  |   App Architecture  |   IIS  |   Wireless
 
Pioneering Active Server
 Power Search








Active News
15 Seconds Weekly Newsletter
• Complete Coverage
• Site Updates
• Upcoming Features

More Free Newsletters
Reference
News
Articles
Archive
Writers
Code Samples
Components
Tools
FAQ
Feedback
Books
Links
DL Archives
Community
Messageboard
List Servers
Mailing List
WebHosts
Consultants
Tech Jobs
15 Seconds
Home
Site Map
Press
Legal
Privacy Policy
internet.commerce














internet.com
IT
Developer
Internet News
Small Business
Personal Technology
International

Search internet.com
Advertise
Corporate Info
Newsletters
Tech Jobs
E-mail Offers

HardwareCentral
Compare products, prices, and stores at Hardware Central!

Building a Simple Mask Control
By James Culshaw
Rating: 3.1 out of 5
Rate this article


  • email this article to a colleague
  • suggest an article

     

    Recently I began the task of creating my first custom server control for ASP.NET. I was under the impression that it should be reasonably straight forward as it all seemed very well documented - what a misapprehension that turned out to be! After hours of trawling through the .NET Framework SDK, numerous Web sites, and newsgroups, I finally succeeded in making my first working control. What I found was that all the important information was not all in one place. This article is an attempt to rectify some of that problem by bringing together all the standard information that is required while showing how to build a working control with client-side, rather than server-side, functionality.

    The final control that we will build is a basic mask control that allows input of time values. This version will accept two different formats - hour of day and duration. The only difference between the two is that duration will allow a time greater than 23:59. In both cases there is a fixed format of hh:mm, and the control will rectify the values entered to fit this format, either automatically modifying them or raising a warning to the user.

    My first piece of advice in creating a custom server control that is designed to do much work on the client side is to create what the control will render on the client in a standard html page, which you can do in notepad, Interdev, or any other html page development tool. It is far simpler to test and debug through this manner than trying to work out if there is something wrong with your client code or the server control code.

    There are two main pieces of code that we need to output - the HTML and the JavaScript. For building the basic mask control, we want to output the HTML shown in Figure 1.

    To create a new custom server control, create a new project in Visual Studio .NET, selecting Web Control Library as your project type. The new project will contain a single class that inherits from System.Web.UI.WebControls.WebControl. This is the base class for all .NET custom server controls, and provides all the basic properties and methods.

    <input name="InputMask1" size="8" maxlength="5" value="00:00" onkeypress="CheckMaskValue(this event);" onchange="CheckMaskFormat(this);">

    Figure 1: Control HTML

    To generate the HTML for the server control, use the Render method of the class. This method has a single parameter, output, which is a System.Web.UI.HtmlTextWriter object. It is with this object that HTML will be emitted. There are two ways of emitting code via the HtmlTextWriter object. The first is with the write method that emits directly to the page the contents of its string parameter, allowing you to pass in complete HTML strings. The second way is to build actual HTML elements using the RenderBeginTag, RenderEndTag, and AddAttribute methods. The latter is my preferred method as the compiler will find malformed HTML, and it is closer to the method used by XSLT to produce HTML with which I was already familiar, and so I will discuss how to use them.

    To create a new HTML tag, issue the RenderBeginTag method with the tag name as its parameter. To close that tag, issue the RenderEndTag method. This method has no parameter, so it will close the last issued open tag. It's a shame about the lack of a parameter as it makes debugging a little difficult, and it's easy to loose track of what you have closed and which close tag matches which begin tag. My tactic has been to use comments, and it has worked satisfactorily so far.

    To add attributes to the tags, issue the AddAttribute method, passing in the attribute and the attribute value as parameters. What is interesting is that you must issue the AddAttribute methods before the RenderBeginTag method for the tag that you wish to attach the attributes to. This caught me out at first.

    Figure 2 shows the code that we need to output the HTML in Figure 1. The important thing to note is the use of Me.UniqueID for the name attribute. To reference your control on the client using JavaScript, you need to give it a unique name. Microsoft has provided a UniqueID property, which is guaranteed to contain a unique name for the control, and so it is by this that you should name your control and then reference it in any client-side script. When building multi-element controls, it is absolutely vital to name at least one form element of your control with the UniqueID property if you are maintaining state within your control, as the page framework uses it to route postback data to your control.

    ProtectedOverridesSub Render(ByVal output As System.Web.UI.HtmlTextWriter)

            output.AddAttribute("name", Me.UniqueID)

            output.AddAttribute("size", "8")

            output.AddAttribute("maxlength", "5")

            output.AddAttribute("value", Me.Text)

            output.AddAttribute("onkeypress", "CheckMaskValue(this, event);")

            output.AddAttribute("onchange", "CheckMaskFormat(this);")

            output.RenderBeginTag("input")

            output.RenderEndTag()

        EndSub

    Figure 2: Render Method

    Because the custom server control is a class, you can define properties and methods just as in any other class. By default the public properties that you declare will appear in the property browser in the IDE. This control will have a property called Text, which contains the value of the control, and a property called MaskFormat, which allows the developer to set the format of the mask.. Figure 3 shows the code for these properties.

        <Category("Appearance"), DefaultValue("")> _

        Public Property [Text]() AsString

            Get

                If ViewState("Text") IsNothingThen

                    Return "00:00"

                Else

                    ReturnCType(ViewState("Text"), String)

                EndIf

            EndGet

            Set(ByVal Value AsString)

                ViewState("Text") = Value

            EndSet

        EndProperty

     

        <Category("Format"), Description("Determines the Date format to be used"), TypeConverter(GetType(InputFormatConverter))> _

        PublicProperty MaskFormat() AsString

            Get

                If ViewState("DateFormat") IsNothingThen

                    Return "Hour of Day (hh:mm)"

                Else

                    ReturnCType(ViewState("DateFormat"), String)

                EndIf

            EndGet

            Set(ByVal Value AsString)

                ViewState("DateFormat") = Value

            EndSet

        EndProperty

    Figure 3: Control Properties

    You can assign attributes to each property. The most common ones used are Category and Description. The Category attribute determines in which category in the property window the property appears. Description provides the description for that property in the bottom of the property window. There are numerous other attributes that can be used, and these are defined in the Microsoft .NET Framework SDK.

    In both properties, the value is stored in a collection called ViewState. This is a dictionary object that allows the values of the properties to be remembered across HTTP requests. These values aren't automatically updated when the user changes them and submits the form. We have to write some code to handle this, but that will be covered later.

    For the MaskFormat property, we want to provide a predefined list of options. This can be done either by defining the type of the property as an enumeration, or by associating the property with a custom type converter via the TypeConverter attribute. The TypeConverter attribute defines the custom type converter to use when the property is accessed. The use of an enumeration is the perfect way to allow options that consist of single words, but as in our case, if you wish to use phrases, then a custom type converter must be used. The custom type converter is defined in a separate class and is illustrated in Figure 4. To create a new custom type converter for your own controls, all you need to do is copy this entire class and replace the values in the _arrFormats() constructor.

    Imports System

    Imports System.ComponentModel

    PublicClass InputFormatConverter

        Inherits TypeConverter

      

        PrivateShared _arrFormats() AsString = {"Hour of Day (hh:mm)", "Hours duration (hh:mm)"}

     

        PrivateShared _Formats As StandardValuesCollection = New StandardValuesCollection(_arrFormats)

     

        'returns that the convertor supports a list of standard values

        PublicOverloadsOverridesFunction GetStandardValuesSupported _       (ByVal context As ITypeDescriptorContext) AsBoolean

            ReturnTrue

        EndFunction

     

        'notes whether you can manually add your own format

        PublicOverloadsOverridesFunction GetStandardValuesExclusive _       (ByVal context As ITypeDescriptorContext) AsBoolean

            ReturnTrue

        EndFunction

     

        'returns the list of standard values

        PublicOverloadsOverridesFunction GetStandardValues _

          (ByVal context As ITypeDescriptorContext) _

          As StandardValuesCollection

            Return _Formats

        EndFunction

     

    EndClass

    Figure 4: Custom Type Converter

    The bulk of the work of this control is handled on the client-side using JavaScript. I will not go into any detail on how this works, but I will describe how it is emitted to the Web browser from the control. Figure 5 shows the JavaScript that we wish to send to the browser, and Figure 6 shows the code within the control that generates it. The GetJavaScripts function is very long, but the bulk of it is taken up with formatting characters to emit nicely formatted JavaScript to the browser. There is no need to do this but it does make debugging a lot easier as you can actually read the code that your control has emitted. The function uses the MaskFormat property to determine what JavaScript to emit. In the example, client-side JavaScript the property had been set to 'Hour of Day (hh:mm)'.

    <script language="JavaScript">

    <!--

    function CheckMaskValue(maskControl, evt)

    {

          var strText = maskControl.value;

          if ((evt.keyCode < 48) || (evt.keyCode > 57))

          {

                evt.keyCode = 0;

          }

    }

    function CheckMaskFormat(maskControl)

    {

          var strText = maskControl.value;

          var a = strText.split(':');

          if (isNaN(a[0]))

          {

                maskControl.value = '00:00';

          }

          else if (isNaN(a[1]))

          {

                if ((a[0].length == 5) || (a[0].length == 4))

                {

                      strText = strText.slice(0,2) + ':' + strText.slice(2,4);

                      maskControl.value = strText;

                }

                if (a[0].length == 3)

                {

                      strText = '0' + strText.slice(0,1) + ':' + strText.slice(1,3);

                      maskControl.value = strText;

                }

                if (a[0].length == 2)

                {

                      strText = strText.concat(':00');

                      maskControl.value = strText;

                }

                if (a[0].length == 1)

                {

                      strText = strText.concat(':00');

                      maskControl.value = strText;

                }

          }

          else if (a[1].length == 1)

          {

                strText = strText.concat('0');

                maskControl.value = strText;

                }

          else if (a[0].length == 1)

          {

                strText = '0' + strText;

                maskControl.value = strText;

          }

          strText = maskControl.value; 

          strText = strText.slice(3,5);

          if (strText > 59)

          {

                alert('You have entered more than 59 minutes');

                maskControl.select();

          }

          strText = maskControl.value; 

          strText = strText.slice(0,2);

          if (strText > 23)

          {

                alert('You have entered more than 23 hours');

                maskControl.select();

          }

    }

    -->

    </script>

    Figure 5: Client-Side JavaScript

     

        PrivateFunction GetJavaScripts() AsString

            Dim strScript AsString = "<script language=""JavaScript"">"

            strScript &= ControlChars.CrLf

            strScript &= "<!--"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.CrLf

            strScript &= "function CheckMaskValue(maskControl, evt)"

            strScript &= ControlChars.CrLf

            strScript &= "{"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab

            strScript &= "var strText = maskControl.value;"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab

            strScript &= "if ((evt.keyCode < 48) || (evt.keyCode > 57))"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab

            strScript &= "{"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab

            strScript &= ControlChars.Tab

            strScript &= "evt.keyCode = 0;"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab

            strScript &= "}"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.CrLf

            strScript &= "}"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.CrLf

            strScript &= "function CheckMaskFormat(maskControl)"

            strScript &= ControlChars.CrLf

            strScript &= "{"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab

            strScript &= "var strText = maskControl.value;"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab

            strScript &= "var a = strText.split(':');"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab

            strScript &= "if (isNaN(a[0]))"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab

            strScript &= "{"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab

            strScript &= ControlChars.Tab

            strScript &= "maskControl.value = '00:00';"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab

            strScript &= "}"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab

            strScript &= "else if (isNaN(a[1]))"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab

            strScript &= "{"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab

            strScript &= ControlChars.Tab

            strScript &= "if ((a[0].length == 5) || (a[0].length == 4))"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab

            strScript &= ControlChars.Tab

            strScript &= "{"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab

            strScript &= ControlChars.Tab

            strScript &= ControlChars.Tab

            strScript &= "strText = strText.slice(0,2) + ':' + strText.slice(2,4);"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab

            strScript &= ControlChars.Tab

            strScript &= ControlChars.Tab

            strScript &= "maskControl.value = strText;"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab

            strScript &= ControlChars.Tab

            strScript &= "}"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab

            strScript &= ControlChars.Tab

            strScript &= "if (a[0].length == 3)"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab

            strScript &= ControlChars.Tab

            strScript &= "{"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab

            strScript &= ControlChars.Tab

            strScript &= ControlChars.Tab

            strScript &= "strText = '0' + strText.slice(0,1) + ':' + strText.slice(1,3);"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab

            strScript &= ControlChars.Tab

            strScript &= ControlChars.Tab

            strScript &= "maskControl.value = strText;"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab

            strScript &= ControlChars.Tab

            strScript &= "}"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab

            strScript &= ControlChars.Tab

            strScript &= "if (a[0].length == 2)"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab

            strScript &= ControlChars.Tab

            strScript &= "{"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab

            strScript &= ControlChars.Tab

            strScript &= ControlChars.Tab

            strScript &= "strText = strText.concat(':00');"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab

            strScript &= ControlChars.Tab

            strScript &= ControlChars.Tab

            strScript &= "maskControl.value = strText;"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab

            strScript &= ControlChars.Tab

            strScript &= "}"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab

            strScript &= ControlChars.Tab

            strScript &= "if (a[0].length == 1)"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab

            strScript &= ControlChars.Tab

            strScript &= "{"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab

            strScript &= ControlChars.Tab

            strScript &= ControlChars.Tab

            strScript &= "strText = strText.concat(':00');"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab

            strScript &= ControlChars.Tab

            strScript &= ControlChars.Tab

            strScript &= "maskControl.value = strText;"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab

            strScript &= ControlChars.Tab

            strScript &= "}"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab

            strScript &= "}"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab

            strScript &= "else if (a[1].length == 1)"

            strScript &= ControlChars.CrLf

            strScript &= ControlChars.Tab