如何使用带有服务器端分页的DataPager ?

时间:2022-03-28 03:38:55

I'm trying to use a DataPager to do Server Side paging. Here is my code

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

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

Code Behind

后面的代码

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 comes from RobConery's useful post http://blog.wekeroad.com/2007/12/10/aspnet-mvc-pagedlistt/.

PagedList来自RobConery的有用文章http://blog.weyasad.com/2007/12/10/aspnet -mvc-pagedlistt/。

The problem is that the DataPager appears to be using the Count property of the ListView to determine the total number of records, which in this case is 20. Somehow, it needs to know that there are 1,500, not 20 total records. The DataPager has a property TotalRowCount, but this is read-only.

问题是,DataPager似乎使用ListView的Count属性来确定记录的总数,在本例中为20。它需要知道总共有1500条记录,而不是20条。DataPager具有属性TotalRowCount,但这是只读的。

I have never seen a DataPager example with Server Side paging, but assumed that it could do Server Side Paging, otherwise what good is the QueryStringField attribute?

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

I am aware that you can do a custom paging solution using methodology like the 4GuysFromRolla did here http://www.4guysfromrolla.com/articles/031506-1.aspx, but I'd first like to know if a solution with the DataPager is possible before creating a custom solution.

我知道您可以使用http://www.4guysfromrolla所使用的方法(http://www.4guysfromrolla.com/articles/031506-1.aspx)进行自定义分页解决方案,但是我首先想知道在创建自定义解决方案之前是否可以使用DataPager解决方案。

UPDATE The more I look at this, the more that I'm coming to the conclusion that this is not possible and that, unfortunately, the datapager is a control meant for small web sites only. What I want to do should really be quite simple if the control were built correctly. I want to be able to say

更新得越多,我就越会得出这样的结论:这是不可能的,不幸的是,datapager只是一个用于小型web站点的控件。如果控件构建正确,那么我想做的应该非常简单。我想说

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

I've been looking for some hack to accomplish the same thing, but it appears that the datapager's TotalRowCount is computed from the actual number of items in the datasource that it's bound to. It seems very odd to me that Microsoft would create a ListViewPagedDataSource() class and a DataPager at the same time and not have them work correctly together, but this appears to have been what has happened.

我一直在寻找一些技巧来完成同样的事情,但是看起来datapager的TotalRowCount是根据它绑定到的数据源中的项目的实际数量来计算的。在我看来,微软会同时创建ListViewPagedDataSource()类和DataPager,但它们不能正确地一起工作,这似乎很奇怪,但事实似乎正是如此。

UPDATE 2 (AHA MOMENT?) It seems that it has been possible to do server side paging since .Net 2.0 by using an ObjectDataSource and customizing the SelectCountMethod(). I believe it should be possible to customize ObjectDataSource to suit my needs. Hmmm. I'm going away for the weekend, so it'll be a couple of days for me to see if this works. Stay tuned, true believers.

更新2(啊?)从。net 2.0开始,通过使用ObjectDataSource和自定义SelectCountMethod(),似乎就可以执行服务器端分页。我认为应该可以定制ObjectDataSource来满足我的需求。嗯。我要去度周末,所以我要花几天时间看看这个办法是否有效。请继续关注,真正的信徒。

3 个解决方案

#1


3  

It seems the only way to get server-side paging to work with the DataPager is to use a LinqDataSource in your markup (update: this is not true, see below), and set the DataSourceID of your ListView (as in ScottGu's sample - step 6), not via the ListView DataSource property. However I did discover that there is a trick to make this more workable so you can define your LinqDataSource query via code-behind rather than in the markup (note, the article says this will work with any DataSource control but I don't think it is the case).

使服务器端分页与DataPager一起工作的唯一方法似乎是在标记中使用LinqDataSource(更新:这不是真的,请参见下面),并设置ListView的DataSourceID(如ScottGu的示例—步骤6),而不是通过ListView DataSource属性。然而,我发现有一个技巧可以使它更加可行,这样您就可以通过代码隐藏而不是在标记中定义LinqDataSource查询(注意,本文说这将适用于任何DataSource控件,但我认为不是这样)。

This will probably not be any help for your situation as I noticed that your calling some sort of service that probably won't (and shouldn't) be returning an IQueryable result which is necessary for this to work. Here it is anyway in-case you're interested ...

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

aspx markup:

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" />

code-behind:

后台代码:

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

Note, MSDN states in regards to the QueryStringField attribute:

注意,MSDN就QueryStringField属性声明:

Setting this property is useful if you want to have all the pages of data indexed by a search engine. This occurs because the control produces a different URL for each page of data.

