Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / HTML

Building AJAX scroller control using Accordion control toolkit as a model

4.81/5 (9 votes)
4 Jun 2007CPOL4 min read 1   532  
This article will show you that creating AJAX Control Toolkit like controls is not a hard task

Screenshot - AjaxScroller.gif

Introduction

Couple of weeks ago, I created a DHTML control, based on the DHTML posted on www.dynamicdrive.com. It was a data-bound control, and it was a nice experience. I had to go through all the details of amazing "Developing Microsoft ASP.NET Server Controls and Components", and I learnt a lot of new things. But, after I start reading about AjaxControlToolkit, and started analyzing the code, I realized that there is a lot easier way to achieve the same thing, in a lot more elegant way.

Background

The control, basically displays some HTML, the displayed HTML fades, and it's replaced with another. The control has to be templated, and data-bound. The ideal base for this requirement was AjaxControlToolkit's Accordion control. It's one of the more complicated controls, but it has everything I needed. The fading effect of the Animation extender I used is really one of the simplest, but still, it gives us the perfect way to start delving into this area of AJAX.

Using the code

You can use the scroller as a full featured data-bound control, or you can enter the content to be scrolled declaratively or programmatically, in the code. These are the steps:

  1. Create an AJAX enabled web site
  2. Add a reference to Scroller and AjaxControlToolkit assemblies, located in the bin folder of the Scroller control
  3. Register Scroller on the web page:

    <%@ Register Assembly="Scroller" Namespace="AjaxScroller" 
                            TagPrefix="cc1" %>    

  4. Add Scroller to the page:

    <cc1:scroller id="Sc1" runat="server" 
        contentcssclass="ScrollerPaneStyle" 
            cssclass="ScrollerStyle">
    <cc1:scroller>    

  5. Add <Panes> tag
  6. Add as many Scroller panes as you like:

    <cc1:ScrollerPane runat="server" ID="p1">
            <Content>
               Some text to be scrolled <br />
            </Content>
    </cc1:ScrollerPane>    

If you want to bind the scroller to the data source, the code should look like this:

ASP.NET
<cc1:Scroller ID="Scroller2" runat="server" DataSourceID="SqlDataSource1" 
        CssClass="ScrollerStyle" ContentCssClass="ScrollerPaneStyle" >
            <Panes></Panes>
            <ContentTemplate>
                <h2>The description of categorie <%# Eval("CategoryName") %> 
            <br /></h2>
                <%# Eval("Description")%>
            </ContentTemplate>
</cc1:Scroller>
<asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="
            <%$ ConnectionStrings:NorthwindConnectionString %>"
                SelectCommand="SELECT * FROM [Categories]">
</asp:SqlDataSource>

This Scroller is bound to the Northwind Categories description field(you will have to change the connection string in the web.config).

The most interesting properties of the Scroller are:

  • TransitionDuration – how long will the fading between panes last (default is 1000 ms),
  • FramesPerSecond – default is 30, and
  • ContentCssClass – which define the style of the content to be scrolled.

Control

