In my opinion, to build a server-side ASP.NET AJAX control
is even more complex than to build a client-side one. On the one hand, control
developers must be familiar with the fundamentals to construct ASP.NET Server
controls; on the other hand, they still have to grasp the client-side DOM,
JavaScript, and ASP.NET AJAX client-side programming. Of course, they can be
divided into two groups—one takes care of the server-side techniques while the
other does the client-side ones. This means the whole process of developing a server-side
ASP.NET AJAX control can be simplified. In real scenarios this is usually not
the case. However, there are still many reasons to explorer the means to
develop server-side ASP.NET AJAX controls. For example, the common web page
developers can more easily and conveniently work with these controls using
standard ASP.NET control syntax without worrying about writing custom
JavaScript code or even using the ASP.NET AJAX script library’s $create() method
to instantiate the controls.
Since we are to write a visual menu control from scratch
that generates HTML and JavaScript, we will get the control derived from System.Web.UI.WebControl and implement the IScriptControl
interface. And, at the same time, the menu control will continue to leverage
the "AjaxMenuCtrl.js" client-side control script discussed in the earlier
article "Explorer Ways to extend ASP.NET AJAX Client-side Function."
Author's Note: In this sample below,
we will embed the related scripts in the server-side controls. Of course, you
can also place the related resources used by the control assembly outside it. For
details about this, you can refer to related topics in MSDN.
Creating a WebControl Class that Implements
IScriptControl Interface
1. Start up Visual Studio 2005 and create a new WebControl library
called AJAXServMenuLib.
2. Right-click the project and select "Add References…"
to add a reference to the System.Web.Extensions.dll
assembly. This is a must have because IScriptControl interface is defined
within namespace "System.Web.UI" defined inside "System.Web.Extensions"
namespace.
3. Add a new JavaScript file called AjaxMenuCtrl.js. Right click
it and, in the Properties panel, set the Build Action property to "Embedded Resource." In
this way, the compiler can embed the file as an assembly resource that can be
loaded in a web page.
4. To make the above script accessible, add the following
assembly attribute immediately above the server control’s namespace definition.
[assembly: System.Web.UI.WebResource("AJAXServMenuLib.AjaxMenuCtrl.js",
"text/javascript")]
namespace AJAXServMenuLib
{……
This makes the script accessible through an HttpHandler
built into ASP.NET AJAX called ScriptResource.axd and
avoids having to deploy the physical script file along with the server control
assembly, which can greatly simplify deployment and maintenance. The WebResource attribute’s constructor accepts the name of the
resource followed by the content-type of the resource. Note that the name given
to the resource is the assembly name followed by the name of the script.
5. Implement interface IScriptControl.
As is emphasized above, to make the server control ajaxified
we should implement IScriptControl interface as follows:
Listing 9
public class AJAXServMenuCtrl: WebControl, IScriptControl
{
//Properties and methods definitions…
protected virtual IEnumerable < ScriptDescriptor > GetScriptDescriptors()
{
ScriptControlDescriptor descriptor = new ScriptControlDescriptor(
"Samples.AjaxMenuCtrl", this.ClientID);
descriptor.AddProperty("MenuTitle", this.MenuTitle);
descriptor.AddProperty("MenuTopCssClass", this.MenuTopCssClass);
descriptor.AddProperty("MenuListCssClass", this.MenuListCssClass);
descriptor.AddProperty("ServicePath", this.ServicePath);
descriptor.AddProperty("ServiceMethod", this.ServiceMethod);
return new ScriptDescriptor[]
{
descriptor
};
}
protected virtual IEnumerable < ScriptReference > GetScriptReferences()
{
ScriptReference r = new ScriptReference();
r.Assembly = "AJAXServMenuLib";
r.Name = "AJAXServMenuLib.AjaxMenuCtrl.js";
return new ScriptReference[]
{
r
};
}
IEnumerable < ScriptReference > IScriptControl.GetScriptReferences()
{
return GetScriptReferences();
}
IEnumerable < ScriptDescriptor > IScriptControl.GetScriptDescriptors()
{
return GetScriptDescriptors();
}
}
Here, with two helper functions, GetScriptDescriptors
and GetScriptReferences, we have implemented two methods
declared inside interface IScriptControl.
The GetScriptReferences() method shown above creates a
ScriptReference object and identifies the script resource that the server
control needs to send to the client (AJAXServMenuLib.AjaxMenuCtrl.js, in this
case). The GetScriptDescriptors() method creates a new ScriptControlDescriptor
object that maps server-side control properties to client-side control
properties. This is necessary so that the property values set on the control by
a developer can be passed to the $create() method used to instantiate the
client-side control. Note that the properties defined in this server-side
control are quite the same ones discussed in my earlier article.
6. Overriding Render and OnPreRender
Once our custom AJAXServMenuCtrl control’s script resources
are identified and server-side properties are mapped to client-side properties,
the control should override the WebControl's OnPreRender
method and retrieve a reference to the ScriptManager object in the page. The
ScriptManager object is then used to register the AJAXServMenuCtrl control as a
script control by calling the ScriptManager’s RegisterScriptControl() method.
Also, this server-side control should override the
WebControl's Render method to register the property
mappings (referred to as script descriptors) returned from the
GetScriptDescriptors() method. This serves the purpose of passing the control’s
property values defined on the server-side to the $create() method generated
automatically by the ScriptManager control. Listing 10 shows the two methods.
Listing 10
protected override void OnPreRender(EventArgs e)
{
if (!this.DesignMode)
{
_ScriptManager = ScriptManager.GetCurrent(Page);
if (_ScriptManager == null)
throw new HttpException(
"A ScriptManager control is required in the page.");
ScriptManager.RegisterScriptControl(this);
}
base.OnPreRender(e);
}
protected override void Render(HtmlTextWriter writer)
{
if (!this.DesignMode)
{
_ScriptManager.RegisterScriptDescriptors(this);
}
base.Render(writer);
}
7. Build the project and generate the final assembly—AJAXServMenuLib.dll.