Atlas Control Toolkit -> AJAX Control Toolkit Migration Guide

AJAX Control Toolkit Migration Guide

This document briefly outlines the steps required to migrate an "Atlas" Control Toolkit extender to an AJAX Control Toolkit extender.

Note this guide is NOT for users consuming Toolkit components, but for users creating custom Toolkit components only. For component users, see the walkthrough on the Toolkit Website.

Also note, I've referenced examples (as file:line) in the codebase if you're looking for a given step in practice. Get the code for these examples here.

Server Components

Migrating server-side components is relatively straightforward. It mostly involves moving the contents of your MyProperties class into your MyExtender and decorating things with some new attributes.

 

1. Change the MyExtender class so it derives from ExtenderControlBase (e.g. without the generics)

2. Change the ClientScriptResource attribute from
      [ClientScriptResource("atlascontroltoolkit", "myExtenderBehavior",
"MyExtender.MyExtenderBehavior.js")]
to
      [ClientScriptResource("MyExtender.MyExtenderBehavior",
"MyExtender.MyExtenderBehavior.js")]

Where “MyExtender.MyExtenderBehavior” is the client class name for your component. See ConfirmButtonExtender.cs.
 

3. Move all properties declared in MyProperties to MyExtender, and add [ExtenderControlProperty(true)] to each of them. Be sure to move any other necessary overrides (like EnsureValid or ChecksIfValid) if needed. See any of the *Extender.cs files. For example:

    public class MyExtender : ExtenderControlBase
{
[ExtenderControlProperty(true)]
public string MyProperty
{
// . . .
}

    }

4. Decorate MyExtender with the TargetControlType attribute (in the Microsoft.Web.UI namespace) to indicate what types of controls it can extend. For example, to extend all controls it would be:
  

    [TargetControlType(typeof(Control))]
public class MyExtender : ExtenderControlBase { /* ... */ }

You can replace Control with anything that derives from it, like TextBox. See ConfirmButtonExtender.cs:26.

5. Remove any RequiredScriptAttributes that reference "Atlas" scripts (like DragDrop, UIGlitz, etc.). See DropShadowExtender.cs:20

6. In MyExtenderDesigner, change
    public class MyExtenderDesigner<MyExtenderProperties, Control> { /* ... */ }
to
    public class MyExtenderDesigner<MyExtender> { /* ... */ }

7. Move anything else in the MyExtenderProperties.cs file that you need into the extender and delete the properties file. See any *Extender.cs file.

 

8. See walkthrough document for instructions on migrating your markup code.

Closures to Prototypes

The next, and most involved step, is to migrate your client code. The Microsoft AJAX Library has changed its object-orientation strategy from a closure-based model to a prototype-based model.

1. Make the constructor of your behavior its own closure, and move all of the definitions into its prototype. See any *Behavior.js file. For example, if you are starting with this:
    AjaxControlToolkit.MyBehavior = function() {
AjaxControlToolkit.MyBehavior.initializeBase(this);
. . .
}

You would convert it to:

    AjaxControlToolkit.MyBehavior = function() {
AjaxControlToolkit.MyBehavior.initializeBase(this);
}
    AjaxControlToolkit.MyBehavior.prototype = {
. . .
}

2. The base Sys.UI.Behavior class now takes an element parameter in its constructor, which should also be passed to its base initialization. See ConfirmButtonBehavior.js:10. It should look like:

    AjaxControlToolkit.MyBehavior = function(element) {
AjaxControlToolkit.MyBehavior.initializeBase(this, [element]);
}

3. In the prototype, change all declarations to be comma separated definitions (note both the commas and colons) and any uninitialized primitive fields should be set to null. Including private fields and methods in the prototype will effectively convert them into public fields and methods. The Microsoft AJAX Library uses the convention that all private members are prefixed with an underscore. For example, if you had:

    AjaxControlToolkit.MyBehavior.prototype = { this.alpha = function() {
_foo = { };
beta('blah');
}
function beta(parameter) {
. . .
}
}

It should become:

    AjaxControlToolkit.MyBehavior = function(element) {
AjaxControlToolkit.MyBehavior.initializeBase(this, [element]);

      this._foo = null;
this._bar = null;
this._baz = this.gamma();
}

    AjaxControlToolkit.MyBehavior.prototype = {

      alpha : function() {
_foo = { };
_beta('blah');
},
_beta : function(parameter) {
. . .
}
}

4. Since all fields and methods are now members, all variable references and function calls must be prefixed by this. See any *Behavior.js file. Then alpha from above would become:

   alpha : function() {
this._foo = { };
this._beta('blah');
},