Scroller is not an extender control. Instead, it uses an extender internally to extend its behavior. Like I said in the background, the control uses Accordion AjaxcControlToolkit control like its model. I won't go deep into explanation of this control, because it will need a lot more than one article, but I will briefly explain all the component classes, and the most interesting points. Accordion control is a good model for creating a templated data-bound control, and most of the pattern is general. I just removed the parts which I don't need (Header template), added couple of things (additional div, so fading will not affect the border of the control, if it's present), and, the internal extender JavaScript had to be completely changed. I didn't remove event items related classes, because, in future scenarios, I could use this fast and easy. Just not to be confused with the code, ScrollerCommandEventArgs.cs and ScrollerItemEvent.cs are not used, and are here if you wish to add some additional functionality.

Classes

ScrollerContentPanel

This is a content pane in which the template will be instantiated. It follows a data-bound pattern, and the only deference from AccordationContentPanel is property InVisible, which will set display style of the panel (div) to "none" or "block". All the panels are invisible when the control is created, and the visibility is controlled in AjaxScrollerExtender.js. Like I said, there is OnBubbleEvent override, which is not used, but it's here for future use. This pane will be actually scrolled (faded) by the AJAX extender.

ScrollerPane

This is the container for the ScrollerContentPane, and it has one template – Content, which is instantiated in the ScrollerContentPane member – ContentContainer. Originally, Accordion control had one more template – Header, which is not needed for our Fading Scroller. The interesting point in this control is that it actually doesn't render begin/end tags, by overriding RenderBeginTag/RenderEndTag.

ScrollerPaneCollection

This is a collection of all ScrollerPanes which implements IList and IEnumerable. I didn't change anything in this class, except, of course, the type of the objects this collection contains.

Scroller

This the most interesting of all classes. The bulk of the control is standard code for the templated, data-bound control. The point I want to emphasize is - how to use extender to extend your control? Before that, let's first explain the actual extender which is used to extend Scroller. The name of the extender class is AjaxScrollerExtender. The AjaxScrollerExtender just defines the properties of the extender, and AjaxScrollerDesigner is doing a simple design time representation of the control. I will show the full AjaxScrollerBegaviour.js code, which has comments that will help you to go through it:

JavaScript
Type.registerNamespace('AjaxScroller');

AjaxScroller.AjaxScrollerBehavior = function(element)
{
    AjaxScroller.AjaxScrollerBehavior.initializeBase(this, [element]);
    // The this._framesPerSecond is used to tune the animation to 
    // perform well depending on the type of effect being used 
    // and the number of accordion panes, etc.
    this._framesPerSecond =  30;

    // The this._duration - how long each fading will last
    this._duration =  1;

    // The this._panes ScrollingPanes corresponds with 
    // ScrollingPane from the Scroller control
    this._panes =  [];

    // The this._currentPane is needed to change the content of the 
    // current fading div
    this._currentPane = 0;

    // This extender uses composite animation - FateIn and FadeOut. 
    // I didn't use Pulse animation because I got an error in Firefox.
    // I don't know if that is that a bug or not, but I had to
    // simulate Pulse with FadeIn and FadeOut. 
    // The this._fadeAnimation is the composite serial animation,
    // this._fadeIn and this._fadeOut are single animations.
    this._fadeAnimation = null;
    this._fadeIn =  null;
    this._fadeOut = null;

    // The this._hoverOverHendler and this._endedHandler are used to pause, 
    // restart animation
    this._hoverOverHendler = null;
    this._hoverOutHendler = null;

    // The this._endedHandler is used to change the content to animate, 
    // and restart the animation
    this._endedHandler = null;
    //  this._autoSize = 0;
}

AjaxScroller.AjaxScrollerBehavior.prototype =
{
    initialize : function() {
        AjaxScroller.AjaxScrollerBehavior.callBaseMethod(this, 'initialize');
    // The structure of the HTML is:
    // <div id="Scroller1a"  style="overflow:hidden;overflow-x:auto;">
    //  <div>
    //        <div> content1 </div>
    //        <div> content2 </div>
    //      .....
    //  </div>
    // </div>
    // According to the structure, the following code populates 
    // the panes array.

        var e = this.get_element();
        var firstchildren = e.childNodes;
        for (var i = 0; i < firstchildren.length; i++) {
            var child = firstchildren[i];
            if (child.nodeType == 1) {
                firstDiv = child;
                break;
            }
        }
        var children = firstDiv.childNodes;

        for (var i = 0; i < children.length; i++) {
            var child = children[i];
            if (child.nodeType == 1) {
                Array.add(this._panes, child);
            }
        }

       if (this._panes.length > 0)
       {
       // Here, we create the animation.
       this._fadeIn = new AjaxControlToolkit.Animation.FadeInAnimation
       (firstDiv, this._duration, this._framesPerSecond,  0, 1, true);
       this._fadeOut = new AjaxControlToolkit.Animation.FadeOutAnimation
       (firstDiv, this._duration, this._framesPerSecond,  0, 1, true);
       this._fadeAnimation = 
        new AjaxControlToolkit.Animation.SequenceAnimation
            (firstDiv, this._duration, this._framesPerSecond, 
                [this._fadeIn, this._fadeOut], 1);
       // When the simulated Pulse finishes, switch the visible div, 
       // and start new animation.
       this._endedHandler = Function.createDelegate(this, this._switchDiv);
       this._fadeAnimation.add_ended(this._endedHandler);
       //If user hovers over the div, stop the animation.
       this._hoverOverHendler = Function.createDelegate(this, this._hoverOver);
       this._hoverOutHendler = Function.createDelegate(this, this._hoverOut);
         $addHandlers(e, {
            mouseover: this._hoverOverHendler,
            mouseout: this._hoverOut,
            focus: this._hoverOverHendler,
            blur: this._hoverOut
            }, this);

       // Set the first visible div.
        this._panes[this._currentPane].style.display = "block";
        //  Let's start!
        this._fadeAnimation.play();
        }
    },

        // Clean the handlers we created.
    dispose : function()
    {
        if (this._endedHandler)
        {
            this._fadeAnimation.remove_ended(this._endedHandler);
            this._endedHandler = null;
        }
        $clearHandlers(this.get_element());


        AjaxScroller.AjaxScrollerBehavior.callBaseMethod(this, 'dispose');
    },

        // Cycle through the panes and set the visibility for the current.
        // Play the animation for the current.
    _switchDiv : function()
    {
        var currentPane = this._panes[this._currentPane];
        currentPane.style.display = "none";
        //Sys.Debug.fail("u pr");
        this._currentPane++;
        if (this._currentPane == this._panes.length) this._currentPane = 0;
        var nextPane = this._panes[this._currentPane];
        nextPane.style.display = "block";
        this._fadeAnimation.play();
    },

    _hoverOver: function()
    {
        this._fadeAnimation.pause();
    },

    _hoverOut: function()
    {
        // The play method doesn't work good after Pause – is it a bug?
        // Just Stop, and Play will fix the problem.
        this._fadeAnimation.stop();
        this._fadeAnimation.play();
    },
 .....

So, back to the point – how do you use this extender to extend your control! Just declare the private AjaxScrollerExtender property in your control, and then in CreateChildControls override it , assign it an ID, and set the TargetControlID to the ID of your control:

C#
_extender = new AjaxScrollerExtender();
            _extender.ID = ID + "_ScrollerExtender";
            _extender.TargetControlID = ID;

            if (!this.DesignMode)
                Controls.AddAt(0, _extender);

Points of Interest

The main point of this article is how easily you can create full featured AJAX controls. This control can be pretty easily extended to use a lot more sophisticated AJAX animations for creation of different text effects. For instance, adding color animation can be done with just a couple of lines. Animation extender from the AJAX Control Toolkit gives you a very strong base, and the only limit is your creativity.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)