Creating an ASP.NET AJAX-Enabled Grid using ICallbackEventHandler (Part 1)


This article details the development of an ASP.NET AJAX-enabled grid using ICallbackEventHandler, with operations which include sorting, paging and page length change. I will work through the code in sequence, but it may help to download to the entire code sample here

The basic features of the gird are as follows (All operations are asynchronous)
  1. Sort in ascending or in descending order by clicking on the arrows next to column name.
  2. Change current page.
  3. Change page length.
In this example we will use one of the most powerful features of ASP.Net – RenderControl.
Using this method we are able to access the HTML of a control. To do this we will have to also use HtmlTextWriter and  StringWriter as follows
e.g.
using (StringWriter sw = new StringWriter())
{
HtmlTextWriter htw = new HtmlTextWriter(sw);
_grid.RenderControl(htw);
htw.Flush();
string result = sw.ToString();
}
The result string will contain the HTML format of the grid control. We will now convert the grid control to HTML after binding the data.
We will start by developing the UI and code in following steps:
  1. Enter the following code in the <form> tag of the page to create a GridView and a Dropdownlist.
<div id=”Gridview” >
<asp:GridView EnableViewState=”false” runat=”server” id=”_grid” OnRowDataBound=”_grid_RowDataBound” AllowPaging=”True” >
</asp:GridView>
<br />
</div> Change page length to –
<asp:DropDownList ID=”ddl” runat=”server”>
</asp:DropDownList>
Please note that RowDataBound event of the grid has been activated.
  1. Now we will create a DataTable to be used as DataSource for the GridView.
