Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET 4.5 GridView: PostBack of last page

I have discovered an issue with GridView pager in ASP.NET 4.5 and 4.5.1 version. Since .NET 2 - 4 I have never experienced such problem.

To the point, I have a gridview that I am populating with data in code behind like this:

protected int CurrentPage { get { return SearchResults.PageIndex + 1; } }

protected void Page_Load(object sender, EventArgs e)
{
    if(!IsPostBack)
         BindGrid();
}

private void BindGrid()
{
    int totalRowCount = 0;
    SearchResults.DataSource = GetPageData(SearchResults.PageIndex, SearchResults.PageSize, out totalRowCount);
    SearchResults.VirtualItemCount = totalRowCount;                   
    SearchResults.DataBind();
}

private IEnumerable GetPageData(int start, int count, out int totalRowCount)
{
    return Membership.GetAllUsers(start, count, out totalRowCount);
}

protected void SearchResults_PageIndexChanging(object sender, GridViewPageEventArgs e)
{            
    SearchResults.PageIndex = e.NewPageIndex;            
    BindGrid();
}

The problem is that if I hit the last page of GridView and I try to return to any other page, my PageIndexChanging does not fire. The problem occurs only if the last page does not have same count of records as PageSize. The behavior is that my page gets reloaded, the page of gridview is filled with empty data rows up to the PageSize. The VirtualItemCount represents correctly total ItemCount.

Markup, if you find something there:

<asp:GridView runat="server" CellPadding="0" CellSpacing="0" GridLines="None" CssClass="table table-condensed table-striped table-footer"
        ID="SearchResults" AllowCustomPaging="true" AllowPaging="true" PageSize="6" OnPageIndexChanging="SearchResults_PageIndexChanging" AutoGenerateColumns="false" UseAccessibleHeader="true">
...
<PagerTemplate>
            <span class="pull-left">
                <strong><%= SearchResults.PageIndex * SearchResults.PageSize + 1 %></strong> - <strong><%= CurrentPage * SearchResults.PageSize %></strong>
            </span>
            <span class="pull-left">
                Total records: <strong><%= SearchResults.VirtualItemCount %></strong>
            </span>
            <ul class="pagination pull-right">
                <li><asp:LinkButton runat="server" CommandName="Page" CommandArgument="First"><span class="glyphicon glyphicon-backward"></span></asp:LinkButton></li>

                <li><asp:LinkButton runat="server" CommandName="Page" CommandArgument="<%# CurrentPage - 2 %>" Visible="<%# CurrentPage > 2 %>"><%= CurrentPage - 2 %> </asp:LinkButton></li>
                <li><asp:LinkButton runat="server" CommandName="Page" CommandArgument="<%# CurrentPage - 1 %>" Visible="<%# CurrentPage > 1 %>"><%= CurrentPage - 1 %> </asp:LinkButton></li>
                <li class="active"><a href="#"><%= CurrentPage %></a></li>
                <li><asp:LinkButton runat="server" CommandName="Page" CommandArgument="<%# CurrentPage + 1 %>" Visible="<%# CurrentPage < SearchResults.PageCount %>"><%= CurrentPage + 1 %></asp:LinkButton></li>
                <li><asp:LinkButton runat="server" CommandName="Page" CommandArgument="<%# CurrentPage + 2 %>" Visible="<%# CurrentPage < SearchResults.PageCount - 1 %>"><%= CurrentPage + 2 %></asp:LinkButton></li>

                <li><asp:LinkButton runat="server" CommandName="Page" CommandArgument="Last"><span class="glyphicon glyphicon-forward"></span></asp:LinkButton></li>
            </ul>
        </PagerTemplate>
</asp:GridView>

Thank you very much, I have been dealing with this for days. Of course I could use QueryString approach, but since I will be using a lot of tables, I would like to stick with the postback approach, if possible...


EDIT:

Simpliest workaround I found was doing a BindGrid on every Page_Load. For some reason the PageIndexChanging just not fire on last page unless LastPageSize == PageSize. Then the DataBind is not recalled in order to bind CommandArguments, hence I cannot postback correctly.

