Introduction
Typical ASP.NET DataGrid
sorting concerns database access and saving data in viewstate. So, minimizing database access times and the size of viewstate would be the key to achieve optimal DataGrid
sorting solution. To minimize database access, it would be a good solution to keep data source in cache such that its time and memory will be balanced. To minimize viewstate, we can turn off the viewstate. However, if the viewstate is disabled, the built-in sorting functionality would get lost and thus the beauty of DataGrid
sorting would disappear since we may have to provide extra buttons. This article introduces a way to overcome the deficiency above to achieve optimal DataGrid
sorting, and also a way to minimize page viewsate while still keeping its functionality.
Non-display ASP.NET Button
For ASP.NET Button
, it has two important properties: Enabled
and Visible
. Often, another style property is overlooked. That is, Display
. If Display
is set to none, the Button
would disappear when it is browsed. But note that it still exists in DHTML object model and it functions like a visible button. We can use this nice feature to disable DataGrid
viewstate. And then use JavaScript to transfer a click event to a non-display button to trigger page postback. In the event handler of this button at server side, we can do things as in DataGrid
item command event handler.
Implementation
To demonstrate this idea, put the following piece of code in HTML page. Note that DataGrid
viewstate has been turned off. A non-display button has been added in.
<asp:datagrid id="dgTest" Runat="server" Width="90%"
EnableViewState="False" ShowHeader="True"
AutoGenerateColumns="False" GridLines="Horizontal"
BackColor="lightblue">
<HeaderStyle Wrap="False" Font-Bold="True" BackColor="#3300cc"
ForeColor="white"></HeaderStyle>
<AlternatingItemStyle BackColor="LawnGreen"></AlternatingItemStyle>
<Columns>
<asp:BoundColumn HeaderText="Last Name" DataField="LastName"
ReadOnly="True"></asp:BoundColumn>
<asp:BoundColumn HeaderText="First Name" DataField="FirstName"
ReadOnly="True"></asp:BoundColumn>
<asp:BoundColumn HeaderText="Address" DataField="Address"
ReadOnly="True"></asp:BoundColumn>
<asp:BoundColumn HeaderText="City" DataField="city"
ReadOnly="True"></asp:BoundColumn>
<asp:BoundColumn HeaderText="Region" DataField="Region"
ReadOnly="True"></asp:BoundColumn>
<asp:BoundColumn HeaderText="PostalCode" DataField="PostalCode"
ReadOnly="True"></asp:BoundColumn>
<asp:BoundColumn HeaderText="Country" DataField="Country"
ReadOnly="True"></asp:BoundColumn>
<asp:TemplateColumn>
<HeaderTemplate>
View Detail
</HeaderTemplate>
<HeaderStyle HorizontalAlign="Center"></HeaderStyle>
<ItemTemplate>
<asp:LinkButton ID="lnkSelect" Runat="server"
text="Select"></asp:LinkButton>
</ItemTemplate>
<ItemStyle HorizontalAlign="Center"></ItemStyle>
</asp:TemplateColumn>
</Columns>
</asp:datagrid>
<br>
<asp:Button ID="SortButton" style="DISPLAY:none" Runat="server"
Text="Sort"></asp:Button>
To transfer an event click to non-display button, modify head text and add some JavaScript as follows:
private void SetHeadText(SORT_ORDER order)
{
string headtext = "<a href='#' onclick='javascript:document" +
".getElementById(\"{0}\").click();'> Address</a>";
if(order == SORT_ORDER.DESC)
{
this.dgTest.Columns[this.Col_Address].HeaderText =
string.Format(headtext,this.SortButton.ClientID);
}
else
{
this.dgTest.Columns[this.Col_Address].HeaderText =
string.Format(headtext,this.SortButton.ClientID);
}
}
In button event handler, add the following code. Sort order has been saved in page viewstate.
private void SortButton_Click(object sender, System.EventArgs e)
{
SORT_ORDER order = (SORT_ORDER)ViewState[SORTORDER];
if(order == SORT_ORDER.ASC)
{
this.BindData(SORT_ORDER.DESC);
}
else
{
this.BindData(SORT_ORDER.ASC);
}
}
Next is bind data. SetHeadText
must be called before calling DataGrid
's DataBind
function.
private void BindData(SORT_ORDER order)
{
DataView dv = this.GetDataSource().DefaultView;
if(order == SORT_ORDER.ASC)
{
this.SetHeadText(SORT_ORDER.DESC);
this.ViewState[SORTORDER] = SORT_ORDER.ASC;
dv.Sort = "Address ASC";
this.SortLabel.Text = "Current sorting order is ascending";
}
else
{
this.SetHeadText(SORT_ORDER.ASC);
this.ViewState[SORTORDER] = SORT_ORDER.DESC;
dv.Sort = "Address DESC";
this.SortLabel.Text = "Current sorting order is descending";
}
this.dgTest.DataSource = dv;
dgTest.DataBind();
}
To minimize database access, we keep data in cache.
private DataTable RetrieveDataFromDB()
{
string SelectString = "select * from Employees";
DataTable dt = new DataTable();
using(SqlDataAdapter adpt = new
SqlDataAdapter(SelectString,this.NorthWindConnStr ))
{
adpt.Fill(dt);
}
return dt;
}
private DataTable GetDataSource()
{
object o;
o = this.Cache[this.CacheKey];
if (o != null)
{
return (DataTable)o;
}
else
{
DataTable dt = RetrieveDataFromDB();
this.Cache.Add(this.CacheKey,dt,null,
System.Web.Caching.Cache.NoAbsoluteExpiration,
System.TimeSpan.FromMinutes(this.CacheTimeSpan),
System.Web.Caching.CacheItemPriority.Normal,null);
return dt;
}
}
Live Demo
You can download code to play with.