如何在服务器端分页中使用DataPager?

时间:2010-07-08 14:25:01

标签: asp.net server-side datapager

我正在尝试使用DataPager进行服务器端分页。这是我的代码

<asp:DataPager ID="pgrFooBars" PagedControlID="lvFooBars" 
    QueryStringField="page" runat="server" >
<Fields>
    <asp:NumericPagerField />
</Fields>
</asp:DataPager>

背后的代码

protected void Page_Load(object sender, EventArgs e)
{
    ConfigureBlogPostListView();
}

private void ConfigureBlogPostListView()
{
    int pageNum;
    int.TryParse(Request.Params["page"], out pageNum);
    int pageSize = 20;

    PagedList<IFooBar> FooBars = FooService.GetPagedFooBars(
        new PagingSettings(pageNum, pageSize));

    ListViewPagedDataSource ds = new ListViewPagedDataSource();
    ds.AllowServerPaging = true;
    ds.DataSource = FooBars;
    ds.MaximumRows = pageSize;
    ds.StartRowIndex = pageNum;
    //TotalCount is the total number of records in the entire set, not just those loaded.
    ds.TotalRowCount = FooBars.TotalCount;

    lvFooBars.DataSource = ds;
    lvFooBars.DataBind();

    pgrFooBars.PageSize = pageSize;
    pgrFooBars.SetPageProperties(pageNum, FooBars.TotalCount, true);
}

PagedList来自RobConery的有用帖子http://blog.wekeroad.com/2007/12/10/aspnet-mvc-pagedlistt/

问题是DataPager似乎使用ListView的Count属性来确定记录总数,在这种情况下是20。不知何故,它需要知道有1,500条记录,而不是20条记录。 DataPager有一个属性TotalRowCount,但这是只读的。

我从未见过使用服务器端分页的DataPager示例,但假设它可以执行服务器端分页,否则QueryStringField属性有什么用呢?

我知道您可以使用类似4GuysFromRolla这样的方法http://www.4guysfromrolla.com/articles/031506-1.aspx来执行自定义分页解决方案,但我首先想知道在创建自定义解决方案之前是否可以使用DataPager的解决方案。< / p>

更新 我越是看到这一点,我就越能得出结论,这是不可能的,不幸的是,数据采集器只是针对小型网站的控件。如果正确构建控件,我想要做的事情应该非常简单。我希望能够说出

dpFooBars.TotalRowCountComputed = false;
dpFooBars.TotalRowCount = AnyNumberThatISoChoose;

我一直在寻找一些黑客来完成同样的事情,但似乎数据抓取器的TotalRowCount是根据它所绑定的数据源中的实际项目数来计算的。对我来说,微软会同时创建一个ListViewPagedDataSource()类和一个DataPager,而不是让它们一起正常工作,这似乎很奇怪,但这似乎已经发生了。

更新2(AHA MOMENT?) 似乎可以通过使用ObjectDataSource并自定义SelectCountMethod()来从.Net 2.0开始进行服务器端分页。我相信应该可以自定义ObjectDataSource以满足我的需求。嗯。我要去周末,所以我要花几天时间看看这是否有效。敬请关注,真正的信徒。

3 个答案:

答案 0 :(得分:10)

您需要做什么 - 使用listview,datapager和linq数据源的SQL服务器分页。如你所说TotalRowCount是只读的。还有SetPageProperties方法可用于设置总行数,但这对我来说也不起作用。

然而我设法以这种方式欺骗它。我有一个带有空项模板的虚拟隐藏列表视图。寻呼机将指向此虚拟列表视图而不是原始列表视图。然后我们需要将虚拟列表视图绑定到具有与原始数据源中相同数量的项的虚拟集合。这将确保正确呈现寻呼机。这是标记。

<asp:DataPager ID="pdPagerBottom" runat="server" PagedControlID="lvDummy" PageSize="5" QueryStringField="page">
<Fields>
    <asp:NumericPagerField ButtonType="Link" RenderNonBreakingSpacesBetweenControls="true" NextPageText="Next" PreviousPageText="Previous" ButtonCount="40" />
</Fields>
</asp:DataPager>

<asp:ListView ID="lvDummy" runat="server" Visible="false">
<ItemTemplate></ItemTemplate>
</asp:ListView>

<asp:ListView ID="lvPropertyList" runat="server">...</asp:ListView>

和背后的代码

        var datasourceQuery = context.Properties.OrderBy(x => x.PropertyID).Skip(pdPagerBottom.StartRowIndex).Take(pdPagerBottom.PageSize);

        this.lvPropertyList.DataSource = datasourceQuery;
        this.lvPropertyList.DataBind();

        this.lvDummy.DataSource = new List<int>(Enumerable.Range(0, context.Properties.Count()));
        this.lvDummy.DataBind();

