Я за свою сознательную жизнь написал с десяток классов для организации постраничего вывода на разных языках и под разные технологии. С приходом LINQ, в C# теперь можно сделать его универсальным. Я не претендую на абсолютную правильность изложенного, тем не менее, я гарантирую, что предложенные методы работают.
WCF и Paging
Допустим у нас есть страница, к базе данных которая подключена через WCF. IQueryable не сериализуется (ну и не надо, зачем нам писать логику построения запроса на уровне презентации), это значит нам надо передавать информацию о текущей странице через параметры. Значит нам нужен следующий класс:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
namespace MyService.Data
{
///
/// Used in PagingListPair by composition.
/// Or created directly from client side.
///
[DataContract]
[Serializable]
public class PagingInformation
{
///
///
/// page index to show
/// page size to show (amount of entries per page index)
///
static public PagingInformation CreateClientPaging(int index, int size)
{
var pageInfo = new PagingInformation();
pageInfo.PageIndex = index;
pageInfo.RecordsPerPage = size;
return pageInfo;
}
///
///
[DataMember]
public long? Count { get; set; }
///
///
[DataMember]
public int? RecordsPerPage { get; set; }
///
///
[DataMember]
public int? PageIndex { get; set; }
///
///
public int PageCount
{
get
{
if (!Count.HasValue || !RecordsPerPage.HasValue)
{
throw new InvalidOperationException("Invalid initialization of count or recordsAmount value");
}
decimal numOfPages = (decimal)Count.Value / (decimal)RecordsPerPage.Value;
numOfPages = Math.Ceiling(numOfPages);
return (int)numOfPages;
//return (int)Math.Round((decimal)(tmpCount / tmpRecPerPage), MidpointRounding.AwayFromZero);
//return (int)Math.Ceiling((decimal)(Count.Value / RecordsPerPage.Value));
}
}
}
}
Теперь на стороне клиента можно использовать CreateClientPaging, для того чтобы получить информацию о страницах и сами данные нам нужен композитный класс, который будет передаватся в качестве ответа:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.Serialization;
namespace MyService.Data
{
///
/// Holds paging information performed to query.
/// Used by
///
///
[DataContract]
[Serializable]
public class PagingListPair
{
///
///
[DataMember]
public PagingInformation PagingInformation { get; set; }
[DataMember]
public List
}
}
Вот и все, добавляем к нашему контракту аттрибуты:
[ServiceKnownType(typeof(PagingInformation))]
[ServiceKnownType(typeof(MyEntity))]
[ServiceKnownType(typeof(PagingListPair
А в методах сервиса используем методы следующего формата:
PagingListPair
Для LINQ пейджинга можно использовать метод помошник:
public static PagingListPair
{
if (!pageInfo.PageIndex.HasValue || !pageInfo.RecordsPerPage.HasValue || !(pageInfo.RecordsPerPage.Value > 0))
{
throw new InvalidOperationException("Invalid pageInforation values");
}
pageInfo.Count = entities.Count();
var output = new PagingListPair
output.PagingInformation = pageInfo;
output.EntityList = entities.Skip(pageInfo.PageIndex.Value * pageInfo.RecordsPerPage.Value).Take(pageInfo.RecordsPerPage.Value).ToList();
return output;
}
ASP.NET стандартный пейджинг
В ASP.NET есть интерфейс IPageableItemContainer, он используется в DataBinging
Его реализация выглядит примерно так:
int _startIndex, _maxRows;
public int MaximumRows
{
get { return _maxRows; }
}
public int StartRowIndex
{
get { return _startIndex; }
}
public event EventHandler
TotalRowCountAvailable;
public void SetPageProperties(int startRowIndex, int maximumRows, bool databind)
{
_startIndex = startRowIndex;
_maxRows = maximumRows;
}
Onload***
{
if (TotalRowCountAvailable != null)
{
TotalRowCountAvailable(this, new PageEventArgs(_startIndex, _maxRows, _dataSource.Count));
_dataSource = _dataSource.Skip(_startIndex).Take(_maxRows).ToList();
}
}
Еще один метод постраничного вывода в качестве ASP.NET контрола.
Это PagingControl который можно использовать на страницах с простой логикой, класс можно переопределить для локализации и переопределения стилей, изменение текущей страницы можно получить после срабатывания события:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace MyControls
{
public enum SpecialPagingButtons
{
Prev, Next, First, Last
}
public class ResultListPager : Control, INamingContainer
{
public static int PageCountFromItems(int itemCount, int itemsPerPage) {
return (int)Math.Ceiling((0.0+itemCount)/itemsPerPage);
}
public event EventHandler PageIndexChanged;
private void OnPageIndexChanged()
{
if (PageIndexChanged != null)
{
PageIndexChanged(this, EventArgs.Empty);
}
}
public int CurrentPageIndex
{
get
{
if (ViewState["CurrentPageIndex"] == null)
ViewState["CurrentPageIndex"] = 0;
return (int)ViewState["CurrentPageIndex"];
}
set
{
if (CurrentPageIndex != value)
{
if ((value < 0) && (PageCount > 1))
{
throw new Exception("CurrentPageIndex must be greater than -1 if PagesCount is greater than 0");
}
if ((value > (PageCount - 1)) || (value < 0))
{
throw new Exception("CurrentPageIndex is out of range");
}
ViewState["CurrentPageIndex"] = value;
}
}
}
public int PageCount
{
get
{
if (ViewState["PageCount"] == null)
ViewState["PageCount"] = 0;
return (int)ViewState["PageCount"];
}
set
{
if (PageCount != value)
{
if (value < 0)
{
throw new Exception("PageCount can not be smaller than zero.");
}
ViewState["PageCount"] = value;
}
}
}
int pageButtonCount = 3;
public int PageButtonCount
{
get
{
return pageButtonCount;
}
set
{
if (value < 3)
{
throw new Exception("PageButtonCount can not be smaller than 3");
}
pageButtonCount = value;
}
}
bool showPrevNext = true;
public bool ShowPrevNext
{
get { return showPrevNext; }
set { showPrevNext = value; }
}
bool showFirstLast = true;
public bool ShowFirstLast
{
get { return showFirstLast; }
set { showFirstLast = value; }
}
protected virtual string GetItemStyle(int index)
{
return (index == CurrentPageIndex)?"item-selected":"item";
}
protected virtual LinkButton CreateItem(string id, int idd)
{
return new LinkButton()
{
Text = (idd+1).ToString(),
CssClass = GetItemStyle(idd)
};
}
protected virtual LinkButton CreateSpacialItem(string id, SpecialPagingButtons btn)
{
var ret = new LinkButton();
switch (btn)
{
case SpecialPagingButtons.Prev:
ret.Text = "<";
ret.CssClass = GetItemStyle(CurrentPageIndex - 1);
break;
case SpecialPagingButtons.Next:
ret.Text = ">";
ret.CssClass = GetItemStyle(CurrentPageIndex + 1);
break;
case SpecialPagingButtons.First:
ret.Text = "«";
ret.CssClass = GetItemStyle(0);
break;
case SpecialPagingButtons.Last:
ret.Text = "»";
ret.CssClass = GetItemStyle(PageCount-1);
break;
default:
break;
}
return ret;
}
private void AddSpecialItem(SpecialPagingButtons type) {
var id = "pg_" + type.ToString();
var linkButton = CreateSpacialItem(id, type);
linkButton.ID = id;
linkButton.Click += new EventHandler(specLinkButton_Click);
this.Controls.Add(linkButton);
}
protected override void CreateChildControls()
{
this.Controls.Clear();
this.ClearChildViewState();
var range = CalculateRange();
if (range.Length <= 1) return;
if (CurrentPageIndex > 0 && ShowFirstLast) AddSpecialItem(SpecialPagingButtons.First);
if (CurrentPageIndex > 0 && ShowPrevNext) AddSpecialItem(SpecialPagingButtons.Prev);
foreach (int i in range)
{
var id = "pg_" + i.ToString();
var linkButton = CreateItem(id, i);
linkButton.ID = id;
linkButton.Click += new EventHandler(linkButton_Click);
this.Controls.Add(linkButton);
}
if (CurrentPageIndex
{
writer.Write("
base.Render(writer);
writer.Write("
");
}
}
void specLinkButton_Click(object sender, EventArgs e)
{
var btn = (SpecialPagingButtons)Enum.Parse(typeof(SpecialPagingButtons), (sender as LinkButton).ID.Substring(3));
switch (btn)
{
case SpecialPagingButtons.Prev:
if (CurrentPageIndex > 0) CurrentPageIndex--;
break;
case SpecialPagingButtons.Next:
if (CurrentPageIndex < PageCount-1) CurrentPageIndex++;
break;
case SpecialPagingButtons.First:
CurrentPageIndex = 0;
break;
case SpecialPagingButtons.Last:
CurrentPageIndex = PageCount - 1;
break;
default:
break;
}
CreateChildControls();
OnPageIndexChanged();
}
void linkButton_Click(object sender, EventArgs e)
{
CurrentPageIndex = Int32.Parse((sender as LinkButton).ID.Substring(3));
CreateChildControls();
OnPageIndexChanged();
}
public int[] CalculateRange()
{
int end, start;
if (PageCount <= PageButtonCount)
{
start = 0;
end = PageCount - 1;
}
else
{
int pagesLeft = (PageButtonCount - 1) / 2;
int pagesRight = (PageButtonCount - pagesLeft);
if (CurrentPageIndex + pagesRight > (PageCount - 1))
{
pagesRight = (PageCount - 1) - CurrentPageIndex;
pagesLeft = (PageButtonCount - pagesRight) - 1;
}
else
{
if (CurrentPageIndex - pagesLeft < 0)
{
pagesLeft = CurrentPageIndex;
pagesRight = (PageButtonCount - pagesLeft) - 1;
}
}
if (pagesLeft + pagesRight == PageButtonCount) pagesRight--;
start = CurrentPageIndex - pagesLeft;
end = CurrentPageIndex + pagesRight;
}
int[] result = new int[(end - start) + 1];
int a = 0;
for (int i = start; i < end + 1; i++)
{
result[a] = i;
a++;
}
return result;
}
}
}
Paging в ASP.NET MVC
Когда я познакомился с MVC, больше мне не хотелось изобретать велосипед, я нашел библиотеку PagedList.
Реализация позожа на мой WCF Paging, пример кода можно посмотреть на основной странице проекта.
Метки:ASP.NET, OpenSource, база данных
Похожие статьи
- 19 сентября 2010 -- StringTemplate на C# (Часть 2) (0)
- 30 декабря 2008 -- Создаем ASHX хендлер в ASP.NET (1)
- 18 декабря 2008 -- Транслитерация RUS 2 LAT на C# (22)
- 22 августа 2008 -- Собственная страница для обработки ошибок на ASP.NET (0)
- 19 сентября 2010 -- StringTemplate на C# (Часть 3) (0)
9 декабря, 2009 at 22:32
Как толково написано, я ввоссторге от статьи.
15 февраля, 2010 at 22:53
Полезная статья, думаю что обязательно пригодится.
27 ноября, 2012 at 19:20
а нормально оформить было явно лень..