如何将 DataPager 与服务器端分页结合使用?

发布于 2024-09-08 19:57:15 字数 2441 浏览 9 评论 0原文

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

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

PagedList背后的代码

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

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

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

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

我知道您可以使用 4GuysFromRolla 此处所做的方法来执行自定义分页解决方案 https://web.archive.org/web/20211020140032/https://www.4guysfromrolla.com/articles/031506-1.aspx,但我'在创建自定义解决方案之前,我首先想知道使用 DataPager 的解决方案是否可行。

更新 我对此了解得越多,就越得出这样的结论:这是不可能的,不幸的是,数据页程序是仅适用于小型网站的控件。如果控件构建正确的话,我想做的事情应该非常简单。我想说

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

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

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

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

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

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.

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?

I am aware that you can do a custom paging solution using methodology like the 4GuysFromRolla did here https://web.archive.org/web/20211020140032/https://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.

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

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.

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

雨的味道风的声音 2024-09-15 19:57:15

正是需要您所做的 - SQL Server 分页利用 listview、datapager 和 linq 数据源。正如您所说,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 记录对其进行了测试。工作是一种享受。

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.

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.

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

冷弦 2024-09-15 19:57:15

似乎让服务器端分页与 DataPager 一起使用的唯一方法是在标记中使用 LinqDataSource更新:这不是真的,请参见下文),并设置 ListView 的 DataSourceID (如 ScottGu 的示例 - 步骤 6),而不是通过 ListView DataSource 属性。然而我确实发现有一个 技巧 使此操作更加可行,以便您可以通过代码隐藏而不是在标记中定义 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 属性的说明:

如果您
想要拥有所有页面的数据
由搜索引擎索引。这
发生是因为控制产生
每页数据都有不同的 URL。

更新:事实上,您可以使用 ObjectDataSource 控件而不是 LinqDataSource 控件来使其工作 - 请参阅 本文并下载示例。虽然使用 ObjectDataSource 并不像使用 LinqDataSource 控件那么简单,但它使用的魔法 linq 内容较少(相反,它使用映射到业务/数据层方法的魔法字符串堆),并且允许为数据访问方法公开 IEnumerable而不是 IQueryable。

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

John,我注意到的另一件事是,您正在尝试将标准 Asp.Net 服务器控件(依赖于 ViewState)与 PagedList 类结合起来,该类是作为 Asp.Net Mvc 应用程序的帮助程序类开发的。虽然这可能有效,但可能还有更简单的路线可供选择。

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

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

aspx markup:

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

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.

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.

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.

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.

桃扇骨 2024-09-15 19:57:15

约翰,我在这里做了一些概念验证,以便您可以看到代码。我把它做得尽可能基本,这样就不会有任何废话。如果您需要的某些属性缺失,请告诉我,我将进行修改。

突出的事情:

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

  2. 必须在 ListView 和分页器上启用 Viewstate(默认情况下)

代码隐藏:

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(忽略所有其他fluff):

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

享受吧,如果您还需要其他东西,请告诉我。

麦克风

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.

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.

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

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

<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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文