5. Any function that makes use of parameters to the behavior should be converted to not rely on the closure.

6. Remove any calls to AjaxControlToolkit.MyBehavior.registerBaseMethod(...); as they are no longer needed.

7. Remove calls to Sys.TypeDescriptor.addType(...); as they are no longer needed.

8. Change AjaxControlToolkit.MyBehavior.registerSealedClass and AjaxControlToolkit.MyBehavior.registerAbstractClass to AjaxControlToolkit.MyBehavior.registerClass. See ConfirmButtonBehavior.js:84

Other Changes

After you've converted your closures to prototypes, there are a number of other changes to make that will replace old "Altas" idioms with their newer Microsoft AJAX Library counterparts.

1. Sys.UI.Behavior no longer requires wrapping DOM elements in a Sys.UI.Control (the elements are now associated by passing them into the behavior's constructor). All references to this.control.element inside a behavior should be changed to this.get_element() ;. See ConfirmButtonBehavior.js:59.

2. All calls to someControl.get_style() should be replaced with element.style.

3. The $('...') alias for document.getElementById('...') has been changed to $get('...') . The $object('...') alias for Sys.Application.findControl('...') has also been changed to $find('...') . See PopupControlBehavior.js:52.

4. Congratulations! This step has been deleted! Please move on to step 5.

5. There is a new alias $create that wraps Sys.Component.create for instantiating new components. For example, to create an instance of your behavior you used to write (See PopupControlBehavior.js:55):

   var myBehavior = new AjaxControlToolkit.MyBehavior();

   var newControl = new Sys.UI.Control(targetElement);
myBehavior.set_foo(7);
myBehavior.set_bar(null);

   newControl.get_behaviors().add(myBehavior);
myBehavior.initialize();

becomes

   var myBehavior = $create(AjaxControlToolkit.MyBehavior,
{'foo' : 7, 'bar' : null }, null, targetElement);

This automatically calls initialize and registers the component for dispose, etc. Also note it is no longer necessary to create a control – the behavior can be directly associated with an element.

6. The event handling mechanisms have changed as well. It is important to note that they the event names should no longer be prefixed with on (for example, 'onclick' should become just 'click'). See ConfirmButtonBehavior.js:33. If you had the following code:

   element.attachEvent('oneventname', handler);
element.detachEvent('oneventname', handler);

You should change it to:

   $addHandler(element, 'eventname', handler);
$removeHandler(element, 'eventname', handler);

7. Declaring your own events is also significantly different. See DynamicPopulateBehavior.js:68. Instead of using:

   this.changed = this.createEvent();

becomes the following three functions:

   add_changed : function(handler) {
this.get_events().addHandler('changed', handler);
}
remove_changed : function(handler) {
this.get_events().removeHandler('changed', handler);
}
raiseChanged : function(eventArgs) {
var handler = this.get_events().getHandler('changed');
if (handler) {
if (!eventArgs) {
eventArgs = Sys.EventArgs.Empty;
}
handler(this, eventArgs);
}
}

8. Handling events is also slightly different. Your handlers should look like this, note the addition of the param of type Sys.UI.DomEvent (evt below), which unifies the differences between browser event objects. See DropDownBehavior.js:325.

_onMouseUp : function(evt) {

        var srcElement = evt.target;

          // …

    }

     

9. Creating new enumerations has changed to look similar to defining new classes. See PopupBehavior.js:10. While the old convention looked like:

   Type.createEnum('AjaxControlToolkit.MyEnum', 'OptionA', 0, 'OptionB', 1);

You now create them like with:

   AjaxControlToolkit.MyEnum = function() {
}
AjaxControlToolkit.MyEnum.prototype = {
OptionA : 0,
OptionB : 1
}
AjaxControlToolkit.MyEnum.registerEnum('AjaxControlToolkit.MyEnum', false);

10. If you need to check if a initialization is complete (e.g. so your setter methods don’t do work when the class is being initialized) call this.get_isInitialized(); See HoverBehavior.js:176.

11. If you have code that checks for browser versions, change as follows. See AlwaysVisibileControlBeahvior.js:99.

Possible values are Opera, FireFox, InternetExploer, and Safari. Sys.Browser also has a float-type version property.

Sys.Runtime.get_hostType() == Sys.HostType.InternetExplorer

To:

Sys.Browser.agent == Sys.Browser.InternetExplorer

Note there is also a version member on Sys.Browser, so you can test version numbers like “Sys.Browser.version < 6.5”