2006-01-30
| Table of Contents: |
| Rate This Article: | Add This Article To: |
( Page 4 of 4 )
-rendering Complete Controls">
Re-rendering Complete Controls
We just finished sending a simple string back to the client in the form of a date and time value. This value was placed into the innerHTML attribute of an HTML element — simple enough. Now let's take it up a notch and return a manipulated webcontrol.
By "manipulated," I mean I set server-side values to the properties of a webcontrol, and re-render the control on the page. However, as you will soon see, much of this works just like the previous example; I am still passing up a string, albeit a more complex one.
Let's extend our previous examples, instead of creating a new form. That lets me show you how multiple callback situations can co-exist in a page or a custom control.
First, I drop two more buttons on the form, one called btnSuccess and the other btnFailure. I also drop another label, called lblMessage, onto the form.
Next, I add more code to the page's PreRender event; you will see the similarity in this code with the previous example.
StringBuilder script2 = new StringBuilder();
using (StringWriter writer2 = new StringWriter(script2))
{
writer2.WriteLine("<script language='JavaScript'>");
writer2.WriteLine(" function DisplayMessage(result, context)");
writer2.WriteLine(" {");
writer2.WriteLine(" var label = document.getElementById('" +
this.lblMessage.ClientID + "');");
writer2.WriteLine(" label.outerHTML = result;");
writer2.WriteLine(" }");
writer2.WriteLine("</script>");
Page.ClientScript.RegisterClientScriptBlock(
this.GetType(), "DisplayMessage", script2.ToString());
}
btnSuccess.Attributes.Add(
"onclick",
Page.ClientScript.GetCallbackEventReference(
this, "'success'", "DisplayMessage", null) + "; return false;");
btnFailure.Attributes.Add(
"onclick",
Page.ClientScript.GetCallbackEventReference(
this, "'failure'", "DisplayMessage", null) + "; return false;");
See the similarities? The main differences in the JavaScript function are the name (DisplayMessage) and label control we are accessing (lblMessage, in this case).
There is one more difference which I will mention now and explain later. Notice that I am not changing the innerHTML attribute of the HTML element we found; instead, I change the outerHTML attribute. This replaces the element all the way to the actual HTML tag that defines it, as opposed to just the contents within the open and close tags. This is necessary when you expect the attributes of the HTML element to have changed and need re-rendering, and not just the content.
Also notice that I add the GetCallbackEventReference call to both the buttons, but I send a different value in each that will later be used. In this case, that's 'success' and 'failure'.
Now comes the cool part. Since I'm going to add more code to the RaiseCallbackEvent method instead of replacing the code from the previous example, I condition around the old code so we can keep it in place. Let's take a look at the new contents of this method:
if (eventArgument == "datetime")
s_CallbackResult = DateTime.Now.ToString();
else
{
if (eventArgument == "success")
{
lblMessage.ForeColor = System.Drawing.Color.Green;
lblMessage.Text = "SUCCESS";
}
else
{
lblMessage.ForeColor = System.Drawing.Color.Red;
lblMessage.Text = "FAILURE";
}
using(StringWriter stringWriter = new StringWriter())
{
using(HtmlTextWriter htmlWriter = new HtmlTextWriter(stringWriter))
{
lblMessage.RenderControl(htmlWriter);
}
s_CallbackResult = stringWriter.ToString();
}
}
First of all, notice I'm still performing the code from the previous example at the beginning, so I placed the new code in the "else" clause of the condition.
As in the previous example with the literal datetime, this time I expect the literal's success or failure to be passed into the function. We've finally arrived at the whole point of this demonstration. Depending on the value of the eventArgument argument, I am manipulating the lblMessage webcontrol. In this case, I only alter its ForeColor and Text properties, but the point is that I am taking full advantage of a control's server-side model. We have a small example with a simple control, but it could very well have been a more complex control, and we could have certainly manipulated it as much as necessary. So far, it appears we are dealing with more than just a simple string.
After I alter the lblMessage control to the point that I want, I want to send it back to the client's DisplayMessage JavaScript function that we declared earlier. However, this is where I still have to deal with strings only.
That's where that next piece of code comes in. Every webcontrol has a RenderControl method which can be used to retrieve the HTML representation of what this control will eventually render to the browser. That's exactly what is taking place here, with the help of a couple of Writers. The final result is placed into the s_CallbackResult variable which is sent back to the client in the GetCallbackResult function.
Now you see why I replaced the outerHTML attribute of the HTML element, instead of the innerHTML attribute. The results of using the RenderControl method include the entire HTML representation of that webcontrol, including its primary container tag, which is what the ClientID search resulted.
Run the form now, and try both buttons. You have to admit, this is pretty cool; and it is not that complex.
The same technique can be used to re-render more complex controls, such as grids and trees. In fact, though I have not reflected through Microsoft's code yet, I am willing to guess that this how the GridView and the other new data controls perform their paging and sorting re-rendering magic.
Using callbacks, you can give your pages and your custom webcontrols a very polished finish and eliminate the annoying screen flicker that accompanies regular postbacks and drives many of my clients nuts.
![]() |
|