public DataTable _sampleData
{
get {
DataTable dt = (DataTable)ViewState["DataTable"];
if(dt == null)
{
dt = new DataTable();
dt.Columns.Add(new DataColumn(“Contact Name”,typeof(string)));
dt.Columns.Add(new DataColumn(“Company Name”, typeof(string)));
dt.Columns.Add(new DataColumn(“City”, typeof(string)));
dt.Columns.Add(new DataColumn(“Country”, typeof(string)));
dt.Rows.Add(new object[] { “Maria Anders” ,”Alfreds Futterkiste”,”Berlin”,”Germany”});
dt.Rows.Add(new object[] { “Ana Trujillo” ,”Emparedados y helados “,”México D.F.”,”Mexico”});
dt.Rows.Add(new object[] { “Antonio Moreno”, “Antonio Moreno Taquería”, “México D.F.”,”Mexico” });
ViewState["DataTable"] = dt;
}
return dt;
}
In the above code we have used ViewState instead of a Session variable.
i.e.       ViewState["DataTable"] = dt;
This will preserve the table for that page. Using Session variable the table would be available throughout the website, but using ViewState the table will only be available for that page.
To bind this table to the GridView we simply use _grid.DataSource = _sampleData;
  1. We will write following four functions for grid operations
    1. We need four functions for ICallBackEventHandler – two are JavaScript functions (one for Callback to server and another for displaying the response data from server). And two are Server side functions (RaiseCallbackEvent(stringeventArgument) and GetCallbackResult())
    1. Action .
    1. Page index of the Grid.
    1. Page size from the dropdown list (i.e. id is ‘ddl’).
    1. Add following code
      protected void Page_Load(object sender, EventArgs e)
    1. Now we will write code in the RowDataBound event of the GridView – i.e. in protected void _grid_RowDataBound(object sender, GridViewRowEventArgs e)
  • private void sortGrid(string Argument , string pageLength)
    {
    DataView dv = _sampleData.DefaultView;
    result = “”;
    dv.Sort = Argument;
    _grid.DataSource = dv;
    _grid.PageSize = Convert.ToInt16(pageLength);
    _grid.DataBind();
    renderGrid(_grid);
    }
    private void changePage(string Argument , string       pageLength)
    {
    result = “”;
    _grid.DataSource = _sampleData;
    _grid.PageSize = Convert.ToInt16(pageLength);
    _grid.PageIndex = Convert.ToInt16(Argument);
    _grid.DataBind();
    renderGrid(_grid);
    }
    private void changePageLength(string Argument,  string pageLength)
    {
    result = “”;
    _grid.DataSource = _sampleData;
    _grid.PageSize = Convert.ToInt16(Argument);
    _grid.DataBind();
    renderGrid(_grid);
    //pageLength is not used
    }
    private void renderGrid(GridView _grid)
    {
    using (StringWriter sw = new StringWriter())
    {
    HtmlTextWriter htw = new HtmlTextWriter(sw);
    _grid.RenderControl(htw);
    htw.Flush();
    lass=textCode>            result = sw.ToString();
    }
    }
    The names of the above functions indicates their functionality, the second parameter of the first three functions is “pageLength this is the value of the dropdown list
    JavaScript  functions are as follows:
    function UpdateGrid(args)
    {
    args = args + “$” + window.document.getElementById(‘ddl’).value;
    <%= ClientScript.GetCallbackEventReference(this,”args”, “ShowResult”, null) %>
    }
    After rendering, if we open the source of the page; above function will appear as
    function UpdateGrid(args)
    {
    args = args + “$” +            window.document.getElementById(‘ddl’).value;
    WebForm_DoCallback(‘__Page’,args,ShowResult,null,null,false)
    }
    When we want to do a Callback, we are actually firing an event which is related to theWebform. Therefore the UpdateGrid(args) function needs to be placed in the <form> tag, otherwise we will get a JavaScript error.
    We register this function by using Page.ClientScript.RegisterClientScriptBlock so the function will appear in <form></form> tag only.
    function ShowResult(eventArgument,context)
    {
    window.document.getElementById(‘Gridview’).innerHTML = eventArgument;
    }
    This function will be fired after server sends a response back to client, thus this function will handle the server response. We have simply put the response as innerHTML of the Div tag. The innerHTML contains the HTML of the updated grid.
    The server-Side functions are as follows
    public string GetCallbackResult()
    {
    return result;
    }
    This function simply returns the result which we have put as innerHTML in theShowResult function.
    public void RaiseCallbackEvent(string eventArgument)
    {
    string[] args = eventArgument.Split(‘$’);
    if (args[0] == “sort”) { sortGrid(args[1], args[2]); }
    else if (args[0] == “changePage”) { changePage(args[1], args[2]); }
    else if (args[0] == “changePageLength”) { changePageLength(args[1], args[2]); }
    }
    In this function the eventArgument will appear as “changePage$1$10” or “sort$1$10” or  “changePageLength$1$10” or “sort$Contact Name Asc$10” or  “sort$Contact Name Desc$10” in the rendered page
    If we split this on “$”, we will get
    (In changePageLength we don’t need args[2]; I have kept is just to simplify the code)
    In RaiseCallbackEvent we have called the function which we have decleared in POINT 3, each function in point 3 will do the respective action and return a HTML string of the Grid.
    if (!IsPostBack)
    {
    _grid.DataSource = _sampleData;
    _grid.DataBind();
    ddl.Items.Add(“10″);
    ddl.Items.Add(“20″);
    ddl.Items.Add(“30″);
    ddl.Attributes.Add(“onchange”, “javascript:UpdateGrid(‘changePageLength$’ + this.value);”);
    }
    [At this point if we compile the code we will able to chage the page  length ]
    //break//
    For adding the columnwise sorting functionality in grid, we will modify columnheader row of the GridView. 
    if (e.Row.RowType == DataControlRowType.Header)
    {
    for (int i = 0; i < e.Row.Cells.Count; i++)
    {
    e.Row.Cells[i].Text = string.Format(“{0}<img alt=\”Ascending\” src=\”Images/up.jpg\” onclick=\”UpdateGrid(’sort${0} Asc’)\”; /><img alt=\”Descending\” src=\”Images/down.jpg\” onclick=\”UpdateGrid(’sort${0} desc’) \”; />”, e.Row.Cells[i].Text);
    }
    }
    We have added up and down images and an onclick event of either of them will call theUpdateGrid function (Ref. Point 4) for sorting  Ascending and Descending order repectively. [At this point you can complie the code and the grid will be sortable].
    After this we will modify the Pager row in such a way that we can use it to change the page index of the grid using UpdateGrid function.(Ref. point 4 public void RaiseCallbackEvent)
    else if (e.Row.RowType == DataControlRowType.Pager)
    {
    GridView gdv = (GridView)sender;
    int _pageCount = gdv.PageCount;
    e.Row.Cells[0].Text = “”;
    for (int i = 0; i < _pageCount; i++)
    {
    HyperLink hyp = new HyperLink();
    hyp.Text = i.ToString() + “&nbsp;”;
    hyp.Attributes.Add(“href”, “javascript:UpdateGrid(‘changePage$” + i + “‘);”);
    e.Row.Cells[0].Controls.Add(hyp);
    extCode>         Label l = new Label();
    l.Text = “&nbsp;”;
    e.Row.Cells[0].Controls.Add(l);
    hyp = null;
    }
    }
    _pageCount contains the pagecount of the GridView. We have to display pages as Numbers so we used a HyperLink and a Label to add space after each page number.
    Finally, we can set EnableViewState=”false” this will not affect the functionality but the extra code of the ViewState will not come on client side which leads to lighter page.
    The entire code for the sample can be downloaded here

Comments