如果您想让搜索引擎索引所有的数据页,那么设置这个属性是很有用的。这是因为控件为每个数据页生成不同的URL。

Update: infact you can get this to working using an ObjectDataSource control instead of the LinqDataSource control - see this article and download the sample. While using an ObjectDataSource isn't nearly as simple as using a LinqDataSource control, it's uses less magic linq stuff (instead it uses heaps of magic strings that map to business/data layer methods) and allows the exposure of IEnumerable for your data access method instead of IQueryable.

更新:事实上,您可以使用ObjectDataSource控件而不是LinqDataSource控件来工作——请参阅本文并下载示例。虽然使用ObjectDataSource远没有使用LinqDataSource控件那么简单,但它使用的是更少的神奇的linq东西(相反,它使用大量映射到业务/数据层方法的神奇字符串),并允许您的数据访问方法使用IEnumerable,而不是IQueryable。

Still, I have never really a fan of embedding any sort of DataSource control in my UI markup (I believe this is necessary), so I would probably steer clear of the DataPager control except for small applications as you have suggested in your first update.

不过,我从来都不喜欢在我的UI标记中嵌入任何类型的数据源控件(我认为这是必要的),所以我可能会避开DataPager控件,除了您在第一次更新中建议的小型应用程序。

John, one other thing I noticed was that you are trying to marry standard Asp.Net server controls (that rely on ViewState) with the PagedList class which was developed as a helper class for Asp.Net Mvc applications. While this can potentially work, there may be simpler routes to take.

约翰,我注意到的另一件事是你想嫁给标准的Asp。Net server控件(依赖于ViewState)使用作为Asp的助手类开发的PagedList类。净Mvc应用程序。虽然这可能行得通,但可能有更简单的方法。

#2


10  

Exactly needed what you did - SQL server paging utilizing listview, datapager and linq data source. As you say TotalRowCount is readonly. There is also SetPageProperties method that can be used to set the total row count but that also didn't work for me.

这正是您所需要的——使用listview、datapager和linq数据源的SQL服务器分页。正如您所说的TotalRowCount是只读的。还有SetPageProperties方法,它可以用来设置总行数,但这对我来说并不适用。

However I managed to trick it this way. I have a dummy hidden listview with empty item template. The pager will be pointing at this dummy listview rather than the original listview. Then we need to bind the dummy listview to a dummy collection with the same number of items as in the original data source. This will ensure rendering the pager correctly. Here is the markup.

不过我还是用这种方法骗了它。我有一个包含空项目模板的隐藏列表视图。寻呼机将指向这个虚拟的listview而不是原始的listview。然后,我们需要将哑列表视图绑定到一个伪集合,该集合的项数与原始数据源中的项数相同。这将确保正确地呈现传呼机。这是标记。

<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>

and code behind

和代码后面

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

Tested it with 100k records. Works a treat.

用100k的记录测试它。一个对待工作。

#3


0  

John, I did a little proof of concept here so that you can see the code. I made this as basic as possible so there's no fluff. Please let me know if there is some attribute to this that you need that is missing and I will revise.

John,我在这里做了一个概念证明这样你就能看到代码了。我把它做得尽可能的简单,这样就不会有绒毛。请让我知道是否有你需要的属性缺失,我会修改。

Things that stood out:

事情引人注目:

  1. The data needs to be bound on the OnPreRender event of the pager when you're not using a DataSource object (xml,Sql,Access...). I am pretty sure this is your issue because this is where I had the most trouble.

    当您不使用数据源对象(xml、Sql、Access…)时,需要将数据绑定到寻呼机的OnPreRender事件。我很确定这是你的问题,因为这是我最麻烦的地方。

  2. Viewstate must be enabled on the ListView and the pager (it is by default)

    必须在ListView和寻呼机上启用Viewstate(默认为)

Code-Behind:

后台代码:

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 (left out all the other fluff):

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>

Enjoy, let me know if there is something else in this that you need.

好好享受,如果你还需要什么,请告诉我。

mike

迈克

#1


3  

It seems the only way to get server-side paging to work with the DataPager is to use a LinqDataSource in your markup (update: this is not true, see below), and set the DataSourceID of your ListView (as in ScottGu's sample - step 6), not via the ListView DataSource property. However I did discover that there is a trick to make this more workable so you can define your LinqDataSource query via code-behind rather than in the markup (note, the article says this will work with any DataSource control but I don't think it is the case).

使服务器端分页与DataPager一起工作的唯一方法似乎是在标记中使用LinqDataSource(更新:这不是真的,请参见下面),并设置ListView的DataSourceID(如ScottGu的示例—步骤6),而不是通过ListView DataSource属性。然而,我发现有一个技巧可以使它更加可行,这样您就可以通过代码隐藏而不是在标记中定义LinqDataSource查询(注意,本文说这将适用于任何DataSource控件,但我认为不是这样)。

