To Update...or Not to Update.. that is the question...

   OK.. so the ask is that we are looking for a server control that will use AJAX. We need this server control to use an UpdateProgress control.

   I started with the basic implementation of the server control like this:

using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

namespace MyControl
{

I want to Inherit from Control (since I am writing a control), so it looks like this:

public class MyControl : Control, INamingContainer
{

I'll need all the requirements, which are an UpdatePanel, a ScriptManager and an UpdateProgress control. Let's also add a button and a label so we can test the control:

private Label _label;
private Button _button;
private ScriptManager _manager;
private UpdatePanel _updatePanel;
private UpdateProgress _updateProgress;

OK.. good so far. Now here is where we need to stop for a second and think about our control. We can only have one ScriptManager on a page. So if my control adds the ScriptManager then I cannot add another instance of my control. Also if the page already has a ScriptManager, then my control won't work, because then we would be adding two ScriptManagers to the page. To get around this, we will declare a property called HasScriptManager. If this is true, then this control will create the ScriptManager, if it's false we just won't create a ScriptManager.

public bool HasScriptManager
{
get { return (this._manager == null); }
set
{
if (value)
{
if (this._manager == null)
{
this._manager = new ScriptManager();
this._manager.ID = "MyControlsScriptManager";
base.Controls.Add(this._manager);
}
}
else
{
if (this._manager != null)
{
base.Controls.Remove(this._manager);
this._manager = null;
}
}
}
}

   Now I'm going to call the constructor for my code. I'll go ahead an initialize my controls here:

public MyControl()
{
this._label = new Label();
this._button = new Button();
this._updatePanel = new UpdatePanel();
this._updateProgress = new UpdateProgress();
}

   Most of the work in MyControl will be done in the CreateChildControls. Note, that we have to tell our UpdateProgress control which UpdatePanel we want to link this to. We do this with the AssociatedUpdatePanelID property. The rest of this code is self explanatory:

protected override void CreateChildControls()
{
base.CreateChildControls();

    // Setup Label

    this._label.ID = "MyControlLabel1";
this._label.Text = "Not Posted Back";

    // Set ID of Update panel

    this._updatePanel.ID = "MyControlUpdatePanel1";

    // Literal Controls for formatting

    this._updatePanel.ContentTemplateContainer.Controls.Add(new LiteralControl("<H1>"));
this._updatePanel.ContentTemplateContainer.Controls.Add(this._label);
this._updatePanel.ContentTemplateContainer.Controls.Add(new LiteralControl("<br /><br />"));

    // Create Button

    this._button.ID = "MyControlButton1";
this._button.Text = "Make PostBack";
this._button.Click += new System.EventHandler(Button_Click);
this._updatePanel.ContentTemplateContainer.Controls.Add(this._button);

    base.Controls.Add(this._updatePanel);

    // Setup Update Progress

    this._updateProgress.AssociatedUpdatePanelID = this._updatePanel.ID;

    // Since we are doing this dynamically we have to create an item that implements
// the ITemplate interface

    MyTemplate template = new MyTemplate();

    this._updateProgress.ProgressTemplate = template;
Controls.Add(this._updateProgress);
}

 

You can see that I hooked up _button to the EventHandler Button_Click. Let's go ahead and get that handler written. In order to test this control, I am going to have the thread sleep for 5 seconds, so that we can test this control for some long running process. In the real world, obviously you would do something much smarter here!

protected void Button_Click(object sender, System.EventArgs e)
{
System.Threading.Thread.Sleep(5000);
this._label.Text = "Asynch Postback Complete!";
}

OK.. almost done. The UpdateProgress control needs a ProgressTemplate to work. If we added this control directly to a page it would look like this:

<asp:UpdateProgress ID="UpdateProgress1" runat="server">
<ProgressTemplate>
This is my UpdateProgress!
</ProgressTemplate>
</asp:UpdateProgress>

Well we can't do this in code.. so we have to create an object that is derived from the ITemplate interface. It looks like this:

private class MyTemplate : ITemplate
{
public void InstantiateIn(Control container)
{
LiteralControl l1 = new LiteralControl("Please Wait");
container.Controls.Add(l1);
}
}

That's it! If you take all this code and compile it in a class library, you can drop this control on your ASPNET AJAX enabled web pages, and you will get a control that implements AJAX Extensions for ASPNET 2.0. This could be a cool tool that you can use to quickly drop on a page and test AJAX functionality. In the real world, this sample will be used for a Sharepoint 2007 WebPart.

I hope this helps and saves you some time coding!