Background
In my previous article, I wrote about Callback and JSON based JavaScript serialization which you can find here.
1. Why Should I Read this Article
Callback doesn’t cause postback and page rendering, neither full nor even partial. We can communicate with server (IIS) and our server side code runs there successfully and could rebind our controls like Dropdownlist
, Gridview
, Listview
, Datalist
, Repeater
or any server side control to which you assign data but problem is when page won't render, its controls won't render and if controls won't render, then changes won't reflect and when changes won't reflect, there won't be anything at the front end to show on webpage.
This article is mainly about Callback and Rendering Controls but through this tutorial, you can also learn many other things like brief introduction of Postback, Rendering, Create server side controls dynamically, Create Datatable dynamically in memory to bind with create server side controls means binding, get server side control at client side and set their properties and registering client side event of server side control from/through server side code.
First of all, I would like to brief some terms which I believe every web developer should aware of.
2. Postback
Postback is a mechanism of communication between client-side (browser) and server-side (IIS). Through postback, all contents of page/form(s) sent to the server from client for processing and after following page life cycle, all server side contents get rendered into client side code and client (browser) displays those contents. Callback is another way of communication between server and client. Callback doesn’t follow the page life cycle which is followed by in standard postback and doesn’t even cause Rendering.
3. Rendering
Rendering is the process of converting server side code/contents into client side code/content so client (browser) can understand that code and could display the output. Browser can understand or you may say decode code of client side languages and scripts like HTML, DHTML, XHTML, JavaScript, VBScript and a long list.
If rendering won't happen, then changes won’t reflect which happens on server at client side. <city><place>Ajax leverages partial postback automatically whereas callback doesn’t cause, so programmer needs to perform that task manually.
ASP.NET team has created RenderControl
method with its each control so by using that control, we can render our control very easily.
If you have read my previous article, you may skip section 4 and 5.
4. Callback
Callback is a lightweight process. It uses well known xmlhttp
object internally to call server side method. It doesn’t cause page postback so doesn’t cause page rendering so to show output at client side, we need to make output HTML ourselves and render controls manually.
5. ICallbackEventHandler
ICallback
implemented in ASP.NET by using ICallbackEventHandler
interface has two methods, one of them used to be called from JavaScript (client side code) and other one returns result asynchronously back to JavaScript function.
We just need to perform some action through server side code at server side and need to return results but results could be in instance or object of any class which could be not be easy for JavaScript code to handle so here we prefer JSON which stands for JavaScript Object Notation.
6. Real Time Scenario with Implementation
Suppose we have categories, subcategories, products data and needs to populate categories and subcategories which depend upon categories data would be populated in two different dropdownlists. For products data which is multicolumn and we need to show that data in tabular format, I would prefer Gridview
control.
So the situation would be load/populate categories on Page_Load
and load/populate subcategories on the basis of selected category using callback and finally load products into Gridview
on the basis of selected subcategory.
Before I start coding, I would like to write pseudo code for better understanding.
7. Pseudo Code
- Create Server side controls, e.g.,
Dropdownlist
s and Gridview
. - Load Categories on Page load.
- Implement
ICallbackEventHandler
interface. - Create subcategories data in server memory to bind to
Subcategory
dropdownlist
. - Render control (subcategory
dropdownlist
) and show output. - Create products data in server memory to bind to
Products gridview
. - Render Control (
products gridview
) and return rendered contents to client side to show. - Set
innerHTML
of each control by rendered contents
Create Controls (DropDownLists, Gridview)
<b>Categories:</b>
<br />
<asp:DropDownList ID="ddlCategories"
runat="server" Width="100" onchange="CallSrv(this);">
</asp:DropDownList>
<br />
<b>Subcategories</b>:
<div id="ddlSubcategories">
</div>
<b>Products:</b>
<div id="grvProducts">
</div>
Callback Server Side Code
Let’s implement ICallbackEventHandler
to call server side method asynchronously step by step.
Implement Server Side (C#) Page/Control class by System.Web.UI.ICallbackEventHandler
.
Following are definitions of two methods which you need to implement:
RaiseCallbackEvent
method invoked by JavaScript function:
public void RaiseCallbackEvent(string eventArgument)
{
string[] commands = eventArgument.Split(",".ToCharArray());
if (commands[0].Equals("LoadSubCategory"))
{
DropDownList ddlSubcategories = new DropDownList();
switch (commands[1])
{
case "Autos":
ddlSubcategories.Items.Add("Cars");
ddlSubcategories.Items.Add("Bikes");
break;
case "Electronics":
ddlSubcategories.Items.Add("Computers");
ddlSubcategories.Items.Add("TV");
break;
}
ddlSubcategories.Attributes.Add("onchange", "CallSrv(this);");
System.Text.StringBuilder sb = new System.Text.StringBuilder();
System.IO.StringWriter sw = new System.IO.StringWriter(sb);
HtmlTextWriter htw = new HtmlTextWriter(sw);
ddlSubcategories.RenderControl(htw);
this.RenderedOutput = "LoadSubCategory," + sb.ToString();
}
else if (commands[0].Equals("LoadProducts"))
{
DataTable dtProducts = new DataTable();
dtProducts.Columns.Add("ProductName");
dtProducts.Columns.Add("ProductDescription");
dtProducts.Columns.Add("ProductPrice");
DataRow drProduct;
switch (commands[1])
{
case "Cars":
drProduct = dtProducts.NewRow();
drProduct["ProductName"] = "Honda";
drProduct["ProductDescription"] = "2000 CC";
drProduct["ProductPrice"] = "$1000";
dtProducts.Rows.Add(drProduct);
drProduct = dtProducts.NewRow();
drProduct["ProductName"] = "<city><place>Toyota</place></city>";
drProduct["ProductDescription"] = "1800 CC";
drProduct["ProductPrice"] = "$800";
dtProducts.Rows.Add(drProduct);
break;
case "Bikes":
drProduct = dtProducts.NewRow();
drProduct["ProductName"] = "Pak Hero";
drProduct["ProductDescription"] = "125 CC";
drProduct["ProductPrice"] = "$100";
dtProducts.Rows.Add(drProduct);
drProduct = dtProducts.NewRow();
drProduct["ProductName"] = "Honda";
drProduct["ProductDescription"] = "250 CC";
drProduct["ProductPrice"] = "$150";
dtProducts.Rows.Add(drProduct);
break;
case "Computers":
drProduct = dtProducts.NewRow();
drProduct["ProductName"] = "Dell";
drProduct["ProductDescription"] = "P4 Centrino";
drProduct["ProductPrice"] = "$400";
dtProducts.Rows.Add(drProduct);
drProduct = dtProducts.NewRow();
drProduct["ProductName"] = "IBM";
drProduct["ProductDescription"] = "P4 Think PAD";
drProduct["ProductPrice"] = "$350";
dtProducts.Rows.Add(drProduct);
break;
case "TV":
drProduct = dtProducts.NewRow();
drProduct["ProductName"] = "Sony";
drProduct["ProductDescription"] = "Plasma";
drProduct["ProductPrice"] = "$600";
dtProducts.Rows.Add(drProduct);
drProduct = dtProducts.NewRow();
drProduct["ProductName"] = "Philips";
drProduct["ProductDescription"] = "Projection";
drProduct["ProductPrice"] = "$550";
dtProducts.Rows.Add(drProduct);
break;
}
GridView grvProducts = new GridView();
grvProducts.DataSource = dtProducts;
grvProducts.DataBind();
System.Text.StringBuilder sb = new System.Text.StringBuilder();
System.IO.StringWriter sw = new System.IO.StringWriter(sb);
HtmlTextWriter htw = new HtmlTextWriter(sw);
grvProducts.RenderControl(htw);
this.RenderedOutput = "LoadProducts," + sb.ToString();
}
}
public string GetCallbackResult()
{
return RenderedOutput;
}
In Page_Load
or Page_Init
event, the following statements are used to register client side methods:
CallServer(arg, context)
as the name implies would be used to call/raise server side method which was RaiseCallbackEvent string eventArgument)
.
ReceiveServerData(arg, context)
would be used to get result through arg
parameter by GetCallbackResult()
.
protected void Page_Load(object sender, EventArgs e)
{
ClientScriptManager scriptMgr = Page.ClientScript;
String cbReference = scriptMgr.GetCallbackEventReference(this, "arg", "ReceiveServerData", "");
String callbackScript = "function CallServer(arg, context) {" + cbReference + "; }";
cm.RegisterClientScriptBlock(this.GetType(),"CallServer", callbackScript, true);
if (!Page.IsPostBack)
{
this.ddlCategories.Items.Add("Select");
this.ddlCategories.Items.Add("Autos");
this.ddlCategories.Items.Add("Electronics");
}
}
Callback Client Side Code
<script language="javascript" type="text/javascript">
function ReceiveServerData(arg, context)
{
var cmd_content = arg.split(',');
if (cmd_content[0] == 'LoadSubCategory')
{
document.getElementById('ddlSubcategories').innerHTML = cmd_content[1];
}
else
{
document.getElementById('grvProducts').innerHTML = cmd_content[1];
}
}
function CallSrv(ddl)
{
if (ddl.id == 'ddlCategories')
{
if(ddl.value != 'Select')
{
CallServer('LoadSubCategory' + ',' + ddl.value, '');
}
}
else
{
CallServer('LoadProducts' + ',' + ddl.value, '');
}
}
</script>
That's it! These are the steps which you need to use to call and get results from server side code using ICallback
.
Asynchronously output would be within a millisecond and without Postback.
Conclusion
Callback is a lightweight technique used to call server side methods asynchronously from JavaScript without any postback and reloading/rendering of unnecessary parts of page and unnecessary code.
So we can use that when we need to perform any operation at backend means at server like update records in database or like that. You don’t need to send all your contents of page in request and make that object heavyweight which could cause slow performance.
History
- 18th January, 2008: Initial version