ASP.NET Callback Panel

Continuing with my quest for reusable, no dependencies, Web Forms AJAX controls, this time I wanted a replacement for the venerable UpdatePanel control. Specifically, I wanted to address the following issues:

  • Allow the partial update of a region on my page, including all of its controls;
  • Being able to cause an update through JavaScript (tricky with UpdatePanel);
  • Have the ability to only send data for controls living inside my control, not everything else on the page (read, __VIEWSTATE);
  • Support JavaScript events at the beginning/end of a callback or in the occurrence of an error (yes, I know about PageRequestManager, but I wanted something different).

I ended up with a CallbackPanel control, which is what I am going to talk about. Here is its declaration:

   1: <web:CallbackPanel runat="server" ID="callback" SendAllData="false" OnBeforeCallback="onBeforeCallback" OnAfterCallback="onAfterCallback" OnCallbackError="onCallbackError" OnCallback="OnCallback">
   2:     <!-- some controls -->
   3:     <asp:Label runat="server" ID="time"/>
   4:     <asp:TextBox runat="server" ID="text"/>
   5:     <asp:Button runat="server" ID="button" Text="Button"/>
   6: </web:CallbackPanel>

The CallbackPanel control supports some properties:

  • SendAllData: if all the data in the form should be sent, including viewstate, or just the data for the controls inside the CallbackPanel (default is false);
  • OnAfterCallback: the name of a JavaScript function to be called after the callback terminates;
    OnBeforeCallback: the optional name of a JavaScript function that gets called before a callback; if we so want, we can return false on this function to cancel the callback;
  • OnCallbackError: the name of a JavaScript function that is called in the event of an error.

Some examples of the JavaScript functions:

   1: <script type="text/javascript">
   1:  
   2:         
   3:     function onCallbackError(error, context)
   4:     {
   5:     }
   6:  
   7:     function onBeforeCallback(arg, context)
   8:     {
   9:         //return false to cancel
  10:     }
  11:  
  12:     function onAfterCallback(result, context)
  13:     {
  14:     }
  15:  
</script>

For causing an update, we call its callback function, passing it a parameter and an optional context:

   1: document.getElementById('callback').callback('test', null);

The most important property in CallbackPanel is the server-side event, OnCallback: this is raised whenever the callback function is called:

   1: protected void OnCallback(Object sender, CallbackEventArgs e)
   2: {
   3:     this.time.Text = e.Parameter + ": " + DateTime.Now.ToString();
   4: }

This event receives a CallbackEventArgs argument, which is nothing more than:

   1: [Serializable]
   2: public sealed class CallbackEventArgs : EventArgs
   3: {
   4:     public CallbackEventArgs(String parameter)
   5:     {
   6:         this.Parameter = parameter;
   7:     }
   8:  
   9:     public String Parameter { get; private set; }
  10: }

