8

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.

4

2 回答 2

2

由于我对我提出的解决方案不满意(为未来的开发带来了太多问题),我决定采用“控制开发”的方式来确保一切都正确创建。无论我使用哪种类型的 PagerTemplate,它都会发生 - 我使用的是一种,回发不会从最后一页触发。希望我不是唯一一个:-)

对于那些遇到同样问题的人,我提供了可以正常工作的自定义控件(当然,没有实现 PagerSettings 和 PagerTemplates,但带来了基本功能)。

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);
    }
}

只要确保你的标记是: - AllowPaging="true" - AllowCustomPaging="false" - PageSize="whatever" - 你仍然在后面的代码中提供 VirtualItemCount

使用 SelectMethod 背后的代码可能是这样的:

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();
}

由于这是我使用出色的 Bootstrap 框架为 .NET 创建服务器端控件的好几次,我在这里创建了一个 git https://github.com/Gitzerai/Bootstrap.NET,我将控件以正确的方式呈现在引导程序中。

于 2013-09-14T19:24:02.483 回答
0

我发现这也发生在我身上。在回发时,最后一页将用第一页中的行“填充”最后一页,但数据之后的第一行除外。示例:如果页面大小为 10 并且我有 25 行。最后一页最初会显示第 21-25 行,然后在回发时会添加第 7-10 行。

我在 gridview 的 RowCreated 中添加了以下“hack”,以防止绘制这些幻像行。GV 是网格视图。DataRowCount 是一个从数据源返回行数的函数。PageIndex 是一个使用会话来保存当前页面索引的属性。

        If e.Row.RowType = DataControlRowType.DataRow Then
        Dim RowsLeft As Integer = DataRowCount() - (GV.PageSize * PageIndex)
        Dim RowsExpected As Integer

        If RowsLeft > GV.PageSize Then
            RowsExpected = GV.PageSize
        Else
            RowsExpected = RowsLeft
        End If

        If e.Row.RowIndex >= RowsExpected Then
            'Last page isn't full, need to force writing nothing out for extra rows
            e.Row.SetRenderMethodDelegate(New RenderMethod(AddressOf RenderNothing))
        End If
    End If

然后我添加了以下功能:

Public Sub RenderNothing(writer As HtmlTextWriter, container As Control)
End Sub

由于 RowCreated 发生在 ViewState 加载之前,因此 GV 的 PageIndex 不可用。所以我创建了一个属性来保存 PageIndex。因此,我的代码现在更新了新属性,并且该属性将其保存到会话并更新了 GV 的属性。这是我添加的属性

Private Const SS_PagerControl_PageIndex As String = "SSPagerControl_PageIndex"
<Bindable(True), CategoryAttribute("Paging"), DefaultValue("0")>
Public Property PageIndex As Integer
    Get
        If Session(SS_PagerControl_PageIndex) Is Nothing Then
            Return 0
        End If

        Return CInt(Session(SS_PagerControl_PageIndex))
    End Get
    Set(ByVal value As Integer)
        Session(SS_PagerControl_PageIndex) = value
        GV.PageIndex = value
        RebindGrid()
    End Set
End Property
于 2015-06-23T21:55:10.027 回答