FREE BOOK

Chapter 11 - Adding Client Capabilities to Server Controls Using the ASP.NET AJAX Control Toolkit

Posted by Addison Wesley Free Book | ASP.NET & Web Forms September 21, 2009
In this chapter, we delve into the details of the toolkit a little further as we develop as series of extender controls that demonstrate the rich features the toolkit provides.

Overview of Declarative Syntax

To get started, let's look at the HTML source we will be working toward being able to work with in our ImageAnimation extender. The source in Listing 11.14 contains an ImageAnimationExtender tag that contains in its body an Animations tag. As you might guess, the approach here is to add various animations that are driven by events raised by the image control we are extending. In our case, we are working with the OnLoad event and adding a Sequence animation that will call a child Fade animation. A Sequence animation is designed to run all its child animations one at a time until all have .nished. So, what this source tells us is that our extender will have an animation that will be tied to the OnLoad event of the image control and will run the child Fade animation whenever the OnLoad event occurs.

Listing 11.14 AnimationImageExtender Declarative Syntax

<asp:Image ID="BannerImage" runat="server" ImageUrl="~/images/1.jpg" />
<cc3:ImageAnimationExtender ID="Banner_ImageAnimationExtender"
runat="server" Enabled="True" TargetControlID="BannerImage">
<Animations>
<
OnLoad>
<
Sequence>
<
FadeIn AnimationTarget="BannerImage" Duration=".3"/>
</Sequence>
</
OnLoad>
</
Animations>
</
cc3:ImageAnimationExtender>
<
cc2:ImageRotatorExtender ID="Image1_ImageRotatorExtender"
runat="server" Enabled="True" TargetControlID="Banner">
<cc2:ImageUrl Url="~/images/2.jpg" />
<cc2:ImageUrl Url="~/images/3.jpg" />
<cc2:ImageUrl Url="~/images/4.jpg" />
</cc2:ImageRotatorExtender>

Providing Declarative Support in Your Extender Class

The AnimationExtenderControlBase class provides most of the functionality we need to parse the Animation tag and all its contents. This class provides internal methods that convert the XML representation of the animation into JSON format, which our behavior will then use to run the animation, and also provides the Animation property that we see in Listing 11.15. The following sections cover the steps needed to ensure the extender will work correctly.

  1. Add attributes to the class.
  2. Create a property for the event.
  3. Add attributes to the property.

Add Attributes to the Class

This type of extender has a couple of added class attribute entries of interest to us. The .rst is the inclusion of the RequiredScript attribute for the AnimationExtender type. The AnimationExtender class provides a lot of the client-side functionality we will be using in our extender control, and by using this type in our RequiredScripts attribute, we are guaranteed that the scripts will be present on the client for us to use. The second attribute is the System.Web.UI.Design.ToolboxItem attribute, which enables our control to show up in the toolbox of Visual Studio. It might seem strange that we have to add this because all our other extenders didn't. If we look at the attributes on the AnimationExtenderControlBase class, however, the support for viewing in the toolbox has been turned off. Therefore, we must reset this value on our control so that it will show up in the toolbox.

Create a Property for the Event

The pattern when creating extenders of this type is to add a property for each event you want to interact with. In our case, we are working with the OnLoad event, so we create a property named OnLoad (to make it easy to understand what the event is). If we were to choose other events, we would name them based on the DOM event they represent. The property accessor for these events must use the GetAnimation and SetAnimation methods to ensure proper data conversion into JSON as the data is stored and retrieved out of the extender's view state.

Add Attributes to the Event Property

The event property must have the Browsable, DefaultValue, Extender ControlProperty, and DesignerSerializationVisibility attributes applied to it. The Browsable attribute stops the property from showing up in the Properties window and therefore excludes the property from being assigned in the Properties window. This is needed because no editor is associated with this property, and we don't want users to try to add anything into the Properties window that would corrupt the values. The Designer SerializationVisibility attribute with a value of DesignerSerialization Visibility.Hidden is used to indicate that the property value should not be persisted by the designer because the Animation property will take care of that for us. The DefaultValue attribute indicates to the designer that the default value will be null, and the ExtenderControlProperty attribute is used to register the property with the ScriptComponentDescriptor.

Listing 11.15 ImageAnimationExtender Class