On the other hand, it is not very clear and could possibly cause issues... At least double binding = double calls to SQL for data on pagechange... Otherwise, I have no idea how to force PageIndexChanging here and seems like a new .NET issue to me.

like image 417
Gitzerai Avatar asked Sep 13 '13 19:09

Gitzerai


1 Answers

As I was not satisfied with my proposed solution (brought too many issues for future development), I have decided to go the "Control development" way to ensure everything is created correctly. It occurs no matter the type of PagerTemplate I am using - I am using one, the postback does not fire from Last page. Hopefully I am not the only one :-)

For those who are experiencing the same issues, I am providing custom control that works OK (of course, does not implement PagerSettings and PagerTemplates, but brings the basic functionality).

public class ExtendedGridView : System.Web.UI.WebControls.GridView
{
    protected override void InitializePager(System.Web.UI.WebControls.GridViewRow row, int columnSpan, System.Web.UI.WebControls.PagedDataSource pagedDataSource)
    {
        HtmlGenericControl ul = new HtmlGenericControl("ul");

        ul.Attributes.Add("class", "pagination pull-right");

        AddPager(ul, commandArgument: "First", text: "<span class='glyphicon glyphicon-fast-backward'></span>");

        for (int i = 0; i < PageCount; i++)
        {
            AddPager(ul, i);
        }

        AddPager(ul, commandArgument: "Last", text: "<span class='glyphicon glyphicon-fast-forward'></span>");

        row.CssClass = "table-footer";
        row.Cells.Add(new System.Web.UI.WebControls.TableCell());
        row.Cells[0].ColumnSpan = columnSpan;
        row.Cells[0].Controls.AddAt(0, ul);            
    }

    protected virtual void navigate_Click(object sender, EventArgs e)
    {
        string commandArgument = ((System.Web.UI.WebControls.LinkButton)sender).CommandArgument.ToString();
        int pageIndex = 0;

        if (!int.TryParse(commandArgument, out pageIndex)) {
            switch (commandArgument)
            {
                case "First": pageIndex = 0; break;
                case "Last": pageIndex = PageCount - 1; break;
                case "Prev": pageIndex = PageIndex - 1; break;
                case "Next": pageIndex = PageIndex + 1; break;
            }
        }

        OnPageIndexChanging(new System.Web.UI.WebControls.GridViewPageEventArgs(pageIndex));
    }

    private void AddPager(System.Web.UI.Control parentControl, int pageIndex = -1, string commandArgument = null, string text = null)
    {
        HtmlGenericControl li = new HtmlGenericControl("li");

        if (pageIndex == PageIndex)
            li.Attributes.Add("class", "active");

        System.Web.UI.WebControls.LinkButton button = new System.Web.UI.WebControls.LinkButton();
        button.CommandName = "Page";

        if (text == null)
            button.Text = (pageIndex + 1).ToString();
        else
            button.Text = text;

        if (string.IsNullOrWhiteSpace(commandArgument))
            button.CommandArgument = string.Format("{0}", pageIndex);
        else
            button.CommandArgument = commandArgument;

        button.Click += navigate_Click;

        li.Controls.Add(button);
        parentControl.Controls.Add(li);
    }
}

Just make sure your markup is: - AllowPaging="true" - AllowCustomPaging="false" - PageSize="whatever" - and you still provide VirtualItemCount in code behind

Code behind using SelectMethod could be like this:

protected void Page_Load(object sender, EventArgs e)
{

}

public IEnumerable SearchResults_GetData(int startRowIndex, int maximumRows, out int totalRowCount, string sortByExpression)
{
    int pageIndex = (int)(startRowIndex / maximumRows);
    return Membership.GetAllUsers(pageIndex, maximumRows, out totalRowCount);
}

protected void SearchResults_PageIndexChanging(object sender, GridViewPageEventArgs e)
{
    SearchResults.PageIndex = e.NewPageIndex;
    SearchResults.DataBind();
}

As this is several time I am created serverside controls for .NET using excellent Bootstrap framework, I created a git here https://github.com/Gitzerai/Bootstrap.NET where I put controls that render in a bootstrap proper way.

like image 197
Gitzerai Avatar answered Nov 20 '22 00:11

Gitzerai