Skip Navigation Links » Blog » Ajax-10-XMLHTTP-callbacks-via
  • Ajax 1.0 XMLHTTP callbacks via Updatepanel in Custom Web Controls. POWERFUL and cake!!!

    by

    When using the brand new ajax library 1.0, officially released not more than a couple of weeks ago, we understand its a very rich control, really nice Smiley

    One question that i have always asked myself is what must i do if i want to incorporate ajax functionality into my custom controls. With this, i dont mean all the glory of the ajax library clientside api only, but also a serverside api that would allowed me to communicate with my clientside, transferring data back and forth based on client interaction with my control. Just like the functionality exposed by the updatepanel.

    Prior to the ajax libraries we made callbacks via an interface we had to implement on our pages or controls -->> ICallbackEventHandler Interface ;  It was very nice, gave us a lot of flexibility but i did find it quite limiting at times and a bit cumbersome too, simply because it werent bringing much to the table, so to speak. It did do a lot of work for us in the background for free. Eg, when implementing this interface we could make use of builtin functionality that took care of :

    1. RaiseCallbackEvent <--- A method that occurred serverside when a client callback request was made.
    2. GetCallbackResult <--- A second method on the serverside that allowed us to send back output, which had to be a string, no html was allowed without some tweaking to the code that made the callback because it only supported textResponse.
    3. Even though the entire page was requested, so the whole page went through its life cycle as if a normal took place, when the output was rendered back, only the string returned by GetCallbackResult was sent back
    4. Some built in functionality that actually allowed us to distinguish btw a postback and a client callback -->> Page.IsCallback
    And a few other things that you can explore for yourselves or already know Big smile

    The issue here is, your left with wanting more functionality, typical Tongue out I was a bit delusional when i found out that the ajax libraries werent bringing much innovation in this area and that i'd still have to use ICallbackEventHandler. Ofcourse, I was wrong.

    The solution was right there in front of me. The updatepanel! The updatepanel not only allows you to use it declaratively, but you can use it programmatically in code as well, adding triggers at runtime, adding the updatecontrol itself at runtime as if it were any other control, and above all nesting content in its content template if you need to.

    It's so cool, and very rich in functionality, however, vast as it is, the only thing i want to cover in this post is how to make use of the updatepanel to start a callback from your clientside code, catch the callback on the serverside, distinguish who made the callback, and take further action based on that, by either refreshing the content in the updatepanels content template OR sending data back to the client, which can include markup or just text Smiley

    And not only, the features are just too much to describe here, but the ajax library very nicely mirrors the asp.net serverside api on the clientside, so basically you can subscribe to events on the clientside for page_load etc. Here, what we want to subscribe to is BeginRequestHandler, when the request was started by the client, before it gets to the server and EndRequestHandler which is the event raised after we got a response from the server. These two events also nicely allow us to show some kind of progress indicator while the callback is taking place. WE ARE GETTING ALL THIS FUNCTIONALITY FREE! Nice!

    For simplicity i am not registering this clientside subscription to BeginRequestHandler and EndRequestHandler in the custom control, which is what you want to do. The code that follows is so simple that i have decided not to waste time leaving comments Tongue out The control renders 3 clickable spans. I have used spans because i didnt want the easy way out, which was to use a control that already had postback events and self contained like the button, linkbutton etc.

    Besides the dynamic time changing, which is included in the updatepanels content template, the output you should expect is :

    Static time: 3/22/2007 11:47:12 AM

    Dynamic time: 3/22/2007 11:47:12 AM

    C#
    using System;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    namespace DummyControls
    {
        /// <summary>
        /// Summary description for PartialRefreshDummy
        /// </summary>
    public class PartialRefreshDummy : WebControl, IPostBackEventHandler 
    {
    // Defines the Click event.
    public event EventHandler Click1;
    public event EventHandler Click2;
    public event EventHandler Click3;
    
    public ScriptManager sm;
    public PartialRefreshDummy() : base("div")
    {
        //
        // TODO: Add constructor logic here
        //
    }
    //Invoke delegates registered with the Click event.
    protected virtual void OnClick1(EventArgs e)
    {
        sm.RegisterDataItem(this, "<h1>Sending data from click1</h1>");
        if (Click1 != null)
        {
            Click1(this, e);
        }
    }
    
    protected virtual void OnClick2(EventArgs e)
    {
        sm.RegisterDataItem(this, "<h2>Sending data from click2</h2>");
        if (Click2 != null)
        {
            Click2(this, e);
        }
    }
    
    protected virtual void OnClick3(EventArgs e)
    {
        sm.RegisterDataItem(this, "<h3>Sending data from click3</h3>");
        if (Click3 != null)
        {
            Click3(this, e);
        }
    }
    
    
    // Define the method of IPostBackEventHandler that raises change events.
    public void RaisePostBackEvent(string eventArgument)
    {
        switch (eventArgument)
        {
            case "click1":
                OnClick1(new EventArgs());
                break;
            case "click2":
                OnClick2(new EventArgs());
                break;
            case "click3":
                OnClick3(new EventArgs());
                break;
        }
    }
    protected override void OnLoad(EventArgs e)
    {
        base.OnLoad(e);
        sm = ScriptManager.GetCurrent(Page);
    }
    protected override void CreateChildControls()
    {
        base.CreateChildControls();
        ClientScriptManager cs = Page.ClientScript;
    
        string click1 = cs.GetPostBackEventReference(this, "click1");
        string click2 = cs.GetPostBackEventReference(this, "click2");
        string click3 = cs.GetPostBackEventReference(this, "click3");
        
        string postback1 = string.Format(
    "<span id=\"span1_{0}\" onclick=\"{1}\" style=\"border:1px solid #000;" +
    "margin:2px;background:yellow;cursor:pointer\">click1</span>", 
    this.ClientID, click1);
        
        string postback2 = string.Format(
    "<span id=\"span2_{0}\" onclick=\"{1}\" style=\"border:1px solid #000;" + 
    "margin:2px;background:yellow;cursor:pointer\">click2</span>", 
    this.ClientID, click2);
        
        string postback3 = string.Format(
    "<span id=\"span3_{0}\" onclick=\"{1}\" style=\"border:1px solid #000;" + 
    "margin:2px;background:yellow;cursor:pointer\">click3</span>", 
    this.ClientID, click3);
        
        Control parent;
        Control container;
        if (sm == null || !sm.EnablePartialRendering)
        {
            // If user did not add a scriptmanager control or has disabled
            // partial rendering, use a
            // dummy control as the container and gracefully degrade to 
            // full postbacks
            parent = container = new Control();
        }
        else
        {
            // Create an UpdatePanel control.
            UpdatePanel up = new UpdatePanel();
            // Instead of adding child controls directly to 
            // the UpdatePanel control, add them to its 
            // ContentTemplateContainer.
            container = up.ContentTemplateContainer;
            // Create triggers for our 3 events -->> click1, click2, click3
            AsyncPostBackTrigger apbt1 = new AsyncPostBackTrigger();
            apbt1.ControlID = this.ClientID;
            apbt1.EventName = "click1";
    
            AsyncPostBackTrigger apbt2 = new AsyncPostBackTrigger();
            apbt2.ControlID = this.ClientID;
            apbt2.EventName = "click2";
    
            AsyncPostBackTrigger apbt3 = new AsyncPostBackTrigger();
            apbt3.ControlID = this.ClientID;
            apbt3.EventName = "click3";
    
            up.Triggers.Add(apbt1);
            up.Triggers.Add(apbt2);
            up.Triggers.Add(apbt3);
            parent = up;
        }
        Controls.Add(new LiteralControl("Static time: " + 
            DateTime.Now.ToString() + "<br /><br />"));
        container.Controls.Add(new LiteralControl("Dynamic time: " + 
            DateTime.Now.ToString() + "<br /><br />"));
        container.Controls.Add(new LiteralControl(postback1));
        container.Controls.Add(new LiteralControl(postback2));
        container.Controls.Add(new LiteralControl(postback3));
        Controls.Add(parent);
        
    }
    
    }
    }
    

    ASP.NET
    <%@ Page Language="C#" %>
    <%@ Register TagPrefix="ctl" Namespace="DummyControls" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    
    <script runat="server">
    </script>
    
    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head id="Head1" runat="server">
        <title>Untitled Page</title>
    </head>
    <body>
    <form id="form1" runat="server">
    <div>
     <asp:ScriptManager ID="ScriptManager1" 
     EnablePartialRendering="true" runat="server">
        </asp:ScriptManager>
    <ctl:PartialRefreshDummy ID="prd1" runat="server" />
    </div>
    <div id="output"></div>
    </form>
    <script type="text/javascript" language="javascript">
    var pageManager = Sys.WebForms.PageRequestManager.getInstance();
    pageManager.add_beginRequest(BeginRequestHandler);
    pageManager.add_endRequest(EndRequestHandler);
    function BeginRequestHandler(sender, args)
    {
        var div1 = $get('output');
         var elem = args.get_postBackElement();
         div1.innerHTML = ' BeginRequestHandler processing... for controlID: '+ 
         elem.id + '<br />';
    }
    function EndRequestHandler(sender, args)
    {
        var div1 = $get('output');
        var ctlID = sender._asyncPostBackControlClientIDs;
        div1.innerHTML += 'EndRequestHandler endprocessing...for controlID: '+ 
        ctlID + '<br />';
        div1.innerHTML += 'Control who made this callback: ' + 
        ctlID; 
        var dataItems = args.get_dataItems();
        if ($get('prd1') !== null)
            div1.innerHTML += dataItems["prd1"]; 
    }
    </script>
    </body>
    </html
    
     
    re: Ajax 1.0 XMLHTTP callbacks via Updatepanel in Custom Web Controls. POWERFUL ...
    by

    thx for article,

    good solution


    A penny for your thoughts