70-564 - Extending controls with control extenders

Filed under: , , , , by:

Before .NET 3.5 to extend a control meant usually to inherit from a base control and add extra functionality or create a custom control that would contain a control to extend with additional items ie. javascript. But .NET 3.5 introduced another way to extend a control, which is through Ajax Control Extender, but I have experienced that still many developers are not aware of that fact and still use either inheritance (in java keyword extends is equivalent of keyword inherits in VB.NET or ":" in C#) or create a custom control where they could take advantage of ExtenderControl class. That class enables you to programmatically add AJAX functionality (or simple javascript) to an ASP.NET server control. Since it's not based on inheritance, there is no limitation of extending only unsealed controls.Also one extender may extend different types of controls (TextBox, Button, MyTextBox, etc.) - before it would require creating custom class for each type of controls being extended. I believe that's why AjaxControlToolkit contains so many control extenders ie. SliderExtender, RoundedCornersExtender or ModalPopupExtender. But even though there always will be a business need (or rather functional requirement) to create a custom control extender ie. FocusExtender that will apply different css style whenever a target control gets focus and reverts it back to the default css style when a target control has lost focus. Plus target control can be any type of control - TextBox, Button, DropDownList, etc.
Control extender is created by creating a new project of type: ASP.NET Ajax Server Control Extender.


Once created, a project contains a class that inherits from ExtenderControl and a javascript file in which client-side behavior is specified that will be added to a target control being extended.
FocusExtender.cs

using System;
using System.Collections.Generic;
using System.Web.UI;
namespace Extenders
{
[TargetControlType(typeof(Control))]
public class FocusExtender : ExtenderControl
{
protected override IEnumerable<ScriptDescriptor>
GetScriptDescriptors(Control targetControl)
{
ScriptBehaviorDescriptor desc = new ScriptBehaviorDescriptor(
"Extenders.FocusBehavior", targetControl.ClientID);
desc.AddProperty("onFocusCssClass", OnFocusCssClass);
desc.AddProperty("defaultCssClass", DefaultCssClass);
yield return desc;
}
 
protected override IEnumerable<ScriptReference>
GetScriptReferences()
{
ScriptReference sRef = new ScriptReference();
sRef.Name = "Extenders.FocusBehavior.js";
sRef.Assembly = this.GetType().Assembly.FullName;
yield return sRef;
}
 
public string OnFocusCssClass
{
get
{
String s = (ViewState["OnFocusCssClass"] as String);
return s ?? "";
}
 
set
{
ViewState["OnFocusCssClass"] = value;
}
}
 
public string DefaultCssClass
{
get
{
String s = (ViewState["DefaultCssClass"] as String);
return s ?? "";
}
 
set
{
ViewState["DefaultCssClass"] = value;
}
}
}
}
FocusBehavior.js
Type.registerNamespace('Extenders');
Extenders.FocusBehavior = function(element)
{
Extenders.FocusBehavior.initializeBase(this, [element]);
this._onFocusCssClass = null;
this._defaultCssClass = null;
this._focusHandler = null;
this._blurHandler = null;
}
Extenders.FocusBehavior.prototype =
{
initialize : function()
{
Extenders.FocusBehavior.callBaseMethod(this, 
'initialize');
var e = this.get_element();
this._focusHandler = Function.createDelegate(
this, this._onFocus);
this._blurHandler = Function.createDelegate(
this, this._onBlur);
$addHandler(e, 'focus', this._focusHandler);
$addHandler(e, 'blur', this._blurHandler);
},
dispose : function()
{
var e = this.get_element();
if (this._focusHandler)
{
$removeHandler(e, 'focus', this._focusHandler);
this._focusHandler = null;
}
if (this._blurHandler)
{
$removeHandler(e, 'blur', this._blurHandler);
this._blurHandler = null;
}
Extenders.FocusBehavior.callBaseMethod(this, 'dispose');
},
_onFocus : function(evt)
{
var e = this.get_element();
if (e != null)
{
e.className = this._onFocusCssClass;
}    
},
_onBlur : function(evt)
{
var e = this.get_element();
if (e != null)
{
e.className = this._defaultCssClass;
}
},
get_defaultCssClass : function()
{
return this._defaultCssClass;
},
set_defaultCssClass : function(value)
{
this._defaultCssClass = value;
this.raisePropertyChanged('defaultCssClass');
},
get_onFocusCssClass : function()
{
return this._onFocusCssClass;
},
set_onFocusCssClass : function(value)
{
this._onFocusCssClass = value;
this.raisePropertyChanged('onFocusCssClass');
}
}
Extenders.FocusBehavior.registerClass('Extenders.FocusBehavior', Sys.UI.Behavior);

Next, Extenders.dll  can be added to any Web Site project and any control can gain new behavior, which will allow to change its color when it got focus.
Default.aspx
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
<%@ Register Assembly="Extenders" Namespace="Extenders" TagPrefix="MI" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title>Untitled Page</title>
<style type="text/css">
.Normal
{
background-color: White;
color:Black;
font-weight:normal;
}
.Highlight
{
background-color: Blue;
color:White;
font-weight:bold;
}
.HighlightButton
{
background-color: Yellow;
color:Red;
font-weight:bold;
}
</style>
</head>
<body>
<form id="form1" runat="server">
<div>
<asp:ScriptManager ID="ScriptManager1" runat="server" />
Username:
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox>
Password:
<asp:TextBox ID="TextBox2" runat="server" TextMode="Password"></asp:TextBox>
<asp:DropDownList ID="DropDownList1" runat="server">
<asp:ListItem>Value1</asp:ListItem>
<asp:ListItem>Value2</asp:ListItem>
</asp:DropDownList>
<asp:Button ID="Button1" runat="server" Text="Button1" />
<MI:FocusExtender ID="focusExtender1" runat="server" TargetControlID="TextBox1" OnFocusCssClass="Highlight"
DefaultCssClass="Normal" />
<MI:FocusExtender ID="focusExtender2" runat="server" TargetControlID="TextBox2" OnFocusCssClass="Highlight"
DefaultCssClass="Normal" />
<MI:FocusExtender ID="focusExtender3" runat="server" TargetControlID="DropDownList1"
OnFocusCssClass="Highlight" DefaultCssClass="Normal" />
<MI:FocusExtender ID="focusExtender4" runat="server" TargetControlID="Button1" OnFocusCssClass="HighlightButton"
DefaultCssClass="Normal" />
</div>
</form>
</body>
</html>

And here are the screenshots:


This very simple example shows how one control extender can change behavior of many different types of controls or add some custom javascript to the page. Some developers who are not experienced in Ajax client-side library and creating Ajax behaviors might find writing control extenders a little complex but nowadays it's almost impossible to be a thriving professional .NET ASP.NET Web Developer without having a good understanding of Ajax client-side programming as he cannot take a full advantage of .NET 3.5 and Visual Studio 2008. And the problem is that Ajax will be getting more and more popular so the sooner you get familiar with all features of Ajax, the easier it will be to learn new features of its next versions and next versions of .NET and Visual Studio. I hope this will encourage everyone to get familiar with control extenders and use them to add extra functionality to the existing controls when next time you will have to extend a control.

0 comments: