Tony Tony - 1 month ago 10
ASP.NET (C#) Question

Enable Sorting and Paging in Gridview Efficiently

By using sample code as a guide, I managed to slap together some code that will perform sorting and paging on a gridview. However, I'm relatively new to webpage programming so I used ViewState quite often (which from what I've gathered, is BAD). As a result, I was wondering if there's anyway to make my code more efficient? (Or different ways of accomplishing the same thing?)

Front end:

<asp:GridView ID="UserAccounts" runat="server" AllowSorting="true" AutoGenerateColumns="false" AllowPaging="true"
OnSorting="gridView_Sorting" OnPageIndexChanging="gridView_PageIndexChanging" PageSize = "20">
<Columns>
<asp:BoundField DataField="UserName" HeaderText="UserName" SortExpression="UserName" />
<asp:BoundField DataField="Email" HeaderText="Email" SortExpression="Email" />
<asp:BoundField DataField="Roles" HeaderText="Role" SortExpression="Roles" />
<asp:CheckBoxField DataField="IsLockedOut" HeaderText="Locked Out?" SortExpression="IsLockedOut" />
<asp:CheckBoxField DataField="IsOnline" HeaderText="Online?" SortExpression="IsOnline" />
<asp:BoundField DataField="LastLoginDate" HeaderText="Last Login Date" SortExpression="LastLoginDate" />
<asp:HyperLinkField Text="Manage" DataNavigateUrlFields="UserName" DataNavigateUrlFormatString="ManageDetails.aspx?user={0}" />
</Columns>
</asp:GridView>


Code Behind to generate table:

private void BindUserAccounts()
{
DataTable dt = new DataTable();
dt.Columns.Add("UserName");
dt.Columns.Add("Email");
dt.Columns.Add("Roles");
dt.Columns.Add("IsLockedOut");
dt.Columns.Add("IsOnline");
dt.Columns.Add("LastLoginDate");

var userRoles = from MembershipUser user in Membership.FindUsersByName(this.UsernameToMatch + "%")
let roles = Roles.GetRolesForUser(user.UserName)
select new
{
UserName = user.UserName,
Email = user.Email,
Roles = string.Join(", ", roles),
IsLockedOut = user.IsLockedOut,
IsOnline = user.IsOnline,
LastLoginDate = user.LastLoginDate
};

foreach (var u in userRoles)
{
DataRow dr = dt.NewRow();
dr["UserName"] = u.UserName;
dr["Email"] = u.Email;
dr["Roles"] = u.Roles;
dr["IsLockedOut"] = u.IsLockedOut;
dr["IsOnline"] = u.IsOnline;
dr["LastLoginDate"] = u.LastLoginDate;

dt.Rows.Add(dr);
}

UserAccounts.DataSource = dt;
UserAccounts.DataBind();
ViewState["DataSource"] = dt;
}


To enable sorting and paging:

protected void gridView_PageIndexChanging(object sender, GridViewPageEventArgs e)
{
UserAccounts.PageIndex = e.NewPageIndex;
BindUserAccounts();
}

protected void gridView_Sorting(object sender, GridViewSortEventArgs e)
{
DataTable dataTable = (DataTable)ViewState["DataSource"];

if (dataTable != null)
{
DataView dataView = new DataView(dataTable);
if ((string)ViewState["SortDir"] == "ASC" || String.IsNullOrEmpty((string)ViewState["SortDir"]))
{
dataView.Sort = e.SortExpression + " ASC";
ViewState["SortDir"] = "DESC";
}
else if ((string)ViewState["SortDir"] == "DESC")
{
dataView.Sort = e.SortExpression + " DESC";
ViewState["SortDir"] = "ASC";
}

UserAccounts.DataSource = dataView;
UserAccounts.DataBind();
}
}

Answer

The code you posted is as efficient as it can be due to the way that you are binding the data to the GridView. More efficient ways can be achieved but not without changing the data binding logic.

For example, a more efficient way to do this could be to handle paging and sorting on the client side using jQuery+dataTables. Another way that would require less code from your part can be accomplished using SqlDataSource and setting it as the DataSource for the GridView - you wouldn't have to do the resorting/paging in code. But again, both approaches require significant changes. Your code is as efficient as it can be IMO.

Update: Tim made a good point on his comment - don't persist the data table on ViewState. Either put it in Session (consider the size of the data before starting to put things in Session) or simply ask the DB to send the data again. Adding this DataTable to ViewState will considerably increase the page size.