Ajax Control Toolkit
Extender base class features

All of the controls in the Toolkit are built upon a set of classes that really help streamline the process of writing control extenders. These classes form a thin "glue" layer between the Toolkit controls and the ASP.NET AJAX classes as they exist today.

To get the most out of this document, you should have written a control with the Toolkit, or at least read about creating a new extender.

The Extender Classes automate a couple of major things:

  • The generation of script that hooks up your behavior to an element on the page
  • The loading of your script file reference
  • The management of any scripts that your behavior or control may be dependent on
  • The mapping of properties and their values from the managed side to the client script
  • Help in developing and debugging your script
  • A mechanism to move state back-and-forth between your extender and the behaviors in the browser

The main class is called AjaxControlToolkit.ExtenderControlBase (ECB for the purposes of this document). ECB is an abstract class, which means you can't instantiate it directly; you have to create a derived type. The main job of this component is to hook up an HTML element with some behaviors on the page. Derived classes can add a TargetControlTypeAttribute to restrict the types of elements they can be hooked up to.

On this class is where you declare your managed object model. A simple one would look like this:

[TargetControlType(typeof(IButtonControl))]
    public class ConfirmButtonExtender : ExtenderControlBase
    {
        [ExtenderControlProperty()]
        public string ConfirmText
        {
            get
            {
                return GetPropertyValue("ConfirmText", "");
            }
            set
            {
                SetPropertyValue("ConfirmText", value);
            }
        }
    }
    

The general lifecycle works as follows. The developer adds an extender to their ASPX page:

<ns:MyExtender ID="myExtender1" runat="server">
TargetControlID="Button1"
SomeValue="Foo" />

When the page loads, this instantiates a server control that derives from ECB above. The server control talks to ASP.NET AJAX to hook itself up, and the client browser automatically downloads and initializes all the necessary scripts.

ExtenderControlBase Features:

RequiredScripts: You can specify which scripts are required for a given extender using the RequiredScriptAttribute.

[RequiredScript(typeof(MyOtherExtender))]
public MyExtender()
{

}

This attribute takes a type that references another script file that needs to be loaded prior to your component's scripts. This allows you to build extenders that have behaviors that derive from or use other behaviors. RequiredScriptAttribute also has a LoadOrder property as well to enforce which scripts get loaded in which order. This loading is recursive as well - if the extenders you reference also have dependencies, they will automatically be created.

[RequiredScript(typeof(MyExtender),0)]
[RequiredScript(typeof(MyOtherExtender),1)]
public
MyCombinedExtender() { }

ScriptPath: This is a debugging helper property that really simplifies your script development. Once you've got your ECB class created, create an ASPX page. Copy your “MyBehavior.js” file next to that ASPX page, and then add the following (in bold) to your extender declaration:

<ns:MyExtender ID="myExtender1" ScriptPath="MyBehavior.js" TargetControlID="Button1"
runat="server">

</ns:<MyExtender>

This will cause the extender to load your script from the URL you give it instead of from the DLL, which is the default. This allows you to debug and modify your script file without having to rebuild your extender. When you're done, don't forget to copy this file back into your project.

BehaviorID: In cases where you would like to access the client-side behavior for your extender from script code in the client, you can set this BehaviorID to simplify the process. See example below:

<ns:MyExtender ID="myExtender1"
runat="server" TargetControlID="Button1" BehaviorID="myBehavior1" />


<script type="text/javascript">
function changeValue() {
var myBehavior = $find("myBehavior1");
myBehavior.set_Value(0);
}
</script>

ClientScriptResourceAttribute: This isn't a method on ECB but it's related to it. This is what associates your ECB instance with a script file. Normally, it looks like this:

[ClientScriptResource("MyNamespace.MyBehavior",
"MyProject.MyBehavior.js")]

But if you want to wrap an existing set of scripts in ASP.NET AJAX so that you can easily use it on the server-side, you just pass null for the namespace and drop the resource name:

[ClientScriptResource(null, "SomeScripts.js")]

