5

i've tried to ask this question a number of ways. It's a difficult question to answer because you have to understand what's going on.

When do i fill a GridView?


The nieve answer is during Page_Load, if not a PostBack:

protected void Page_Load(object sender, EventArgs e)
{
    if (!IsPostBack)
    {
       DataSet ds = GetStuffToShow();
       GridView1.DataSource = ds;
       GridView1.DataBind();
    }
}

The problem with that is that if it is a postback, the grid is not filled. The reason the grid is not filled is because we've turned off the viewstate of the grid.

So don't look at IsPostBack

We need to always fill the grid, postback or not:

protected void Page_Load(object sender, EventArgs e)
{
    DataSet ds = GetStuffToShow();
    GridView1.DataSource = ds;
    GridView1.DataBind();
}

The problem with that is that if the user sorts a column, the OnSorting event is called after both Page_Init and Page_Load:

protected void GridView1_Sorting(object sender, GridViewSortEventArgs e)
{
    DataSet ds = GetStuffToShow(e.SortExpression, e.SortDirection);
    GridView1.DataSource = ds;
    GridView1.DataBind();
}    

we've run two database queries, when only one was required.

Cache is fine for column sorting

If i'm willing to accept invalid cache during column sorting, i can store the DataSet in the session variable, as long as i invalidate it for any other operation.

The problem is the OnSorting event is called after i need it (Page_Load):

protected void Page_Load(object sender, EventArgs e)
{
    if (AGridViewOnSortingEventIsntBeingRaised)
    {
       DataSet ds = GetStuffToShow();

       StoreTheDatasetInTheSessionSomehowInCaseTheyCallSortInTheFuture(ds);

       GridView1.DataSource = ds;
       GridView1.DataBind();
    }
}

protected void GridView1_Sorting(object sender, GridViewSortEventArgs e)
{
    DataSet ds = GetDataSetOutOfSessionSomehowThatDamnWellBetterBeThere();

    SomehowSortAReadOnlyDisconnectedDataSet(ds, e.SortExpression, e.SortDirection);

    GridView1.DataSource = ds;
    GridView1.DataBind();
}    

Fear of the unknown

Then there's still the terror i have because i turned off viewstate of the GridView. i don't think that a read-only asp:GridView should need tens of kilobytes base64 encoded, when i can just rebuild it from the server (or from memory).

But i believe that i am obligated to return the GridView to the state it was in the last time the page was rendered. And i have to do it before Page_Load (i.e. during Page_Init). i have this fear because someone said so. So i turn it into

protected void Page_Init(object sender, EventArgs e)
{
    if (AGridViewOnSortingEventIsntBeingRaised)
    {
       DataSet ds = GetStuffToShow();

       StoreTheDatasetInTheSessionSomehowInCaseTheyCallSortInTheFuture(ds);

       GridView1.DataSource = ds;
       GridView1.DataBind();
    }
}

The problem with this is that GetStuffToShow depends on things the user has typed into text boxes, which don't exist during Page_Init

Anyway, i'm rambling. It's too hot in here. Hopefully this question will be answered, unlike my other recent frustrations with asp.net

Bonus Reading

Community
  • 1
  • 1
Ian Boyd
  • 220,884
  • 228
  • 805
  • 1,125
  • Is it possible in your case to do server side sorting, that is doing sorting everytime GetStuffToShow is called. You would still need to persist curent sort expression and pass it everytime into GetStuffToShow(sortExpression). Saving DataSet to the session is ok for small sets, but for huge ones it will be a big no-no. Sorting on database level is always better for huge sets. – fenix2222 Jun 19 '12 at 23:02
  • It is very possible to to so (SQL) Server side sorting. i still come up the again the problem of the ASP.net Page Cycle forcing me to run the same query two or three times, when i only want one. – Ian Boyd Jun 20 '12 at 01:08
  • What if you create BindData() method which gets sorted dataset, then in pageload use (!IsPostback) to make sure it loads only once. Then OnSortingEvent should call BindData or any other methods that do postback. This way it will only be called once – fenix2222 Jun 20 '12 at 01:14
  • If i call `BindData()` **only** during page load, then it won't get called again a moment later in `OnSorting`, when i need to *reload* the data. – Ian Boyd Jun 20 '12 at 01:36
  • Yes, as I said, call BindData() when OnSorting runs to reload it and pass sort expression – fenix2222 Jun 20 '12 at 01:42
  • @fenix2222 Oh, you mean get an an *unsorted* dataset from SQL Server during `PageLoad`, apply default sorting, save it in a member variable, and bind it. And if there *happens* to be an `OnSorting` call later, i re-sort the *in-memory* `DataSet`, and re-bind it to the GridView? Two binds rather than one? – Ian Boyd Jun 20 '12 at 02:06
  • @IanBoyd How are you using the GridView? Only for displaying info, or also for editing, hosting other controls that can post back etc? – user1429080 Jun 26 '12 at 14:37
  • I would suggest you use ASP.NET object DataSource with input paramters, instead of doing so many bind and forget about when to bind ti will all be taken care by the datasource and grid. – Guru Kara Jun 27 '12 at 13:00

3 Answers3

0

By adding a couple of hidden fields, one for the sort expression, and the other for the sort direction you can use those values to populate the GridView once at page load and then update the sorting in the Sorting event (sort code modified from the All-In-One Code Framework GridView sample):

    protected void Page_Load(object sender, EventArgs e)
    {
        DataSet ds = GetStuffToShow();
        GridView1.DataSource = ds;
        GridView1.DataBind();
    }

    protected void GridView1_Sorting(object sender, GridViewSortEventArgs e)
    {
        // If the sorting column is the same as the previous one, 
        // then change the sort order.
        if (SortExpression.Value.Equals(e.SortExpression))
        {
            SortDirection.Value = SortDirection.Value.Equals("ASC") ? "DESC" : "ASC";
        }
        // If sorting column is another column, 
        // then specify the sort order to "Ascending".
        else
        {
            SortExpression.Value = e.SortExpression;
            SortDirection.Value = "ASC";
        }

        var sortedView = new DataView(<convert your DataSet to a DataTable>)
            { Sort = string.Format("{0} {1}", this.SortExpression.Value, this.SortDirection.Value) };
        GridView1.DataSource = sortedView;

        GridView1.DataBind();
    }

Note that SortDirection and SortExpression are the hidden fields. This also lends itself well to caching the DataSet.

Also, I wouldn't be concerned about the Page_Init issue that you brought up. That should only apply if you are dynamically creating your Controls.

GunnerL3510
  • 705
  • 3
  • 15
0

A simple solution is to call Gridview.DataBind() on the Page.Pre_Render event, which makes it called after having handled any Button/Sorting, events. This a good way to ensure you call it only once per Request.

To make things clearer, it is also a good thing to access your dataset through a Property which will basically call your "Store-The-Dataset-In-The-Session-Somehow-In-Case-They-Call-Sort-In-The-Future" method in its Set part and your "Get-Data-Set-Out-Of-Session-That-Had-Better-Be-There" in the Get part.

4444
  • 3,523
  • 10
  • 27
  • 43
Lucas
  • 1
0

You may try to fill the grid on Gridview Needdatasource event it will be called when ever you perform a postback and get the proper functionality what ever you code in the event. also if you want to bind it again you can just data databind method and that will call the needdatasource event again

vikzzz
  • 43
  • 1
  • 1
  • 5