How to maintain sorting throughout paging in gridview in asp.net.
Below is my code behind for binding grid, paging, and sorting
private string SortDirection
{
get { return ViewState["SortDirection"] != null ? ViewState["SortDirection"].ToString() : "ASC"; }
set { ViewState["SortDirection"] = value; }
}
private void BindGV(string sortExpression = null)
{
string CS = ConfigurationManager.ConnectionStrings["DBCS"].ConnectionString;
SqlConnection con = new SqlConnection(CS);
SqlCommand cmd = new SqlCommand("SPGetEmpDetailes", con);
cmd.CommandType = CommandType.StoredProcedure;
SqlDataAdapter sda = new SqlDataAdapter(cmd);
using (DataTable dt = new DataTable())
{
sda.Fill(dt);
if (sortExpression != null)
{
DataView dv = dt.AsDataView();
this.SortDirection = this.SortDirection == "ASC" ? "DESC" : "ASC";
dv.Sort = sortExpression " " this.SortDirection;
EmployeeGV.DataSource = dv;
}
else
{
EmployeeGV.DataSource = dt;
}
EmployeeGV.DataBind();
}
}
I am new to .NET and I want to maintain sorting throughout paging but I don't know how to do it.
protected void EmployeeGV_PageIndexChanging(object sender, GridViewPageEventArgs e)
{
EmployeeGV.PageIndex = e.NewPageIndex;
BindGV();
}
protected void EmployeeGV_Sorting(object sender, GridViewSortEventArgs e)
{
this.BindGV(e.SortExpression);
}
CodePudding user response:
Ok, so since we have a data pager here, then that means the data page routine will re-bind the GV. So, that means:
Loading up - first page load - we fill the grid
When we page - we fill the grid
When we sort - we fill the grid.
So, from above, we see at least 3 places. That suggests we need (and want) ONE routine to load up the grid - since we have several places where we will do this.
And I see you have one routine for this. We tweak it up a bit.
Next up:
The sort value we get/have does not persit, so once again we can use ViewState for that. (and again I see you attempting to use that).
So, lets lay this out.
First up, and NEVER forget this rule:
You want to load combo boxes, grids, listbox and ANY data on your FIRST page load, and ONLY your first page load, and ONLY first time. If you break this rule then you in effect cannot build a working web forms page - you just can't!!!!
And the reason is simple:
Any button click, any post-back, and any code operations on that page will ALWAYS and I repeat ALWAYS will trigger the page load event first and then YOUR event - (button click, paging or whatever). So, that means we ALWAYS will have our page load code stub and have a if (!IsPostBack) code stub - (darn near always follow this design pattern).
Ok, so lets code this up.
next up: I see no reason to put say a datatable in some using block - they safe dispose on their own. However, adopting using blocks for sql connections, and things like a sql command object - yes, do apply a using block.
Ok, I think we quite much cleared up the base issues.
So, lets setup the code, say like this:
So, say our simple markup - we dropped in a row select button.
<div style="padding:35px;width:55%">
<asp:GridView ID="GridView1" runat="server"
AutoGenerateColumns="False" DataKeyNames="ID" CssClass="table"
AllowPaging="True" OnPageIndexChanging="GridView1_PageIndexChanging" PageSize="8" AllowSorting="True" OnSorting="GridView1_Sorting">
<Columns>
<asp:BoundField DataField="FirstName" HeaderText="First" SortExpression="FirstName" />
<asp:BoundField DataField="LastName" HeaderText="Last" SortExpression="LastName" />
<asp:BoundField DataField="HotelName" HeaderText="HotelName" SortExpression="HotelName" />
<asp:BoundField DataField="City" HeaderText="City" SortExpression="City" />
<asp:BoundField DataField="Description" HeaderText="Description" SortExpression="Description" />
<asp:CheckBoxField DataField="Active" HeaderText="Active" ItemStyle-HorizontalAlign="Center" />
<asp:TemplateField>
<ItemTemplate>
<asp:Button runat="server" Text="View" CssClass="btn" />
</ItemTemplate>
</asp:TemplateField>
</Columns>
<PagerStyle CssClass="GridPager" />
</asp:GridView>
</div>
Ok, now our code, say this:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
ViewState["MySort"] = "HotelName";
BindGV();
}
}
void BindGV()
{
using (SqlConnection conn = new SqlConnection(Properties.Settings.Default.TEST4))
{
using (SqlCommand cmdSQL = new SqlCommand("SELECT * FROM MyHotels", conn))
{
conn.Open();
DataTable rstData = new DataTable();
rstData.Load(cmdSQL.ExecuteReader());
// apply optional sort
if (ViewState["MySort"] != null)
rstData.DefaultView.Sort = ViewState["MySort"] as string;
GridView1.DataSource = rstData;
GridView1.DataBind();
}
}
}
protected void GridView1_PageIndexChanging(object sender, GridViewPageEventArgs e)
{
GridView1.PageIndex = e.NewPageIndex;
BindGV();
}
protected void GridView1_Sorting(object sender, GridViewSortEventArgs e)
{
ViewState["MySort"] = e.SortExpression;
BindGV();
}
And now we get this:
So, the trick or approach?
We have ONE view sate that holds our sort - when we want to sort, we simply put into this ViewState["MySort"] = some column to sort. After that, our bind load routines are free to run - and we sort if set.
Thus, the pager routine, sort routine, and load routine -- they all call that one routine, and thus we don't have to write a lot of code.
And the view button? Well, if we click on that button (say we add a click event) like this:
Then the row button click can be this:
protected void Unnamed_Click(object sender, EventArgs e)
{
Button btn = sender as Button;
GridViewRow gRow = btn.NamingContainer as GridViewRow;
Debug.Print("Row index click = " gRow.RowIndex.ToString());
// get database (hidden pk row id
int? PKID = GridView1.DataKeys[gRow.RowIndex]["ID"] as int?;
Debug.Print("Database PK = " PKID.ToString());
Debug.Print("Hotel name this row = " gRow.Cells[2].Text);
}
And clicking, we get: