AspAlliance.com LogoASPAlliance: Articles, reviews, and samples for .NET Developers
URL:
http://aspalliance.com/articleViewer.aspx?aId=1526&pId=-1
Developing Custom Controls - Part 1
page
by G. Mohyuddin
Feedback
Average Rating: 
Views (Total / Last 10 Days): 38527/ 97

Introduction

Server controls are meant for eliminating redundant code and boosting productivity. They make development tasks easier by hiding their internal details and providing ready to use packaged functionality. There are many controls shipped with ASP.NET that shorten development effort, but there are always opportunities for us to build our own controls to reduce even more development effort. Many times business scenarios create the need of custom controls to simplify development solutions.

If you want some reusable functionality in a single application or for few developers and it can be achieved from combining existing web controls then user controls can be a good choice. But when you want to make a piece of functionality reusable and redistributable then custom controls will be the best solution. You can extend some control or make one from scratch or combine multiple ones as per your need. This activity will let you get into the things and will add confidence for bringing novelty and uniqueness into your projects. Most importantly, you will become adept at intrinsic details of the architecture of the dozens of standard controls shipped with ASP.NET. The purpose of these controls is to achieve a different functionality (from the existing server control), a totally new functionality, or a combined functionality of several other existing or new server controls.

Need of a Custom Control

In this article we will examine the need, types and the architecture of custom controls. First of al, you must be aware of your requirement whether or not it really requires a custom control irrespective of the fact you will be able to develop it or not. It might be that the case that can be best handled with user controls. To analyze the need of a custom control you must know what it lacks, if there is some similar control already in the standards controls toolkit. Sometimes you may decide to go for an entirely new control if none of the controls have any functionality that you could inherit. The need of custom controls can be better understood if we learn a little about their types.

Types of Custom Controls

Literally speaking, "a control is custom for user if it fulfills a requirement that can’t be satiated by any standard control." The term custom control becomes vague sometime for the user when there is a parallel term of user controls which are also custom controls in nature, but are created differently. I would like to put light on this ambiguity and will try to resolve it. Yet before this, it is more important to know the types of custom controls and their place in the hierarchy of controls.

It is important to know that all controls are server controls i.e. they run at server and are rendered into the appropriate HTML controls at the client's browser.

We can make two major level categories of server controls.

·         Just In Time (JIT) Compiled

·         Pre Compiled 

JIT compiled controls are those controls that are embeddable in the source of aspx page and are compiled as per demand i.e. just in time. These controls, directly or indirectly, are inherited from System.Web.UI.WebControls.WebControl or System.Web.UI.Control which let them expose properties and provide events. They are compiled when the page gets compiled by the Framework. The following are the main types of JIT compiled controls.

·         HTML server controls

·         Web server controls

·         Validation controls

·         User controls

Let us take a look on these controls one by one.

HTML Server Controls

HTML controls support an additional property of “runat.” If this set is to “server,” it runs at server and is accessible in the code behind. Therefore, it is said to be an HTML Server control. Since these controls require less UI support, they are inherited from System.Web.UI.Control.

Web Server Controls

Web server controls are all standard server side controls that are shipped with ASP.NET. They include simple controls such as Panel and as rich as GridView. The basic purpose of these controls is to minimize the effort required for designing pages or building tools/controls that automatically generate user interface. These controls hide details of their internal working and make the task easier and less erroneous for developers. It is important to know that these controls inherit one of these two classes.

System.Web.UI.Control

It defines the properties, methods, and events that are common to all web server controls, for example methods and events needed handle the execution lifecycle. This does not support any UI specific feature. The controls that require no UI support inherit this class, such as ASP.NET controls including Literal, Repeater, PlaceHolder and XML.

or

System.Web.UI.WebControls.WebControl

The WebControl class inherits Control class and exposes additional properties and provides methods for UI support. A control that renders some UI should be inherited from WebControl. It is the class which provides UI-related properties such as BackColor, ForeColor, Font, Height, Width, etc. All ASP.NET controls except aforementioned controls inherit this class.

