Как использовать 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, похоже, использует свойство Count в ListView для определения общего количества записей, которое в данном случае равно 20. Почему-то нужно знать, что существует 1500, а не 20 записей. DataPager имеет свойство TotalRowCount, но оно доступно только для чтения.
Я никогда не видел пример DataPager с пейджингом на стороне сервера, но предполагал, что он мог бы выполнять пейджинг на стороне сервера, иначе что хорошего в атрибуте QueryStringField?
Я знаю, что вы можете создать собственное решение для пейджинга, используя методологию, подобную той, что 4GuysFromRolla сделал здесь http://www.4guysfromrolla.com/articles/031506-1.aspx, но сначала я хотел бы узнать, есть ли решение с DataPager возможно до создания индивидуального решения.
ОБНОВЛЕНИЕ Чем больше я смотрю на это, тем больше я прихожу к выводу, что это невозможно, и, к сожалению, DataPager - это элемент управления, предназначенный только для небольших веб-сайтов. То, что я хочу сделать, должно быть довольно простым, если элемент управления был построен правильно. Я хочу быть в состоянии сказать
dpFooBars.TotalRowCountComputed = false;
dpFooBars.TotalRowCount = AnyNumberThatISoChoose;
Я искал какой-нибудь хак для достижения той же цели, но похоже, что TotalRowCount хранилища данных вычисляется из фактического количества элементов в источнике данных, к которому он привязан. Мне кажется очень странным, что Microsoft создаст класс ListViewPagedDataSource() и DataPager одновременно и не даст им правильно работать вместе, но, похоже, именно это и произошло.
ОБНОВЛЕНИЕ 2 (AHA MOMENT?) Похоже, что было возможно делать подкачку на стороне сервера начиная с.Net 2.0, используя ObjectDataSource и настраивая SelectCountMethod(). Я считаю, что должна быть возможность настроить ObjectDataSource в соответствии с моими потребностями. Хммм. Я уезжаю на выходные, так что у меня будет пара дней, чтобы посмотреть, сработает ли это. Оставайтесь с нами, истинно верующие.
3 ответа
Кажется, единственный способ заставить пейджинг на стороне сервера работать с DataPager - это использовать LinqDataSource
в вашей разметке (обновление: это не так, см. ниже) и установите DataSourceID вашего ListView (как в примере 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.
Тем не менее, я никогда не любил встраивать какие-либо элементы управления DataSource в мою разметку пользовательского интерфейса (я считаю, что это необходимо), поэтому я бы, вероятно, держался подальше от элемента управления DataPager, за исключением небольших приложений, как вы предложили в своем первом обновлении.
Джон, еще одна вещь, которую я заметил, это то, что ты пытаешься объединить стандартные серверные элементы управления Asp.Net (которые полагаются на ViewState) с классом PagedList, который был разработан как вспомогательный класс для приложений Asp.Net Mvc. Хотя это потенциально может работать, могут быть более простые маршруты.
Точно нужно то, что вы сделали - пейджинг SQL-сервера с использованием списка, источника данных и источника данных 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 записей. Работает угощение.
Джон, я сделал небольшое подтверждение концепции, чтобы вы могли увидеть код. Я сделал это как можно более простым, чтобы не было пуха. Пожалуйста, дайте мне знать, если есть какой-то атрибут, который вам нужен, который отсутствует, и я пересмотрю.
Вещи, которые выделялись:
Данные должны быть связаны с событием OnPreRender пейджера, когда вы не используете объект DataSource (xml,Sql,Access...). Я почти уверен, что это ваша проблема, потому что именно здесь у меня больше всего проблем.
Viewstate должно быть включено на ListView и пейджере (по умолчанию)
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 (опущены все остальные пух):
<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>
Наслаждайтесь, дайте мне знать, если в этом есть что-то еще, что вам нужно.
Майк