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:CallbackPanelrunat="server"ID="callback"SendAllData="false"OnBeforeCallback="onBeforeCallback"OnAfterCallback="onAfterCallback"OnCallbackError="onCallbackError"OnCallback="OnCallback">
2:<!-- some controls -->
3:<asp:Labelrunat="server"ID="time"/>
4:<asp:TextBoxrunat="server"ID="text"/>
5:<asp:Buttonrunat="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:<scripttype="text/javascript">1:2:3:function onCallbackError(error, context)4: {5: }6:7:function onBeforeCallback(arg, context)8: {9://return false to cancel10: }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:protectedvoid 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:publicsealedclass 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:publicclass 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:publicevent 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:protectedoverridevoid 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:protectedvirtualvoid 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! ![]()