Validation Controls

These controls are used for validation of the input of other controls of a page/user control. These controls can perform client side, server side or both types of validation. Validation controls inherits the BaseValidator class that implements IValidator interface and inherits Label class, thus they inherit System.Web.UI.WebControls.WebControl indirectly.

User Controls

Lastly let us have a look at user controls and know how they are different from custom controls. User controls are custom in nature by purpose not by architecture. Development and design-wise they have nothing common with custom controls. They are closer to an aspx page than a custom control. This may be a bit misleading yet we have to take them entirely separate. Their architecture and way of working is entirely different the only thing that is common is they both can fulfill custom needs of a user.

The following are the important differences between a user control and a custom control from all aspects.

User Control vs. Custom Control

·         A user control inherits System.Web.UI.UserControl class with class hierarchy (bottom up) MyUserControlà UserControlà TemplateControlà Controlà Object. Whereas a custom controls inherits System.Web.UI.Control or System.Web.UI.WebControls.WebControl class with class hierarchy (bottom up) either MyCustomControlà Controlà Object OR MyCustomControlà WebControlà Controlà Object.

·         A user control has extension .ascx and is JIT compiled, while a custom control is in the form of dynamic linked library (.ddl), thus pre-compiled.

·         A user control must encapsulate functionality of other web server control(s) through dragging and dropping in design mode. A custom control does not have any design editor. It may or may not inherit functionality of other standard control(s). It can be an entirely new one.

·         A user control is easy to create because it can be designed like a page is designed i.e. they have design editor and code behind simultaneously while a custom control does not have design editor so is harder to create. 

·         A user control requires registration and instantiation per page and resides on a page as an object and is compiled along with the page. It is not in pre-compiled form whereas a custom control (newly created or extended) is a separate and pre-compiled component.

·         A user control cannot be registered with Global Assembly Cache (GAC) while a custom control can be registered with GAC, thus giving an advantage of being a framework wide component. A custom control is more reusable because you do not need a separate copy of it for each application; it can automatically be loaded be from the GAC.

·         A user control is compiled as a part of the same page class while a custom control is compiled as a separate, distinct class. Moreover, any client side logic (html, JavaScript, DOM etc.) is also written programmatically in case of Custom controls.

·         A user control cannot provide the level of richness that a custom control can provide. It is because custom control’s class is not bound with any design. Here you can take the leverage of the flexibility and richness of .NET programming model; you can expose properties, override functions of the base class and also register complex client side logic in the class.

·         A user control may fulfill your need if you are working on a single web application while a custom control is the best choice especially when you want to make your controls redistributable, more reusable and make it a Visual Studio IDE aware component.

·         A user control can use a custom control, but vice versa is not possible.

Custom Server Controls

Now getting specific to custom controls they are Pre Compiled in nature. As mentioned earlier, there three types of custom control with respect of their requirement and way of developing them. Types of custom controls are Extended, Composite and Fully Custom. It is important to note that these categories are not hard and fast. There can be a control that can fall into all three categories simultaneously. It can be a totally new, composite and may have its constituent controls extended, composite or new ones. We will explore them separately. Let us take a look on each of these types with respect to their requirement and development method.

Extended

If there is a standard web server control that meets most of our requirements and you want it to do few more tasks, then you will go for extended custom control. You will add a few more properties to achieve the desired tasks. For this purpose you extend the existing web server control, letting the new one inherit the full functionality set and behavior of the existing control and adding the required ones. 

Composite

You build a new control combining functionality of two or more existing controls. Suppose you want to build a control named PrimeTester; you would be required to include three standard web server controls to make it accomplished. It will combine functionality of a Textbox, Button and Label. A TextBox for input, a Button to test if it is prime and a Label to show the test result. Many standard web server controls shipped with ASP.NET are of composite nature. A simpler example is ASP.NET CheckBox control. Its gets rendered into two html controls. Say there is the following ASP.NET Checkbox control on the page.