And finally, the code for the CallbackPanel itself:

   1: public class CallbackPanel : Panel, INamingContainer, ICallbackEventHandler
   2: {
   3:     public CallbackPanel()
   4:     {
   5:         this.OnAfterCallback = String.Empty;
   6:         this.OnBeforeCallback = String.Empty;
   7:         this.OnCallbackError = String.Empty;
   8:         this.SendAllData = true;
   9:     }
  10:  
  11:     public event EventHandler<CallbackEventArgs> Callback;
  12:  
  13:     [DefaultValue("")]
  14:     public String OnBeforeCallback { get; set; }
  15:  
  16:     [DefaultValue("")]
  17:     public String OnAfterCallback { get; set; }
  18:  
  19:     [DefaultValue("")]
  20:     public String OnCallbackError { get; set; }
  21:  
  22:     [DefaultValue(true)]
  23:     public Boolean SendAllData { get; set; }
  24:  
  25:     protected override void OnInit(EventArgs e)
  26:     {
  27:         var sm = ScriptManager.GetCurrent(this.Page);
  28:         var reference = this.Page.ClientScript.GetCallbackEventReference(this, "arg", String.Format("function(result, context){{ document.getElementById('{0}').innerHTML = result; {1} }}", this.ClientID, (String.IsNullOrWhiteSpace(this.OnAfterCallback) == false) ? String.Concat(this.OnAfterCallback, "(result, context);") : String.Empty), "context", String.Format("function(error, context){{ {0} }}", ((String.IsNullOrWhiteSpace(this.OnCallbackError) == false) ? String.Concat(this.OnCallbackError, "(error, context)") : String.Empty)), true);
  29:         var script = String.Concat("\ndocument.getElementById('", this.ClientID, "').callback = function(arg, context){", ((this.SendAllData == true) ? "__theFormPostCollection.length = 0; __theFormPostData = '';  WebForm_InitCallback(); " : "__theFormPostCollection.length = 0; __theFormPostData = '';  WebForm_InitCallback(); for (var i = 0; i < __theFormPostCollection.length; ++i) { if (__theFormPostCollection[i].name.indexOf('" + this.UniqueID + "$') == -1) { __theFormPostCollection[i].value = '' } }; "), (String.IsNullOrWhiteSpace(this.OnBeforeCallback) == true ? String.Empty : String.Concat("if (", this.OnBeforeCallback, "(arg, context) === false) return; ")), reference, ";};\n");
  30:  
  31:         if (sm != null)
  32:         {
  33:             this.Page.ClientScript.RegisterStartupScript(this.GetType(), String.Concat("callback", this.ClientID), String.Format("Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded(function() {{ {0} }});\n", script), true);
  34:         }
  35:         else
  36:         {
  37:             this.Page.ClientScript.RegisterStartupScript(this.GetType(), String.Concat("callback", this.ClientID), script, true);
  38:         }
  39:  
  40:         base.OnInit(e);
  41:     }
  42:  
  43:     protected virtual void OnCallback(CallbackEventArgs e)
  44:     {
  45:         var handler = this.Callback;
  46:  
  47:         if (handler != null)
  48:         {
  49:             handler(this, e);
  50:         }
  51:     }
  52:  
  53:     #region ICallbackEventHandler Members
  54:     String ICallbackEventHandler.GetCallbackResult()
  55:     {
  56:         var builder = new StringBuilder();
  57:  
  58:         using (var writer = new StringWriter(builder))
  59:         using (var htmlWriter = new HtmlTextWriter(writer))
  60:         {
  61:             this.Render(new HtmlTextWriter(writer));
  62:  
  63:             return (builder.ToString());
  64:         }
  65:     }
  66:  
  67:     void ICallbackEventHandler.RaiseCallbackEvent(String eventArgument)
  68:     {
  69:         this.OnCallback(new CallbackEventArgs(eventArgument));
  70:     }
  71:     #endregion
  72: }

Again, it is implementing ICallbackEventHandler, for client callbacks, but this time it is inheriting from Panel, which is a nice container for other controls. The rest should be self-explanatory, I guess. If you have questions, do send them to me!

As always, hope you like it! Winking smile

                             

3 Comments

  • Thank you, this is very usefull!
    I did some test on the code and it works very well.
    I have a couple of questions:
    - When the onCallbackError is raised?
    - The onAfterCallback event is called the server postback. (OnBeforeCallback -> OnAfterCallback -> Server); is this right?
    - What is the best way to fire the callback?
    I did this:

    And then:
    function DoCallBack(arg) { document.getElementById('').callback(arg, null);
    }
    It this good or you can suggest a better way to do it?
    Thank you!

  • Hi, Danielle!

    Thanks! Believe me, it makes me very happy to help other people! ;-)
    As for your questions:
    - onCallbackError is only called if an error occurs, which isn't very easy to simulate;
    - Yes, that's right!
    - Your example seems good to me; or, you can have a static ClientID (ClientIDMode=Static).

    Do let me know your problems!

    RP

  • Dear,
    Thanks, it is very useful. I also implement CallBack Panel as code provided by you above. It is working nice but it is working same as Update Panel. When I compared VIEWSTATE of Callback Panel and Update Panel both are sending full data on server. I also set SendAllData to True and False, but in both cases VIEWSTATE is same. So, I think it is not beneficial. I think you can help, if I have done some mistake when implement it.

Add a Comment

As it will appear on the website

Not displayed

Your website