Introduction
Have you ever written code to implement custom functionality, (text formatting, permission checks, etc.), to existing ASP.NET controls? You can achieve such customizations by implementing custom web controls, but wouldn't it be nice to apply extra attributes to existing ASP.NET controls to achieve the same result? This article covers a way you can use existing functionality in ASP.NET to extend existing web controls by applying your own custom HTML attributes.
A Quick Background
To understand this extended functionality, one must be familiar with the ASP.NET life-cycle, particularly the way a System.Web.UI.Page
creates its child controls. If you look Page
's object hierarchy, you can see that it directly inherits from the System.Web.UI.TemplateControl
, which in terms inherits from the System.Web.UI.Control
. The Control
class defines the protected, and virtual, CreateChildControls
method, a method used to notify server controls to create any contained child controls in preparation for posting back or rendering. To implement our own version of this method, we need to create our own class that inherits from System.Web.UI.Page
.
Creating A Simple Custom Page
To implement a simple base ASP.NET page, define a new class that inherits from System.Web.UI.Page.
public class BasePage : System.Web.UI.Page { ... }
Next we need to override the CreateChildControls
method to specify our own control creation logic.
protected override void CreateChildControls() { ... }
Since all web and user controls must reside in a HTML form in order to work, we need to do two things:
- Within
CreateChildControls
, iterate through all 'page' controls and find the control of type System.Web.UI.HtmlControls.HtmlForm
- Iterate through the controls within the form control and select all controls of type
System.Web.UI.WebControls.WebControl
The following code defines this discovery process:
protected override void CreateChildControls()
{
foreach (Control c in this.Controls)
{
if (c is System.Web.UI.HtmlControls.HtmlForm)
{
ProcessForm(c as HtmlForm);
}
}
base.CreateChildControls();
}
private void ProcessForm(HtmlForm form)
{
foreach (Control cc in form.Controls)
{
if (cc is WebControl)
{
AttributeParser.ProcessControl(cc as WebControl);
}
}
}
Now that we have our base page and we've defined a discovery process, we can start the handling of our custom attributes with the help of some extra classes.
Stepping Through The Code
The main class for discovering custom HTML attributes is the AttributeParser
class. This class contains a method called ProcessWebControl
that simply iterates through all the previously defined attributes, represented by the AttributeType
enumeration, and checks each web control's attributes for a match. If a match is found, the AttributeType
is used in conjunction with the AttributeFactory
class to get an instance of a class that implements the IAttribute
interface. Using this class instance we then call the IAttribute.Apply
method and pass the value of the attribute along with the control on which the attribute is defined in. This will allow the implementing class to perform any custom actions on the web control based on the value of the custom HTML attribute.
Here's a quick look at the code:
public static void ProcessControl(WebControl control)
{
foreach(string attribute in Attributes)
{
string attributeValue = control.Attributes[attribute];
if (attributeValue != null)
{
AttributeType at = (AttributeType)Enum.Parse(typeof(AttributeType),
attribute, true);
IAttribute customAttribute = AttributeFactory.GetAttribute(at);
if (customAttribute != null)
{
customAttribute.Apply(attributeValue, control);
}
}
}
}
Two classes that implement the IAttribute
interface are the PermissionAttribute
class and the TextStyleAttribute
class. The PermissionAttribute
class is used to apply declarative security on the web control that contains the permission
HTML attribute. In other words, if a TextBox
controls defines the permission attribute in its HTML, then the TextBox
will be enabled for the list of roles defined otherwise the control will be disabled.
<asp:textbox id="txtData" runat="server" permission="Administrators">INFO</asp:textbox>
Here's what the TextBox
will look if the HttpContext.User
is not part of Administrator role.
The TextStyleAttribute
class is used to apply a custom look and feel to a TextBox
web control. For this article I've implemented the phone style that formats your text as a phone number as you type it with the aid of JavaScript. To enable this custom style, you need to apply the textstyle
custom HTML attribute to your text box.
<asp:textbox id="txtPhone" runat="server" textstyle="phone" />
This TextBox
will automatically add the '-' between the area code-prefix-postfix as you type in a number into the TextBox
.
The TextStyleAttribute
class is implemented a little bit different from the PermissionAttribute
class in the sense that it uses the value of the custom attribute to further define custom actions within the custom HTML attribute. This allows you to implement different styles for the TextBox
. For example, you can define a date textstyle
that automatically formats the numbers entered as a simple date (mm/dd/yyyy). The TextStyleAttribute
class is implemented as follows:
public class TextStyleAttribute : IAttribute
{
#region IAttribute Members
public void Apply(string attributeValue, WebControl wc)
{
if (wc is TextBox)
{
TextStyle mode = (TextStyle)Enum.Parse(typeof(TextStyle),
attributeValue, true);
switch(mode)
{
case TextStyle.Phone:
PhoneStyle(wc as TextBox);
break;
}
}
}
#endregion
private void PhoneStyle(TextBox text)
{
text.Attributes.Add("onKeyUp", "javascript:AutoPhone(this)");
text.MaxLength = 12;
}
}
For more information on the classes used, see the attached source.
Conclusion
The two presented attribute implementations are intended to show you how easy it is to extend the current controls bundled with ASP.NET and to give you a head start for creating your own custom HTML attributes. If you currently have a custom ASP.NET framework adding this type of functionality will make your framework a bit more customizable.
Please feel free to leave any comments below.