Listing 1

<asp:CheckBox  id=”checkbox1” runat=”server” 
    Text=”This is simple composite control” … />

If you see the source of the page in the browser, you will notice that it has been rendered into these controls.

Listing 2

<input type=”checkbox” id=checkbox1…/> and 
<Label for=” checkbox1”> This is simple composite control </Label >

Since the html control “input” of type checkbox cannot support storing and displaying the contents of Text property of the ASP.NET Checkbox control, the html Label control has been added to keep record of the text of the Checkbox. The “for” property of the html label control keeps the ID of the associated Checkbox.

We will see composite custom controls in detail with a working example of a new composite control in Part II of the article.

Fully Custom

When none of the standard web server controls fulfills your requirements, what you want is entirely different from the functionality of any existing server control. You need to go for an entirely new one that neither extends any of the standard web server controls nor combines functionality of an exiting control. Based on the functionality of a fully custom control, it might have to implement one or more of the interfaces System.Web.UI.INamingContainer, System.Web.UI.IPostBackDataHandler or System.Web.UI.IPostBackEventHandler.  We will look on fully custom controls in detail with a working example of a new fully custom control in Part II of the article.

Building an Extended Custom Control

Extending an existing server control is an interesting and a simple job most of the time. I have chosen TextBox control for this purpose. Let us first analyze our need of building an extended TextBox control as we do not want to extend it just for the sake of an example! This analysis can be divided into two parts finding deficiencies to make up and suggesting enhancements that are useful. I emphasize on the analysis so that we can extend it meaningfully and purposefully making it a really useful member of our control tool kit. With inclusion of these properties in our control you will also be able to learn how a custom control can emit HTML and JavaScript as per required. 

There is a deficiency that really becomes problematic for developers; when the TextMode of the TextBox is set to Multiline, the MaxLength property stops working. We will discuss why it cannot support this.

While focusing on enhancements there are several which can be very useful, for example providing a support for entering cased input uppercase or lowercase, numeric, and alphanumeric. A textbox can also be self validating, these enhancements can add a good worth to it. So after a little analysis, we are able to figure out what we want to overcome and what we want to add in our new control.

Our new Textbox, ExtendedTextBox, will have the following features.

1.    It supports a cased input by providing TextCaseMode property.

2.    It supports an input mode for numeric and alphanumeric input by providing InputMode property.

3.    It supports MaxLength property when TextMode is set to MultiLine also.

4.    It provides built in required field validation by providing IsRequired property.

Now getting specific to the implementation, the following are the steps to accomplish all this.

5.    First, create a new project of Web Control Library. Please note that VB and Visual C# in Standard Edition do not support the Web Control Library template. It is only available in the professional edition.

6.    Give the class some suitable name, in our case ExtendedTextBox, and extend it from its base i.e. TextBox as shown below.

Listing 3