用100k记录测试它。是一种享受。

答案 1 :(得分:3)

似乎让服务器端分页与DataPager一起使用的唯一方法是在标记中使用LinqDataSource更新:这不是真的,见下文),并设置ListView的DataSourceID(如ScottGu's sample - step 6中所示),而不是通过ListView DataSource属性。但是我确实发现有一个trick使这个更可行,所以你可以通过代码隐藏而不是标记来定义你的LinqDataSource查询(注意,文章说这将适用于任何DataSource控件但我不喜欢我认为是这样的。)

这可能对您的情况没有任何帮助,因为我注意到您调用某种服务可能不会(也不应该)返回一个必要的IQueryable结果,这是必要的。无论如何,无论如何你都感兴趣......

aspx标记:

<asp:ListView ID="lvFooBars" DataSourceID="dsLinq" ....>
   ......
</asp:ListView>

<asp:DataPager ID="pgrFooBars" PagedControlID="lvFooBars" 
    QueryStringField="page" runat="server" >
<Fields>
    <asp:NumericPagerField />
</Fields>
</asp:DataPager>

<asp:LinqDataSource id="dsLinq" runat="server" OnSelecting="dsLinq_Selecting" />

代码隐藏:

protected void dsLinq_Selecting(object sender, LinqDataSourceSelectEventArgs e)
{
    //notice this method on FooService requires no paging variables
    e.Result = FooService.GetIQueryableFooBars(); 
}

注意,MSDN表示QueryStringField属性:

  

如果您设置此属性非常有用   想拥有所有数据页面   由搜索引擎索引。这个   因为控件产生了一个   每页数据的不同网址。

更新:实际上你可以使用ObjectDataSource控件而不是LinqDataSource控件来使用它 - 请参阅this article and download the sample。虽然使用ObjectDataSource并不像使用LinqDataSource控件那么简单,但它使用较少的魔法linq(而是使用大量的魔术字符串映射到业务/数据层方法)并允许IEnumerable暴露于您的数据访问方法而不是IQueryable。

尽管如此,我从来没有真正喜欢在我的UI标记中嵌入任何类型的DataSource控件(我相信这是必要的),所以我可能会避开DataPager控件,除了你在你的建议中的小应用程序第一次更新。

John,我注意到的另一件事是你试图将标准的Asp.Net服务器控件(依赖于ViewState)与作为Asp.Net Mvc应用程序的辅助类开发的PagedList类结合起来。虽然这可能有用,但可能会有更简单的路线。

答案 2 :(得分:0)

John,我在这里做了一些概念验证,以便您可以看到代码。我把它做成尽可能基本的,所以没有绒毛。如果您需要的某些属性遗失,请告诉我,我会修改。

突出的事情:

  1. 当您不使用DataSource对象(xml,Sql,Access ...)时,数据需要绑定在寻呼机的OnPreRender事件上。我非常确定这是你的问题,因为这是我最麻烦的地方。

  2. 必须在ListView和寻呼机上启用Viewstate(默认情况下)

  3. 代码隐藏:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    
    public partial class _Default : System.Web.UI.Page 
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!Page.IsPostBack)
            {
                this.BindListData();
            }
        }
    
        protected void dp_PreRender(object sender, EventArgs e)
        {
            this.BindListData();
        }
    
        protected void BindListData()
        {
            List<Nums> n = new List<Nums>();
            for (int i = 0; i <= 100; i++)
            {
                n.Add(new Nums { Num1 = i, Num2 = i * 3 });
            }
    
            this.lvMyGrid.DataSource = n;
            this.lvMyGrid.DataBind();
        }
    }
    
    public class Nums
    {
        public int Num1 { get; set; }
        public int Num2 { get; set; }
    }
    

    ASPX(遗漏了所有其他的毛病):

    <asp:DataPager OnPreRender="dp_PreRender" runat="server" ID="dpMyPager" QueryStringField="page" PagedControlID="lvMyGrid" PageSize="15">
        <Fields>
            <asp:NumericPagerField />
        </Fields>
    </asp:DataPager>
    
    <asp:ListView runat="server" ID="lvMyGrid" DataKeyNames="Num1">
        <LayoutTemplate>
            <asp:Literal runat="server" ID="itemPlaceholder" />
        </LayoutTemplate>
        <ItemTemplate>
            <div style="border: 1px solid red; padding: 3px; margin: 5px; float: left; clear: both;">
            <%# Eval("Num1") %> : <%# Eval("Num2") %>
            </div>
        </ItemTemplate>
    </asp:ListView>
    

    请欣赏,如果您有其他需要,请告诉我。

    麦克