ResolveTargetControlID: There are times where it is inconvenient to put the extender directly next to the control that it is extending. In cases where a NamingContainer is involved, this can prevent the extender from being able to locate it's target control. In these cases, the user can handle the ResolveTargetControlID event on the extender. If the ECB is unable to locate the control, this event will be called with the control's ID. In this event, the user code can locate the control and return the instance. Note that this fires for TargetControlID only, not for other properties marked with the IDPropertyAttribute. We may add that support later.

   protected void MyExtender_ResolveTargetControlID(
                        object sender, ResolveControlEventArgs e) {
        if (e.ControlID == "Button1") {
            // Button1 is in another container -
            // find it.
            e.Control = container1.FindControl(e.ControlID);
        }
      }
      

EnsureValid: When you create your ECB instance, you override this to make sure that the user has set all of the appropriate values. The default implementation checks that all of the properties marked with the RequiredPropertyAttribute are populated.

EnableClientState: This property enables client state transport between your extender and the class running on the client. See the ClientState property below. This defaults to false, so if you don't set it, ClientState will do nothing.

ClientState: This is a simple string property that will be made available to your running behavior instance on the client side. So you could do something like this on the server side:

protected void Page_Load(object sender, EventArgs e)
{

MyExtender1.ClientState = "hello";
}

And on the client side, (in Javascript):

var state = this.get_ClientState(); // returns "hello"

It's that simple. There is a corresponding set_ClientState method on the Javascript side that you can call as well to change the value that the server-side receives. These values persist only for the life of the page. If the user refreshes or navigates away and back to the page, they start from their initial state again.

Adding Extender Properties

To add extender properties to your class, you need to let the Toolkit framework know which properties it should send to the client.

The first property is a required one. Put this property on all properties that are related to your client-side behavior:

[ExtenderControlProperty]
public string SomeValue { … }

ExtenderControlPropertyAttribute: Marks a property to be sent to the client side behavior.

Here are the optional attributes you can use on the ECB properties that you create:

DefaultValueAttribute: Put this on a TPB property to tell the framework what the default value is for a property. Note this does not set that value, but is a comparison. So, of you've got a string value, you would normally put [DefaultValue(null)] or [DefaultValue(0)] for an int. The benefit of this is that it prevents the ECB from serializing out default values. It sounds simple but it's pretty important to get things working right.

ClientPropertyNameAttribute: This allows you to declare a different property name in your managed code from your client script code. For example, you may want to have your managed class names start with an upper-case character, and your client script ones start with a lower case character. Or you may be wrapping an existing behavior and prefer other names.

[ClientPropertyName("someValue")][ExtenderControlProperty]
public string SomeValue { … }

IDReferenceAttribute: This attribute tells the ECB that it references the ID of a control on the page. So during rendering, the ECB will replace this value with the Client ID of the control. Note this also tells the designer what kind of controls can be set into this property:

[IDReferenceProperty(typeof(WebControl))]
[DefaultValue("")]
[ExtenderControlProperty]

public string PopupControlID { … }

ElementReferenceAttribute: This is related to the IDReferenceAttribute above. This tells the framework that the property on the client side takes an element reference rather than an ID. In this case, the framework will automatically look up the component and pass it's value to the client instead of the ID. This will usually be done in conjunction with the ClientPropertyNameAttribute as in the example below:

[IDReferenceProperty(typeof(WebControl))]
[DefaultValue("")]
[ExtenderControlProperty]
[ClientPropertyName("popupElement")]
[ElementReference]
[ExtenderControlProperty]

public string PopupControlID { … }

One last thing is on the AjaxControlToolkit.Design.ExtenderControlBaseDesigner

class. In most cases you won't need to do anything with this class but there is one virtual member:

ExtenderPropertyName: Override this to control the name of the property that shows up in the property grid for your extender when you click on the item that you want to extend.

protected override string ExtenderPropertyName
{

get
{

return "TheNameToShowInThePropertyGrid";
}
}



Copyright © 2006-2009 Microsoft Corporation. All Rights Reserved.