This will probably not be any help for your situation as I noticed that your calling some sort of service that probably won't (and shouldn't) be returning an IQueryable result which is necessary for this to work. Here it is anyway in-case you're interested ...

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

aspx markup:

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" />

code-behind:

后台代码:

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

Note, MSDN states in regards to the QueryStringField attribute:

注意,MSDN就QueryStringField属性声明:

Setting this property is useful if you want to have all the pages of data indexed by a search engine. This occurs because the control produces a different URL for each page of data.

如果您想让搜索引擎索引所有的数据页,那么设置这个属性是很有用的。这是因为控件为每个数据页生成不同的URL。

Update: infact you can get this to working using an ObjectDataSource control instead of the LinqDataSource control - see this article and download the sample. While using an ObjectDataSource isn't nearly as simple as using a LinqDataSource control, it's uses less magic linq stuff (instead it uses heaps of magic strings that map to business/data layer methods) and allows the exposure of IEnumerable for your data access method instead of IQueryable.

更新:事实上,您可以使用ObjectDataSource控件而不是LinqDataSource控件来工作——请参阅本文并下载示例。虽然使用ObjectDataSource远没有使用LinqDataSource控件那么简单,但它使用的是更少的神奇的linq东西(相反,它使用大量映射到业务/数据层方法的神奇字符串),并允许您的数据访问方法使用IEnumerable,而不是IQueryable。

Still, I have never really a fan of embedding any sort of DataSource control in my UI markup (I believe this is necessary), so I would probably steer clear of the DataPager control except for small applications as you have suggested in your first update.

不过,我从来都不喜欢在我的UI标记中嵌入任何类型的数据源控件(我认为这是必要的),所以我可能会避开DataPager控件,除了您在第一次更新中建议的小型应用程序。

John, one other thing I noticed was that you are trying to marry standard Asp.Net server controls (that rely on ViewState) with the PagedList class which was developed as a helper class for Asp.Net Mvc applications. While this can potentially work, there may be simpler routes to take.

约翰,我注意到的另一件事是你想嫁给标准的Asp。Net server控件(依赖于ViewState)使用作为Asp的助手类开发的PagedList类。净Mvc应用程序。虽然这可能行得通,但可能有更简单的方法。

#2


10  

Exactly needed what you did - SQL server paging utilizing listview, datapager and linq data source. As you say TotalRowCount is readonly. There is also SetPageProperties method that can be used to set the total row count but that also didn't work for me.

这正是您所需要的——使用listview、datapager和linq数据源的SQL服务器分页。正如您所说的TotalRowCount是只读的。还有SetPageProperties方法,它可以用来设置总行数,但这对我来说并不适用。

However I managed to trick it this way. I have a dummy hidden listview with empty item template. The pager will be pointing at this dummy listview rather than the original listview. Then we need to bind the dummy listview to a dummy collection with the same number of items as in the original data source. This will ensure rendering the pager correctly. Here is the markup.

不过我还是用这种方法骗了它。我有一个包含空项目模板的隐藏列表视图。寻呼机将指向这个虚拟的listview而不是原始的listview。然后,我们需要将哑列表视图绑定到一个伪集合,该集合的项数与原始数据源中的项数相同。这将确保正确地呈现传呼机。这是标记。

<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>

and code behind

和代码后面

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

Tested it with 100k records. Works a treat.

用100k的记录测试它。一个对待工作。

#3


0  

John, I did a little proof of concept here so that you can see the code. I made this as basic as possible so there's no fluff. Please let me know if there is some attribute to this that you need that is missing and I will revise.

John,我在这里做了一个概念证明这样你就能看到代码了。我把它做得尽可能的简单,这样就不会有绒毛。请让我知道是否有你需要的属性缺失,我会修改。

Things that stood out:

事情引人注目:

  1. The data needs to be bound on the OnPreRender event of the pager when you're not using a DataSource object (xml,Sql,Access...). I am pretty sure this is your issue because this is where I had the most trouble.

    当您不使用数据源对象(xml、Sql、Access…)时,需要将数据绑定到寻呼机的OnPreRender事件。我很确定这是你的问题,因为这是我最麻烦的地方。

  2. Viewstate must be enabled on the ListView and the pager (it is by default)

    必须在ListView和寻呼机上启用Viewstate(默认为)

Code-Behind:

后台代码:

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 (left out all the other fluff):

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>

Enjoy, let me know if there is something else in this that you need.

好好享受,如果你还需要什么,请告诉我。

mike

迈克