Atlas developers - especially Atlas developers working with classic ASP.Net applications - are going to fall in love with <UpdatePanel> controls because they make migrating traditional ASP.Net 1.x/2.0 applications to Atlas a breeze (er, for the most part). You will run into issues, at times, when dealing with update panels because, even though they are pretty solid in implementation and require (sometimes) no changes in code on the server - they still can break existing code. Understanding the internals of them is always a benefit.
The syntax for an update panel is simple, and looks like this:
<atlas:UpdatePanel runat="server" ID="UpdatePanel5">
... controls that will now be AJAX'd ...
</atlas:UpdatePanel>
Pretty harmless looking actually. What you do is simply surround surround your classic ASP.Net controls with these tags and *voila*, you suddenly enabled them all for partial postbacks. If you have a button and a textbox inside the UpdatePanel tags, and when you click the button the text box says hello, you can now do this without forcing the rest of the web page to render. There are several features to the UpdatePanel, and I'm not going to restate them , so if you're curious look here because the topic of this blog post is how the Atlas framework enables you to simply surround your original ASP.Net controls with a single tag and turn them into something which supports partial postbacks.
The slight of hand behind the whole process is the onsubmit handler in the form tag for every Atlas application:
<form name="form1" method="post" action="UpdatePanel1_cs.aspx" onsubmit="javascript:return WebForm_OnSubmit();" id="form1">
The method is large - too large to paste in this post in its entirety - but its function is simple: to detect if partial postbacks are enabled for the page, and if so, swallow any submit event that originates within an UpdatePanel (which are typically rendered as <span> or <div> tags in the HTML) and, in its stead, send an asynchronous form post back to the server. It registers a handler method for dealing with the response from the server and parses data out and updates the content using standard DHTML tricks. The main lines of code to take note of within the Submit method follows:
var request = new Sys.Net.WebRequest();
request.set_url(form.action);
request.get_headers()['delta'] = 'true';
request.get_headers()['Cache-Control'] = 'no-cache';
request.set_timeoutInterval(90000);
request.set_priority(Sys.Net.WebRequestPriority.High);
request.completed.add(Function.createDelegate(this, this._onFormSubmitCompleted));
request.timeout.add(Function.createDelegate(this, this._onFormSubmitTimeout));
request.set_body(formBody.toString());
_request = request;
this.raisePropertyChanged('inPostBack');
request.invoke();
One main point of interst is the creation and setting of the 'delta' header variable. For partial postbacks, this particular header is always there for it is this that clues the server-side framework into the fact that a partial postback is occuring, and to format the response stream in such a manner that the client can easily and effeciently parse out the change or "delta" in the browser content. A summary of fiddler response for a partial postback follows:
HTTP/1.1 200 OK
Cache-Control: no-cache
Date: Tue, 09 May 2006 03:56:25 GMT
Pragma: no-cache
Content-Type: text/xml; charset=utf-8
Expires: -1
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
X-AspNet-Version: 2.0.50727
Vary: Accept-Encoding
<delta>
..........
</delta>
Pay special attention to the fact that the response type is text/xml - this is because the response is, yes, XML. If you scan the method that deals with the server response, it is littered with dozens of XPath expressions that tear out of the response pertinent bits of information for DHTML-based updating of controls. For example:
..........
var hiddenFields = delta.selectNodes('/delta/rendering//input[@type="hidden"]');
for (var j = 0; j < hiddenFields.length; j++) {
var id = hiddenFields[j].attributes.getNamedItem('id').nodeValue;
var value = hiddenFields[j].attributes.getNamedItem('value').nodeValue;
var hiddenFieldElement = document.getElementById(id);
if (!hiddenFieldElement) {
hiddenFieldElement = document.createElement('input');
hiddenFieldElement.id = id;
hiddenFieldElement.name = id;
hiddenFieldElement.type = 'hidden';
_form.appendChild(hiddenFieldElement);
}
hiddenFieldElement.value = value;
}
..........
Now the server-side components that come into play here reside within the server-side code for the ScriptManager control. Particularly, two methods are of interest: first, the one that identifies we're dealing with partial postbacks and the one that generates the response stream for the client. The following bit of code within the ScriptManager hooks the .Render method (in the same way that the .Render method is hooked when inline [WebMethod] calls are made) to override the page's normal output and send custom output:
if (this._inPartialRenderingMode)
{
RenderMethod method1 = new RenderMethod(this.RenderPageCallback);
this._page.SetRenderMethodDelegate(method1);
}
When the page housing the ScriptManager control is asked to render, the following method is called instead:
private void RenderPageCallback(HtmlTextWriter writer, Control pageControl)
{
Page page1 = (Page) pageControl;
HttpResponse response1 = page1.Response;
response1.Cache.SetCacheability(HttpCacheability.NoCache);
response1.ContentType = "text/xml";
writer.Write("<delta>");
....
writer.Write("</delta>");
}
...which builds the response the client framework expects (an XML document).