[Designer(typeof(ImageAnimationDesigner))]
[ClientScriptResource("ImageAnimation.ImageAnimationBehavior",
"ImageAnimation.ImageAnimationBehavior.js")]
[RequiredScript(typeof(AnimationExtender))]
[ToolboxItem("System.Web.UI.Design.WebControlToolboxItem, System.Design,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
[TargetControlType(typeof(Image))]

public class ImageAnimationExtender : AnimationExtenderControlBase
{
    private Animation _onLoad;
    [DefaultValue(null)]
    [Browsable(false)]
    [ExtenderControlProperty]
    [DesignerSerializationVisibility(
    DesignerSerializationVisibility.Hidden)]
    public new Animation OnLoad
    {
        get { return GetAnimation(ref _onLoad, "OnLoad"); }
        set { SetAnimation(ref _onLoad, "OnLoad", value); }
    }
}

Adding Declarative Support to Your Behavior Class

The ImageAnimationBehavior class, shown Listing 11.16, provides all the client-side functionality for our extender with support from the animation script .les associated with the AutomationExtender class. These associated scripts provide support for converting the JSON representation of the FadeIn animation that was captured on the server to an actual animation, support for associating the animation with the high-level OnLoad event, and support for playing the animation when the OnLoad event occurs.

You need to complete a few steps for each event you plan to work with:

  1. Add variables to the class.
  2. Create functions.
  3. Add handlers.

Add Variables to the Class

Each event that your behavior will work with needs a variable that references the GenericAnimationBehavior for the event and a delegate that will

Listing 11.15 ImageAnimationExtender Class

[Designer(typeof(ImageAnimationDesigner))]
[ClientScriptResource("ImageAnimation.ImageAnimationBehavior",
"ImageAnimation.ImageAnimationBehavior.js")]
[RequiredScript(typeof(AnimationExtender))]
[ToolboxItem("System.Web.UI.Design.WebControlToolboxItem, System.Design,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")]
[TargetControlType(typeof(Image))]
public class ImageAnimationExtender : AnimationExtenderControlBase
{
    private Animation _onLoad;
    [DefaultValue(null)]
    [Browsable(false)]
    [ExtenderControlProperty]
    [DesignerSerializationVisibility(
    DesignerSerializationVisibility.Hidden)]
    public new Animation OnLoad
    {
        get { return GetAnimation(ref _onLoad, "OnLoad"); }
        set { SetAnimation(ref _onLoad, "OnLoad", value); }
    }
}

Adding Declarative Support to Your Behavior Class

The ImageAnimationBehavior class, shown Listing 11.16, provides all the client-side functionality for our extender with support from the animation script .les associated with the AutomationExtender class. These associated scripts provide support for converting the JSON representation of the FadeIn animation that was captured on the server to an actual animation, support for associating the animation with the high-level OnLoad event, and support for playing the animation when the OnLoad event occurs.

You need to complete a few steps for each event you plan to work with:

  1. Add variables to the class.
  2. Create functions.
  3. Add handlers.

Add Variables to the Class

Each event that your behavior will work with needs a variable that references the GenericAnimationBehavior for the event and a delegate that will be called for the event that will be processed. In the ImageAnimation Behavior class, we use the _onLoad variable to store a reference to the GenericAnimationBehavior class and the _onLoadHandler variable to store a reference to the delegate that will handle the onLoad event. The guidelines established so far in the toolkit use a naming convention that includes the event name in all the variable names.

Create Functions

The behavior needs a series of functions for each event you will work with. The get_OnLoad and set_OnLoad functions in our case take care of working with the JSON-based data for the FadeIn animation and utilize the functionality provided by the GenericAnimationBehavior class to store and retrieve that data. The get_OnLoadBehavior function returns a reference to the GenericAnimationBehavior instance that was created for our FadeIn animation, providing the ability to work with the behavior that directly exposes the play, stop, and quit methods common to all animations.

Add Handlers

Handlers must be added for each event the behavior will process and should correspond to the events exposed on the extender control. In our case, we are working with the onLoad event, so we need to create the _onLoadHandler delegate and associate it with the onLoad event of the image using the $addHandler shortcut. The opposite of this must happen in the dispose of our behavior, when we use the $removeHandler shortcut to ensure proper memory cleanup.

Listing 11.16 ImageAnimationBehavior Class

Type.registerNamespace('ImageAnimation');
ImageAnimation.ImageAnimationBehavior = function(element) {
ImageAnimation.ImageAnimationBehavior.initializeBase(this, [element]);
this._onLoad = null;
this._onLoadHandler = null;
}
ImageAnimation.ImageAnimationBehavior.prototype = {
initialize : function() {
ImageAnimation.ImageAnimationBehavior.callBaseMethod(this,
initialize');
var element = this.get_element();
if (element)
{
this._onLoadHandler = Function.createDelegate(this,
this.OnLoad);
$addHandler(element, 'load', this._onLoadHandler);
}
},
dispose : function() {
ImageAnimation.ImageAnimationBehavior.callBaseMethod(this,
'dispose');
var element = this.get_element();
if (element) {
if (this._onLoadHandler) {
$removeHandler(element, 'load', this._onLoadHandler);
this._onLoadHandler = null;
}
}
this._onLoad = null;
},
get_OnLoad : function() {
return this._onLoad ? this._onLoad.get_json() : null;
},
set_OnLoad : function(value) {
if (!this._onLoad) {
this._onLoad = new
AjaxControlToolkit.Animation.GenericAnimationBehavior(
this.get_element());
this._onLoad.initialize();
}
this._onLoad.set_json(value);
this.raisePropertyChanged('OnLoad');
},
get_OnLoadBehavior : function() {
return this._onLoad;
},
OnLoad : function() {
if (this._onLoad) {
this._onLoad.play();
}
}
}
ImageAnimation.ImageAnimationBehavior.registerClass(
'ImageAnimation.ImageAnimationBehavior',
AjaxControlToolkit.BehaviorBase);

Final Thoughts

The HTML source for our sample, shown Listing 11.14, contains a Fade animation that targets the BannerImage control and runs for a duration of .3 seconds. We could have chosen almost any type of animation as long as it occurred when the OnLoad event .red on the BannerImage image control. This .exibility provides a JavaScript-free way to set up animations of any type when a pattern such as this is used. In fact, this is exactly how the Animation extender works; and if it weren't for the way it handles the OnLoad event, we would have used it in our example.

Total Pages : 7 34567

comments