Have you ever wished that one of ASP.NET's intrinsic controls could do just a little bit more? Have you ever wished that you could change the behavior of a certain control? Have you ever wanted a round button instead of a rectangular one? Have you ever wanted to slap a mime silly? Well, if you've ever wanted to do any of these things, brother, you've come to the right place (except for the mime part - we've all wanted to do that at least once in our life - but you're on your own for that).
In this article we'll show you how to extend the functionality of an ASP.NET intrinsic control. We'll touch briefly on the object-oriented topic of inheritance, discuss how to persist a control's property values throughout an ASP.NET page's postback process, and then demonstrate how to reference the extended control in a WebForm. The code available for download with this article contains a very basic ASP.NET solution consisting of two projects: 1) the files for the extended control, and 2) the project used to test the control in a WebForm.
The Problem | |
Sometimes an intrinsic control just doesn't do everything I need it to do. On more than one occasion, for instance, I have wanted to make sure that the user knows, without a doubt, that a TextBox control contained read-only data. I know this isn't an enormous problem, but when I am in the user's chair, I like things in an application to be as obvious as possible - and as easy (I'm lazy that way). I'm fully aware that the TextBox control already has a ReadOnly property which when set to TRUE will prevent the user from modifying its contents, but there's no way for the user to know what state the TextBox is currently in (read-only or editable) unless he gives the control focus and tries typing. So, I'd like to be able to see right away if a TextBox is read-only or not.
As with most things, there's more than one way to skin a mime - er, cat. One way to accomplish this would be to put an asterisk, or some other character or image, next to the read-only control. I don't like that option because it can easily create clutter on the web page. Another way would be to change the background color or font color of a read-only control. That doesn't quite rub me the right way either: the colors chosen may not mean the same thing to every user, thus preventing us from achieving our primary goal of clarity.
After pondering over what would immediately tell me that a control's text could not be edited, a Label control popped into my mind. Everyone know that Labels are read-only. There's no mistaking that one. So, what if when my TextBox is in a read-only state the box part disappears and all we see are static, unchangeable contents? That sounded like a pretty good plan to me too.
| |
Inheritance | |
In the old days, the controls that came with your IDE were usually the ones you were stuck with - unless you bought some third-party ones or developed your own from scratch, of course. Because of .NET's object-oriented nature, extending a control is now nearly child's play.
As with any good OO technology, .NET allows inheritance. Inheritance allows us to derive a new class from an existing base class. This process ensures that the new derived class will automatically have all the same public and protected members that the original base class had, as well as any additional custom members the developer may choose to include. Explaining the intricacies of object-oriented programming concepts is beyond the scope of this article, so suffice it to say that in order to extend a control to make a new control, we employ the use of inheritance as shown in the C# code below:
| |
public class DerivedControl : OriginalBaseControl { //Some code here } | |
In our case, we want all the functionality of a normal TextBox control in addition to any extended functionality we might choose to add. This is accomplished by inheriting from the intrinsic TextBox control as shown here: | |
public class LabelTextBox : TextBox { //Some code here } | |
What this code gives us is a new class, LabelTextBox, derived from the original intrinsic TextBox control. All of the TextBox control's members are intact in the new class and we are now free to add new ones or modify existing ones. | |
Overriding and the Control Execution Lifecycle | |
In order to customize the new derived class, we can add members to it that the original TextBox control didn't have. We can also override some of the original control's members. Notice that I said "some". Not all members of a base class can be overridden by a derived class.
In ASP.NET, a page that is retrieved from a server (whether on the first retrieval or on a postback) goes through a process called the "Control Execution Lifecycle" (CEL) which consists of the following steps:
The MSDN Library explains this process in greater detail (see: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconcontrolexecutionlifecycle.asp), but the step that mainly interests us for our project is the Render step.
Web controls in ASP.NET all have a Render() method that is called during the Render step of the CEL. This method handles the generation of the HTML to output to the web page so that we can actually see the control. Conveniently for us, the Render() method is overridable, so we can modify what it does in order to customize our derived control. In the code below we see how to override the Render() method:
| |
protected override void Render(System.Web.UI.HtmlTextWriter writer) { if(this.ReadOnly){ //Only show the text - not the textbox writer.Write(this.Text); } else{ //Perform the normal rendering base.Render(writer); } } | |
In this code we see that rather than using the base class's original rendering code, we check first to see if the control has been flagged as read-only. If so, the control will only spit out the text contents of the control into the HTML. If the control is not marked as read-only, however, we will call the base class's original Render() method to ensure that our control will appear as a normal TextBox in the web page.
We'll leave that method as it is so far and come back to it in a moment after we have discussed one more important topic.
| |
ViewState | |
An ASP.NET page can persist its form data so that no information is lost during a submission of the form to the server. For intrinsic web controls, this is done behind the scenes through a process that basically dumps the contents of the control's properties into a ViewState object and then retrieving them back from the ViewState object during the CEL process.
Suppose we want to add a LabelCSSClass property to our new derived class to be able to specify a CSS class name for the read-only text version of our control. We could jump right in and add the following code:
| |
private string _sLabelCSSClass = ""; public string LabelCSSClass{ get{return this._sLabelCSSClass;} set{this._sLabelCSSClass = value;} } | |
This will work just fine until we go through a postback process, in which case if we try to access the property, the returned value will be the empty string ("") that it's initialized to. The reason this happens is that we're not specifying anywhere that this property value should be persisted in ViewState. We'll have to do this manually as shown here: | |
public string LabelCSSClass{ get{return ((string)ViewState["LabelCSSClass"]);} set{ViewState["LabelCSSClass"] = value;} } | |
Note that we have removed the private member that was holding the value. It's not necessary anymore because we are indicating that the value should be stored in, and retrieved from, the ViewState object.
Be aware that using ViewState is not always the most efficient way to do things. It increases the overhead involved in communicating with the server because all the values stored in ViewState must be sent over the wire to the server in a postback process. In our case, we're just storing an itty, bitty, little string, so the overhead is unnoticeable. in scenarios requiring the storage of large amounts of data, though, you may want to consider other ways of persisting your form data.
| |
Render() Revisited | |
Now we can return to our overridden Render() method and incorporate the CSS class information that we're storing in the ViewState object. To pretty up our read-only version of the control's text, we can modify our code as follows: | |
protected override void Render(System.Web.UI.HtmlTextWriter writer) { if(this.ReadOnly){ //Only show the text - not the textbox writer.Write(@"<span class=""{0}"">{1}</span>", this.LabelCSSClass, this.Text); } else{ //Perform the normal rendering base.Render(writer); } } | |
This code will now apply whatever CSS class we have specified to the read-only text that will be written. Now all we need to do is compile the code and it will be ready for action. | |
Register Directive | |
Once our control code is compiled, we can turn our attention to the WebForm that we'll be using to test it. The first thing we need to do there is include a reference to the control's assembly. We can do so by placing a Register directive at the top of the ASP.NET page as shown here: | |
<%@ Register TagPrefix="eisc" Namespace="ExtIntSrvCtl" Assembly="ExtIntSrvCtl" %> | |
The sample application that accompanies this article demonstrates how our new control looks and behaves in comparison to an intrinsic TextBox control. Give it a whirl! | |
Conclusion | |
In this article we have seen that extending an intrinsic ASP.NET control is really rather easy. The steps we followed in doing this were:
Feel free to tinker with the control in the accompanying application. Try adding other properties or methods to the new control - or better yet, try extending a different intrinsic control.
Hmmm...If only we could tweak a mime's properties...
|
No comments:
Post a Comment