[Description("Extended TextBox v1.0 (Developed by Mahr G. Mohyuddin)")]   
[DefaultProperty("Text")]                       
[ToolboxData("<{0}:ExtendedTextBox runat=server></{0}:ExtendedTextBox1>")]
public class ExtendedTextBox : System.Web.UI.WebControls.TextBox
  {
...

You can see the class has been adorned with some attributes, Description, DefaultProperty and ToolboxData. Description attribute keeps the description of the control that is displayed on bringing the ouse over it in the tool box. DefaultProperty is used to bring the given property to focus when the property window of a control is displayed. ToolBoxData is a mandatory attribute that is used to define the look of control declaration in aspx markup when it is dropped on the page. It ensures that the control is given the class name; otherwise it will flag an error. You can also add more attributes like DefaultEvent, which add declaration of the specified event in the code behind when a control is double clicked. ToolBoxItem specifies whether the control should be visible or not; when it is set to True the control is visible and vice versa. ToolBoxBiitmap sepecifies the bitmap image that should be used as the icon for the control and finally ToolBoxItemFilter attribute specifies the filter Visual Studio 2005 uses to determine whether the control should be enabled or disabled for a specific designer.

Now, add properties that we need to expose to let the world use features we are going to provide. This includes TextCaseMode, InputMode, IsRequired, and ErrorMessage. Definition of all properties is similar. For instance, TextCaseMode sets the case of the text to be entered. It can be Uppercase, Lowercase or NotSet. We will need an enumeration for this purpose. The following code snippet shows the definition of TextCaseMode property.

Listing 4

[Description("Sets the case of the text entered")][Category("Behavior")
  ][DefaultValue(TextCaseMode.NotSet)]
public TextCaseMode TextCaseMode
{
  get
  {
    object o = ViewState["TextCaseMode"];
    return (o != null ? (TextCaseMode)o: TextCaseMode.NotSet);
  }
  set
  {
    ViewState["TextCaseMode"= value;
  }
}

The property has been adorned with three attributes: Description, Category and DefaultValue. Use of Description is quite evident. Category attribute takes the name of the category in the property pane of the control in Visual Studio IDE. All properties of a control are organized in Accessibly, Appearance, Behavior, Data, Layout and Misc categories. We want the new property TextCaseMode to be displayed in Behavior category.

Another important attribute is DefaultValue which sets a default value,; the specified one, is shown when the no value is set by user. In our case we want the text case mode not to be set by default. Therefore, NotSet has been provided as the default value.

This property is of type TextCaseMode enum that has been defined. A property that is of type enum is shown in the form of Dropdownlist in the control property pane. 

Figure 1

The new properties added to the control have been encircled. You can see that the name with description (provided in Description attribute) of the selected property appears on the bottom of the pane.

A value of property is set and retrieved in/from ViewState having name of property of the property as key, since ASP.NET controls save their state through ViewState. Similarly, a custom control should also save its state. In other words, the value of properties exposed by a custom control should be retained on postbacks. It can be possible only if we set the value of a property in ViewState and get from it as shown above. The following code snippet shows the definition of TextCaseMode and InputMode enums.

Listing 5

public enum TextCaseMode
{
  UpperCase, LowerCase, NotSet
} public enum InputMode
{
  Alphanumeric, Numeric, NotSet
}

Similarly, define other properties InputMode, IsRequired and ErrorMessage. In short, InputMode property is used to set the mode the text being entered that can either be Numeric, Alphanumeric or NotSet. The enum used has been defined in Listing 3.  IsRequired property when set to true makes input in the textbox mandatory and stops all postback until some value is entered. It shows the error, in tooltip, given in ErrorMessage property when the mouse comes over it.  The following code snippet shows the definition of the rest of properties.

Listing 6

[Description("Sets input mode")][Category("Behavior")][DefaultValue
  (InputMode.NotSet)]
 
public InputMode InputMode
{
  get
  {
    object o = ViewState["InputMode"];
 
    return o != null ? (InputMode)o: InputMode.NotSet;
  }
  set
  {
    ViewState["InputMode"= value;
  }
}
 
[Description("Mandates if the text is required")][Category("Behavior")
  ][DefaultValue(false)]
public bool IsRequired
{
  get
  {
    object o = ViewState["IsRequired"];
 
    return o == null ? false : true;
  }
  set
  {
    ViewState["IsRequired"= value;
  }
}
 
[Description("Displays messages if textbox input is required")][Category(
  "Behavior")][DefaultValue("")]
public string ErrorMessage
{
  get
  {
    object o = ViewState["ErrorMessage"];
 
    return o != null ? (string)o: "";
  }
  set
  {
    ViewState["ErrorMessage"= value;
  }
}

Since all the features of the control should work on client side we have client side script events to handle this. We have to write JavaScript code that will be emitted by the control as per demanded by the user who specify them through properties.

For this purpose we have to override PreRender event of the control so that our new control will be able to add the JavaScript logic before completing the rendering of the control.

We will add this code in the overridden PreRender event of the control as the following exhibit shows.

Listing 7

protected override void OnPreRender(EventArgs e)
{
  base.OnPreRender(e);
 
  string alphaNumeric = "function alphaNumeric(textBox){ " +
    "if(/[^0-9A-Za-z]/.test(textBox.value)){ " +
    "textBox.value=textBox.value.replace(/([^0-9A-Za-z])/g,''); " +
    "textBox.title = ''; " + "} " +
 
  " }";
 
  string numeric = "function numeric (textBox){ " +
    "if(/[^0-9a-z]/.test(textBox.value)){ " +
    "textBox.value= textBox.value.replace(/([^0-9])/g,''); " +
    "textBox.title = ''; " + "} " +
 
  " }";
  ...

The code snippet shows that we have called PreRender event of the base class i.e. TextBox and then declared and initialized two string members alphaNumeric and numeric that define javascript methods for respective functionality. I will explain one of them as an example and all upcoming JavasSript code will be quite similar. Please note that we have not registered them yet, that will be done later. We have defined alphaNumeric method that takes the textbox object as parameter and tests it on a regular expression (regExp) object by calling test method. If it is true then call the replace method on the result, that replaces any undesired input char with empty string; it uses again the same regExp object with a global search qualifier g to search the input. You may have noticed that before closing the body of the methods, we have cleared the title property of the textbox which shows an error message as the tooltip when IsRequired is set to True. So on any input we should clear the tooltip.

Likewise, define other javascript methods for UppCase,LoweCase, MaxLength and IsRequired features and set the respective string variables so that later could be registered on demand.

Listing 8

string upperCase = "function upperCase(textBox){ " +
"if(/[^0-9A-Z]/.test(textBox.value)){ "+     
"textBox.value=textBox.value.toUpperCase().replace(/([^0-9A-Z])/g,''); "+
"textBox.title = ''; "+ 
     "} " +
    "}";
 
string lowerCase = "function lowerCase(textBox){ " +
"if(/[^0-9A-Z]/.test(textBox.value)){ "+     
"textBox.value=textBox.value.toLowerCase().replace(/([^0-9A-Z])/g,''); "+
"textBox.title = ''; "+ 
     "} "+
    "}";
…

UpperCase and lowerCase methods have similar logic, the only change is the input is upper/lower cased by calling string class’s method toUpperCase() and toLowerCase(). Now it is time to register them on the basis of the property set by the user. The user may form any combination of the properties TextCaseMode (UpperCase,Lowercase,NotSet), InputMode (Numeic,AlphaNUmeric,NotSet). For this purpose we have to define another method which will call these methods depending upon the properties’ values. The following code snippet shows this.

Listing 9

… 
string cleintScript = "function extendedTextBox_PropertyChange(xTextBox){";
// method body starts
if (this.InputMode == InputMode.Numeric)
{
  Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "numeric",
    numeric, true);
 
  cleintScript += "numeric(xTextBox); ";
}
 
if (this.InputMode == InputMode.Alphanumeric)
{
  Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "alphaNumeric",
    alphaNumeric, true);
 
  cleintScript += "alphaNumeric(xTextBox); ";
}
 
if (this.TextCaseMode == TextCaseMode.UpperCase)
{
  Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "upperCase",
    upperCase, true);
 
  cleintScript += "upperCase(xTextBox); ";
}
 
if (this.TextCaseMode == TextCaseMode.LowerCase)
{
  Page.ClientScript.RegisterClientScriptBlock(this.GetType(), "lowerCase",
    lowerCase, true);
 
  cleintScript += "lowerCase(xTextBox); ";
}
 
cleintScript += " }"// end of method body

We have defined a string variable “clientScript” initialized with the method name; the forthcoming code registers script for a particular method and appends its script, i.e being added in the body of the main work horse method extendedTextBox_PropertyChange. At the end, append the string that closes the body of the method. Now we have to register extendedTextBox_PropertyChange method. The following code snippet not only registers it, but also adds it in the attributes collection of the control for onpropertychange event. We can also use onkeypress event, but using this will notice little elapse in uppercasing and lowercasing the input character. 

Listing 10

…
Page.ClientScript.RegisterClientScriptBlock(
     this.GetType(),
     "cleintScript",
     cleintScript, true);
this.Attributes.Add("onpropertychange", "extendedTextBox_ PropertyChange (this);");
…

Next, we have defined checkMaxLength method that makes MaxLength property of the textbox working when its TextMode is set to MultiLine. In TextBox control this problem arises when TextBox is MultiLine because in this case it is rendered into “TextArea” html control (<textarea id="ExtendedTextBox1" />) instead of  “input control with type text” (<input name="ExtendedTextBox1" type="text" id="ExtendedTextBox1). It is important to know that TextArea control does not support MaxLengh property. Therefore, when we set TextMode property to MultiLine, the MaxLength property stops working. To resolve this problem we have to write some client side logic and make part of the control as shown in the following listing.

Listing 11

string checkMaxLength = "function checkMaxLength(textBox) {" +
       " if(textBox) { " +
         " return ( textBox.value.length <" + this.MaxLength + "); " +
       " } " +
     "} ";
if (this.TextMode == TextBoxMode.MultiLine)
  {
    Page.ClientScript.RegisterClientScriptBlock(
         this.GetType(),
         "checkLength",
          checkMaxLength, true);
     this.Attributes.Add("onkeypress", "return checkMaxLength(this);");
  }

It checks and returns true if the length of input is less than or equal to the value of MaxLength property and false if vice versa. Then we have to register it as usual and add in the attributes collection of the control for onkeypress event.

Now, there is another very important feature added in the new textbox control. It mandates the input if specified using the IsRequired property. The implementation of this is simple, we have to handle form’s client side onSubmit event as shown in following code.

Listing 12

if (this.IsRequired == true)
{
         this.Page.Form.Attributes.Add("onSubmit""if(" + this.ClientID +  
 
".value==''){"+ 
        this.ClientID + ".title = '" + this.ErrorMessage + "';"+
        "return false;} "+ 
      "else{ " + this.ClientID + ".title = ''; "+
       " return true;}");
}
} // end of PreRender

Instead of making a separate method, the handler for onSubmit has been added directly in the attributes collection of the control. It checks if IsRequired property is set to True then sets the title property (used for tooltip) of control with the error message given by the user in ErrorMessage property and return false, otherwise clears the title property and return true i.e. allows postbacks.

When IsRequired property is set to True, it performs the required field validation, stops all postabacks and shows the error message in tooltip when the mouse is brought over the control. The Following figure shows this.

Figure 2

Downloads
Conclusion

Custom controls play very important and vital roles in development. They truly boost the productivity. They make many complex tasks easier. Building custom server controls is really fascinating job for a true developer! It is the domain where people usually enter when they desperately need it. But, I think, when some standard control does not fulfill your requirements before searching some third party control, you must go for your own. In this article we explored custom controls and their types in general and specifically focused on building an extended custom control. We built ExtendedTextBox that can be a very useful member of our control toolkit. The next two parts of the article will cover composite and fully custom controls with a working example of each. You might have noticed that we did not introduce any new event in the control as it was not required as per our analysis, yet we can add new events in a custom control, our forthcoming examples might have much to do with new events.

 



©Copyright 1998-2021 ASPAlliance.com  |  Page Processed at 2021-12-05 5:30:17 PM  AspAlliance Recent Articles RSS Feed
About ASPAlliance | Newsgroups | Advertise | Authors | Email Lists | Feedback | Link To Us | Privacy | Search