With the introduction of ASP.NET AJAX and the AjaxControlToolkit came a new model for adding functionality to existing controls: Extenders. Extenders are neat little controls that extend (thus the name) existing controls by adding new client-side functionality. The magic in the Extender model is that it enables you to set properties in a compiled server control that are automatically fed into supporting JavaScript functions. Amazing examples of what can be done with this model are available on the AjaxControlToolkit Live Demo site.
If you've been developing with the radControls for long, you know that there are times when you wish that your amazing radControl could just do one more thing. Maybe you want to have a grid with drag-and-drop reordering. Maybe you want an input control that can be watermarked with CSS styles. Can we use Extenders to add this functionality to our 3rd party controls? I decided to try.
The executive summary: you can extend radControls with Extenders, but it's not easy.
Why? radControls, like many 3rd party components, come with lots of client-side functionality out of the box (after all, that's why you use these controls over the standard ASP.NET controls). That poses a challenge when you try to add your own client-side functionality to the controls, because you're faced with breaking disabling existing functionality in order to get your own additions to work. That makes extending 3rd party controls much more difficult than extending normal ASP.NET controls (as is done by the AjaxControlToolkit).
I made two attempts to extend radControls: one to add drag-and-drop reordering of rows to grid and one to add styled watermarks to input. The input project fared much better than the grid project, though neither has resulted in perfect results yet. For this post, I will discuss the more successful input project with some code and screenshots.
extending radInput
The goal for my InputWatermarkExtender is simple: create a control that can apply a CSS styled watermark to an input control. Out of the box, input supports some basic watermarking capability, but it does not allow watermarks to be styled with CSS. That may change in version 2 (due in April), but for now we'll need an Extender to get the job done.
To create the InputWatermarkExtender, I borrow heavily from the existing TextBoxWatermarkExtender that comes with the AjaxControlToolit. I start by creating my new AjaxControlToolkit Web Site in Visual Studio and then adding a new ASP.NET AJAX Control Project. With the basics established, I add two public properties to my server control class (WatermarkText and WatermarkCssClass) and one private read-only property (InputControlID to return the actual ID of the visible input textbox). When you add property accessors in an Extender, you must do things a little differently in order to wire-up the automatic communication with your JavaScript code:
<extendercontrolproperty()> _
<requiredproperty()> _
<defaultvalue("")> _
Public Property WatermarkText() As String
Get
Return GetPropertyValue(strWatermarkText, "")
End Get
Set(ByVal value As String)
SetPropertyValue(strWatermarkText, value)
End Set
End Property
Notice the property decorators that let the complier know these are Extender properties and the Get/SetPropertyValue functions that are used to pass your values into the JavaScript. The actual leg work is done by the AjaxControlToolkit Extender classes.
Next, I need to create the properties and methods in my supporting JavaScript file that will apply the watermark on page load and then remove it when an user focuses on the input control. When I compile my Extender, the JavaScript file will be included as an embedded web resource, so don't worry about where your JavaScript will be located when you deploy your Extender. Before I show some of the JavaScript code, though, you need to understand how an input control renders on the page.
When an input control renders in the browser, it is actually rendering much more than a single "magic" textbox. In fact, it's rendering 3 textboxes and 2 span tags like this:
<span id="radInputTst_wrapper">
<script type="text/javascript" ...
<span id="radInputTstStyleSheetHolder" style="display: none;"></span>
<input id="radInputTst" value="" type="hidden">
<input id="radInputTst_Value" name="radInputTst" type="hidden">
<input id="radInputTst_TextBox" name="radInputTst_TextBox" type="text">
<script type="text/javascript" ...
</span>
The key observation is that the textbox that users actually see has an ID composed of your input server control's ID and a "_TextBox" suffix. In order to apply a watermark, we must target this control in our JavaScript.
In our JavaScript, we add code to take over input OnMouseOver, OnMouseOut, OnFocus, and OnBlur events. We use the built-in ASP.NET AJAX helper functions to do this, like so:
this._mouseOverHandler = Function.createDelegate(this, this._onMouseOver);
$addHandler(e,'mouseover',this._mouseOverHandler);
Here you want to notice that you do not need to include the "on" portion of a client-side event's name when using the ASP.NET AJAX $addHandler function. The "_onMouseOver" function referenced in our "createDelegate" function exists in our prototyped InputWatermarkBehavior JavaScript class.
The only other trick to working with input, as I mentioned before, is making sure you access the visible textbox rendered to the page. By default, Extenders use the "this.get_element();" JavaScript function to access the target control in the DOM. To extend input, we used the following:
var controlID = this.get_element().id + '_TextBox';
var e = $get(controlID);
Once you've got all of your events and properties wired-up, you can build your control and drop it onto a page in your test website. The only markup you need on your page to make this Extender work looks like this:
<radI:RadMaskedTextBox runat="server" id="radInputTst" Mask="#####" />
<cc1:InputWatermarkExtender runat="server" id="inputExtender1" WatermarkText="Enter your zip code" WatermarkCssClass="watermark" TargetControlID="radInputTst" />
Build your solution and you end up with an input control with a styled watermark that looks like this:
Conclusions
While extending radControls with Extenders is possible, it will require extra time to analyze and work around the existing client-side functionality. Hopefully this example has highlighted some of the issues you'll need to consider if you attempt your own Extenders and shown you some of the basics to writing Extenders. As soon as I find a good place to host the code, I'll post a link to download the code used in this article (with a trial version of radInput, of course). Until then, have fun experimenting with Extenders!