An “Ajaxable” Panel


Recently I had the pleasure of modifying a melting pot of Commerce Server out-of-the-box “crap” code, home-brewed web parts designed to be functionally similar to the crap code, but have much better structure. There were four web parts in total, and even though they had to communicate through SharePoint connections, they functioned in isolation. Anyone that has worked with a Commerce Server/SharePoint/BizTalk setup knows that the out-of-the box code runs horribly. Everything renders a post back, and the pages “feel” clunky.

With a one-week deadline looming and “ship-the-shit” mode activating, a new requirement came in that the entire page had to be “ajaxed” (clients term, not mine). The other unwritten requirement was that we needed to be able to perform performance testing, and the home-brew toolkit wasn’t ajax-friendly. My goal was to create a container that could be deployed to various areas of the application to make our post-back laden website feel a little lighter weight by using UpdatePanels. And thus was born a small utility called the “AjaxablePanel.” The complete code is below:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Common
{
    public class AjaxablePanel
    {
        private static bool UseAjax = true;
        private static UpdatePanelUpdateMode AjaxUpdateMode = UpdatePanelUpdateMode.Always;

        private bool _autoCreateUpdateProgress = false;

        public bool AutoCreateUpdateProgress
        {
            get { return _autoCreateUpdateProgress; }
            set { _autoCreateUpdateProgress = value; }
        }

        public string ProgressText { get; set; }

        private string _progressImageUrl = "/Style Library/images/ajax-loader.gif";
        public string ProgressImageUrl
        {
            get { return _progressImageUrl; }
            set { _progressImageUrl = value; }
        }

        private Control _containerControl;
        public Control Control
        {
            get { return _containerControl; }
        }

        ControlCollection _containerControlCollection;

        public string ID
        {
            get { return (_containerControl == null) ? string.Empty : _containerControl.ID; }
            set { if ( _containerControl != null ) _containerControl.ID = value; }
        }

        public AjaxablePanel()
        {
            if (UseAjax)
            {
                _containerControl = new UpdatePanel()
                {
                    UpdateMode = AjaxUpdateMode
                };
                _containerControlCollection = ((UpdatePanel)_containerControl).ContentTemplateContainer.Controls;
            }
            else
            {
                _containerControl = new Panel();
                _containerControlCollection = _containerControl.Controls;
            }

            _containerControl.Load += new EventHandler(_containerControl_Load);
        }

        void _containerControl_Load(object sender, EventArgs e)
        {
            if (this.AutoCreateUpdateProgress && UseAjax)
            {
                UpdateProgress progress = new UpdateProgress()
                {
                    DisplayAfter = 200,
                    AssociatedUpdatePanelID = _containerControl.ID,
                    ProgressTemplate = new ProgressTemplate(ProgressImageUrl, ProgressText)
                };

                Panel p = new Panel();

                _containerControlCollection.Add(p);
                _containerControlCollection.Add(progress);
                _containerControlCollection = p.Controls;
            }
        }

        public AjaxablePanel(string ControlID) : this()
        {
            _containerControl.ID = ControlID;
        }

        public AjaxablePanel(string ControlID, bool AutoCreateUpdateProgress)
            : this(ControlID)
        {
            this.AutoCreateUpdateProgress = AutoCreateUpdateProgress;

        }

        public void AddControl(Control c)
        {
            _containerControlCollection.Add(c);
        }

        public void ClearControls()
        {
            _containerControlCollection.Clear();

        }

        public void RegisterPostbackScript(Control control, Type type, string key, string script, bool addScriptTags)
        {
                ScriptManager.RegisterStartupScript(control, type, key, script, addScriptTags);
        }

    }
}

Some basic functionality was added for adding sub-controls, clearing the panel, and registering JavaScript. Something that may need further examination is logic to determine what script manager to use when registering client script. Currently it is assumed we are in “Ajax” mode when adding client script. The other “nicety” added was the ability to automatically add an UpdateProgress if Ajax is enabled. This provides some nice visual feedback “for free.” The template is not included only for lack of access right now, but it follows the same format as the MSDN documentation.

The basic structure I believe is pretty self-evident, but there are a few areas I’m most proud of. First off, this was not meant to be able to enable partial-page updates on a control-by-control basis, but for a developer to be able to turn off AJAX calls across the entire application by changing a single boolean. Additionally, since the website was developed without partial-page postbacks in mind, we needed the UpdateMode to be pinned at always to maintain functionality in several areas. The primary difficulty when trying to swap out UpdatePanels and standard containers is that an UpdatePanel doesn’t have “controls” directly attached to it, but rather to it’s template. In order to satisfy this, the local variable _containerControlsCollection was added and intelligently references either Panel.Controls or UpdatePanel.ContentTemplateContainer.Controls. A little additional trickery is thrown in if we are automatically creating the ProgressUpdate to keep the update indicator towards the bottom. We create a new container panel within the UpdatePanel.ContentTemplateContainer, use that for _containerControlsCollection, and simply add the UpdateProgress to the ContentTempalteContainer after the panel.

The end result is using this simple utility as the topmost panel of all our web parts, we provide convenient access to an ajax-style UI but the immediate availability of post-backs for performance testing.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s