Members

Technology Zones

IBM Learning Center

Articles

Hosted By

MaximumASP

Info

This resource has not currently been approved, and is not currently linked to from our directory of resources. It is being displayed here for preview by the author and moderators only.
Rated
Read 638 times

Contents

    Building ASP.NET AJAX Controls - Components

    Components

    I expect most people reading this are familiar with ASP.NET AJAX and maybe have some experience with the AJAX Control Toolkit (which is full of ASP.NET AJAX controls, mainly extenders). But you still may be asking yourself "What, exactly is an ASP.NET AJAX control and why should I care?". Well, we could define an ASP.NET AJAX control as a control (client or server) that takes advantage of ASP.NET AJAX (Extensions or Library) to enrich its functionality. But I'm not sure that's terribly helpful. How about we take an example...

    In my VS 2008 Launch Session I used a simple control which displayed a picture of a property (my launch session was built around a fictitious "houses" database) and, when you mouseover the picture, it changes to show a Virtual Earth map of the property location thus:

    Control

    Of course you could build such a control without ASP.NET AJAX but building it with ASP.NET AJAX offers advantages including a component model framework and cross-browser compatibility features to name but two. It essentially makes it much easier to create controls with client-site JavaScript functionality.

    Can I take it for read you're still interested? Where should we start? We should start on the client and build up from there. There are 3 client (JavaScript) types that you should be familiar with:

    Sys.Component from which derive Sys.UI.Behavior and Sys.UI.Control. The key differentiator is whether you are building a visual (Behavior / Control) or non-visual (Component) class. Here's how I described them in summary form in my session:

    • Sys.Component (eg Timer)
      • Encapsulate client code for re-use
      • Non-visual
      • No associated DOM element
    • Sys.UI.Behavior (eg Highlight)
      • Extends an existing DOM element
      • Associated with a DOM element
    • Sys.UI.Control (eg HighlightTextBox)
      • Represents a DOM element as a client object
      • Typically adds some functionality

    So if you want to build a simple timer that just raised events at set intervals, you need a Component. If, on the other hand, you want to have some UI associated with your class, you need either a Behavior or a Control. Which one you want can be a little tricky to describe. A Behaviour is associated with a DOM element to add some new functionality and you can therefore add multiple Behaviors to a single DOM element. A Control on the other hand is intended to represent or encapsulate an enhanced DOM element and you can only have a single Control associated with a DOM element. Behaviors and Controls have a lot in common but you'll see where they come into their own when we start to talk about server controls.

    We've not got very far as yet but we need to at least get some common ground under our feet. Next I want to talk about common features of these types (ie what do I get if I buy into this and derive from one of them), how to create them and how to access them at runtime. At that point, we might even be able to do something vaguely useful... Then we can walk through building the server control I mentioned above.

    Let's look at what these types offer us:

    Picture1

    Sys.Component is the base class for the other two types and implements (amongst other things):

    • Sys.IDisposable (I implement a dispose() method to clean up resources)
    • Sys.INotifyDisposing (I have a disposing event you can register an interest in)
    • Sys.INotifyPropertychange (I have a propertyChanged event you can register an interest in)
    • An events property of type EventHandlerList used to maintain a list of event handlers mapped to the component's events
    • An id property used to identify the component
    • A raisePropertyChanged() method used to raise the propertyChanged event
    • Sys.UI.Control adds an element property that represents the associated DOM element
    • Sys.UI.Behavior adds an element property and a name property (as you can have multiple behaviors associated with a single DOM element you need a way to uniquely identify them)

    Let's take a look at a definition of a component. Conveniently, in Visual Studio 2008 / ASP.NET 3.5 there is a new item template for both a behavior and a control:

    Picture2

    If I opt for a behavior, this is what I get (NB to change this or a control definition to a component is a trivial task):

    Type.registerNamespace("WebSite1");
    
    WebSite1.ClientBehavior = function(element) {
        WebSite1.ClientBehavior.initializeBase(this, [element]);
    }
    
    WebSite1.ClientBehavior.prototype = {
        initialize: function() {
            WebSite1.ClientBehavior.callBaseMethod(this, 'initialize');
            
            // Add custom initialization here
        },
        dispose: function() {        
            //Add custom dispose actions here
            WebSite1.ClientBehavior.callBaseMethod(this, 'dispose');
        }
    }
    WebSite1.ClientBehavior.registerClass('WebSite1.ClientBehavior', Sys.UI.Behavior);
    
    if (typeof(Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();

    If you're well acquainted with JavaScript this probably looks pretty familiar. If not, it may look a little daunting. Let me try and explain. Firstly, the ASP.NET AJAX client framework allows me to register namespaces just as I can in .NET. We can then break down out component definition into 3 parts; constructor, class definition and registration.

    You can think of the Website1.ClientBehavior assignment as the equivalent of a .NET constructor. Each time we create an instance of our ClientBehavior class that anonymous function code will be executed giving us an opportunity to perform any actions required (including initialising the base class with a call to initializeBase()).

    The JavaScript prototype model is used to allow us to create a definition that can be re-used or cloned multiple times. Every object in JavaScript has a prototype object that it inherits from. Defining properties on the ClientBehavior's prototype object ensures these properties are shared across all instances. Defining methods here makes them available to every instance of our component. If we define a field in the prototype that value is also shared across all instances; not necessarily what we want. If we want an instance variable, we should define it in the constructor and expose it via property getters and setters in the prototype. We'll see all this in action shortly.

    Finally we register our new type with ASP.NET AJAX via the call to registerClass() specifying the base type (in this case Sys.UI.Behavior). The last line of code ensures that we notify the ScriptManager that we're done so it can carry on with its next action.

    Now this all sounds a bit complicated but in essence, if you start with the template it's pretty straightforward to modify to achieve what you want. I should say this all holds true for ASP.NET 2.0 with the ASP.NET AJAX Extensions as well (though you'll need to manually copy the templates from somewhere as I don't think you get the new item templates in Visual Studio).

    Let's take the template above and modify it to be a basic Sys.Component that helps us to see the component lifecycle:

    Type.registerNamespace("DevWeek");
    
    DevWeek.SimpleComponent = function() {
        DevWeek.SimpleComponent.initializeBase(this);
        alert('Simple Component Constructor');
    }
    
    DevWeek.SimpleComponent.prototype = {
    
        initialize: function() {
            DevWeek.SimpleComponent.callBaseMethod(this, 'initialize');
            
            // Add custom initialization here
            alert('Simple Component Initialising');
        },
        
        dispose: function() {        
            //Add custom dispose actions here
            alert('Simple Component Disposing');
            DevWeek.SimpleComponent.callBaseMethod(this, 'dispose');
        }
    }
    DevWeek.SimpleComponent.registerClass('DevWeek.SimpleComponent', Sys.Component);
    
    if (typeof(Sys) !== 'undefined') Sys.Application.notifyScriptLoaded();

    Notice there are a few alerts() in there which will hopefully help us to understand what's going on. But before we get there we need to know how to create and use one of these things. Well there's nothing stopping you creating an instance provided you perform the right sequence of steps but there is a create() helper method on the Sys.Component type (with a $create shortcut) that does the hard work for you.

    So to create an instance of our new DevWeek.SimpleComponent class all I need to do is make a call to $create during Sys.Application.init (the initialisation event of the Sys.Application object). In our case this would be something as simple as:

    Sys.Application.add_init(pageInit);
    
    function pageInit() {
        $create(DevWeek.SimpleComponent,    // class
            {},                             // properties
            {},                             // events
            {},                             // references
            null);                          // associated element
    }

    ie we add a handler to the Sys.Application.init event and in there call $create passing the type of component we want to create as the first parameter. Other parameters on $create allow us to set property values, hook up event handlers, add references to other components and associate our component with a DOM element on the page (to be used with behaviors and controls).

    Sys.Component.create() does a number of things:

    • It creates an instance of the specified component
    • It "wires up" properties, events etc as specified in the parameters
    • It calls the initialise() method on the component
    • It adds the component to the Sys.Application object which hosts and tracks client components created in the page. This provides an easy way to access the component again (we'll get to that) and also ensures that the dispose() method on the component is called when the page is unloaded.

    If we create a simple aspx page as follows

    <%@ Page Language="C#" %>
    
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head runat="server">
        <title>Simple Component</title>
    </head>
    <body>
        <form id="form1" runat="server">
        <asp:ScriptManager ID="ScriptManager1" runat="server">
            <Scripts>
                <asp:ScriptReference Path="~/Component.js" />
            </Scripts>
        </asp:ScriptManager>
    
        <script type="text/javascript">
    
        Sys.Application.add_init(pageInit);
    
        function pageInit() {
            $create(DevWeek.SimpleComponent,    // class
                {},                             // properties
                {},                             // events
                {},                             // references
                null);                          // associated element
        }
    
        </script>
    
        </form>
    </body>
    </html>

    and save our component definition in a file called Component.js then when we run the page we'll see two alerts "Simple Component Constructor" (we're in the constructor code) and "Simple Component Initialising" (in the initialise method). Navigate away from the page or close the browser and the "Simple Component Disposing" (in the dispose method) alert will pop-up.

    Next on the list is accessing the components we've created as well as adding properties and events. We're at the stage now where we can start to use my control from the launch session to illustrate some of these concepts and ultimately build them into an ASP.NET AJAX server control you can just drop onto an aspx page.

    James first started writing tutorials on Visual Basic in 1999 whilst starting this website (then known as VB Web). Since then, the site has grown rapidly, and James has written numerous tutorials, articles and reviews on VB, PHP, ASP and C#. In October 2003, James formed the company Developer Fusion Ltd, which owns this website, and also offers various development services. In his spare time, he's a 3rd year undergraduate studying Computer Science in the UK. He's also a Visual Basic MVP.

    Comments