ASP.NET MVC: Как сделать PagedList

Опубликовано 18.06.2011 01:02:08 в категории Микросервисы

При разработке одного из проектов (ASP.NET MVC) потребовалось реализовать постраничное отображение данных. Так как решений в интернете много, не могу не показать еще и своё решение в этой статье. А в дальнейшем планируется "завернуть" этот пример в пакет для NuGet, чтобы можно было в одно мгновение установить PagedList в проект MVC.

Постановка задачи

  1. Нужен интерфейс, который можно было бы использовать как Dependency Injection
  2. Нужно расширение для HtmlHelper, которое будет “рисовать” пейджер для переключения страниц
  3. Также потребуется расширение, чтобы использовать “страничность” по принципу fluid.

Интерфейс IPagedList

На самом деле интерфейс на столько прост, а названия его свойств на столько говорят о себе, что я не стану описывать данный код, просто приведу его целиком:

public interface IPagedList
{
     int TotalPages { get; }
     int TotalCount { get; }
     int PageIndex { get; }
     int PageSize { get; }
     bool IsPreviousPage { get; }
     bool IsNextPage { get; }
}

В силу небольшой сложности интерфейса, реализация его также проста. Я сделал два конструктора, чтобы можно было “подкидывать” в него разные типы списков.

public PagedList(IQueryable<T> source, int index, int pageSize)
{
   /* ... */
}

и второй:

public PagedList(List<T> source, int index, int pageSize)
{
   /* ... */
}

Теперь приступим к реализации расширения для HtmlHelper. Я хочу сделать так, чтобы можно было пейджер разместить на странице вот таким образом:

@Html.PagerForPagedList(Model.PageIndex, Model, "pager")

Сигнатуру конструктора опишу ниже.

Расширение HtmlHelper

Нехитрыми манипуляциями с TagBuilder и MvcHtmlString моё расширение выдает на страницу такую разметку:

<div class="pager">
    <ul>
        <li class="pager highlighted">
            <span>
                <a href="/Home/index/1">
                    <span>1</span>
                </a>
            </span>
        </li>
        <li>
            <span>
                <a href="/Home/index/2">
                    <span>2</span>
                </a>
            </span>
        </li>
    </ul>
</div>

Обращу Ваше внимание на то, что текущая (выбранная) страница подсвечивается (см. строку 3), а сам список обёрнут в div (см. строку 1). Приводить код CSS-файла пока не буду, потому что надеюсь, что Вы и сами сможете кучку <li> разместить горизонтально при помощи CSS. Прошу у Вас только одного совета. Дело в том, что хотелось бы от Вас услышать “нормальная” ли структура, подойдет ли она для большинства CSS, которыми пользуются люди в мире? Просто уже очень много CSS, которые так или иначе преобразуют тэги UL и LI в меню, списки и т.д. Вот и хотелось бы “подогнать” генерируемый код под общепринятые стандарты.

Расширение для PagedList

Приведу текст кода полностью, потому что он совсем даже небольшой:

public static class PagedListExtentions
{
    public static PagedList<T> ToPagedList<T>(this IQueryable<T> source, int index, int pageSize)
    {
        return new PagedList<T>(source, index, pageSize);
    }

    public static PagedList<T> ToPagedList<T>(this IQueryable<T> source, int index)
    {
        return new PagedList<T>(source, index, 10);
    }
}

Пример использования PagedList

Хочу чтобы на главной странице Home-контролера был список чего-нибудь. О! Я же уже делал один пакет NuGet с данными для демонстраций. Его-то я и буду использовать.

Установил пакет:

Install-Package SampleData

Сначала подключу namespace библиотеки примеров.

using SampleData;

Теперь код Home-контроллера:

public class HomeController : Controller
{
   public ActionResult Index(int? id)
   {
       int currentPage = id ?? 1;
       ViewBag.Message = "Welcome to ASP.NET MVC!";
       List<Person> list = SampleData.People.GetPeople();
       return View(list.AsQueryable().ToPagedList<Person>(currentPage - 1));
   }

   public ActionResult About()
   {
       return View();
   }
}

В строке 7 создаем список и наполняем его данными. А в следующей строке возвращаем данные уже разбитые на страницы подставляя номер выбранной страницы полученной как параметр в конструкторе метода (строка 3). Настройку маршрутов менять не пришлось, оставил как и было, потому что есть параметр, который может принимать значение null.

Дабы избежать многочисленных слов в описании представления (View) просто покажу весь текст разметки:

@model MvcApplication2.Engine.PagedList<SampleData.Person>
@{
    ViewBag.Title = "Index";
}
<h2>
    Index</h2>
<p>
    Total: @Model.TotalCount
    <br />
    Page size: @Model.PageSize</p>
<ul>
    @foreach (SampleData.Person pers in Model)
 {<li>
    @Html.DisplayFor(x => pers.Name)
    @Html.DisplayFor(x => pers.Weight)
    @Html.DisplayFor(x =>  pers.Gender)
    @Html.DisplayFor(x =>  pers.Age)</li>
 }
</ul>
<div>
    @Html.PagerForPagedList(Model.PageIndex, Model, "pager")
</div>

Отмечу следующие строки:

1-ая - определяем модель в представлении
8-ая – отображаем общее количество объектов
10-ая - отображаем размер страницы
с 12 по 28 – рисуем объект
21-ая – рисуем пейджер

Первым параметром в PagerForPagedList идет номер выбранной страницы, далее экземпляр PagedList и последний параметр это название CSS-стиля для пейджера:

.pager
{
   width: 100%;
   margin: 0px;
   display: inline-block;
   padding: 0px;
}
.pager ul
{
    list-style: none;
    margin: 0px;
    padding: 0px;
}
.pager ul li
{
    float: left;
    min-width:30px;
    text-align:center;
}
.pager ul li span
{
    display: block;
    margin: 0px 1px;
    padding: 4px;
    text-decoration:none;
    border: 1px solid #ccc;
}
.pager ul li a
{
    display: block;
    margin: 0px 1px;
    padding: 4px;
    text-decoration:none;
    border: 1px solid #ccc;
}
.highlighted
{
    background-color: #ccc;
    color: #000;
}

Эти стили обнговлены для версии 0.5.0

Вот такой вид имеет приложение при старте.

image

Заключение

Как по Вашему мнению, следует ли данный пример попытаться завернуть в NuGet-пакет, чтобы возможно было в одно мгновение подключит к проекту MVC3 пейджинг сущностей на страницах сайта? Если да, то какие будут замечания и пожелания?

P.S.: Да прибудет сила с Вами!

Сделал-таки NuGet-пакет

Обновление 19.06.2011 года:

Сделал пакет. Можно установить его написав команду в консоли управления пакетами:

PM> Install-Package PagedListExt

или другим способом.

После создания проекта MVC3 и установки пакета SampleData и PagedListExt (описанный в этой статье) достаточно добавить код в HomeController:

public class HomeController : Controller
{
    public ActionResult Index(int? id)
    {
        ViewBag.Message = "Welcome to ASP.NET MVC!";
        int index = id ?? 1;
        IQueryable<Person> query = SampleData.People.GetPeople().AsQueryable();
        return View(query.ToPagedList(index));
    }
        public ActionResult About()
    {
        return View();
    }
}

В _Layout.cshtml надо добавить строку с Css:

<link href="@Url.Content("~/Content/pager.css")" rel="stylesheet" type="text/css" />

А вот так выглядит Index.cshtml:

@model PagedList<SampleData.Person>
@{
   ViewBag.Title = "Home Page";
}
<h2>@ViewBag.Message</h2>
<p>
   @Html.DisplayFor(x => @Model)</p>
<p>
   @Html.PagerForPagedList(@Model.PageIndex, @Model, "pager")</p>

и всё заработает. Отмечу только внешний вид отображаемых данных при этой разметки оставляет желать лучшего. Просто для простоты не стал “разукрашивать” представление класса Person.

Пробуйте, пишите, критикуйте, советуйте…

Обновление 11.07.2011 года:

В обновленной версии 0.3.2 расширены меторы дополнительным параметром routeValues и появились новые методы использующие IEnumerable<T> для построения PagedList.

Обновление 0.3.4. от 07.10.2011:

В новой версии добавлено:

prev_next_pager

Обновление 0.4.3 от 18.01.2012:

В PrevNextForPagedList добавлен параметр RouteValues.

Обновление 0.5.0 от 22.03.2012:

В новой версии многое изменилось:

- теперь можно использовать стили от jQueryUI если не задавать параметр pagerCss.
- файл pager.css удален из проекта, вы можете теперь создавать свои стили. Пример смотрите выше.
- добавлена возможность задавать количество сегментов отображаемых страниц в режиме PagerForPagedList
- исправлена ошибка при использовании режима PrevNextForPagedList: не скрывалась кнопка "следующая" при достижении конца коллекции.

Обновление 0.7.0 от 12.10.2013:

Пакет адаптирован для использования совместно с Bootstrap 3.0. Для того чтобы использовать возможность разметки bootstrap, установите свойство pagerCss равным “pagination”.

Обновление 0.7.5 от 11.02.2014:

Добавил несколько перегрузок методов.

Обновление 0.8.0 от 07.11.2014:

Добавил специальный метод непосредственно для Bootstrap. Теперь можно задавать шаблон URL для генерации пейджера. Например:

string department = ViewBag.DepartmentUrlName;
string catalog = ViewBag.CatalogUrlName;
tring subcatalog = ViewBag.SubcatalogUrlName;
var url = string.Format("/items/{0}/{1}/{2}/{3}", department, catalog, subcatalog, "{PageIndex}");
@Html.PagerForPagedListBootstrap(Model, url)

В строке 5 новый метод MvcHelper’а. который принимает как параметр IPagedList (в примере это переменная “Model”) и второй TemplateUrl для построения pagination через bootstrap.

Ссылки

ASP.NET MVC3: Как сделать PagedList на AJAX
PagedListLite.MVC

Комментарии к статье ()

Загрузка...

Что-то пошло не по сценарию и завершилось ошибкой. Перезагрузить страницу (F5) 🗙

Переподключаем сервер...

Переподключение сломалось... Пробуем еще раз сек.

Не получилось переподключится.
Пожалуйста, обновите страницу F5

Сессия была поставлена сервером на паузу.

Восстановить сессию не получилось.
Пожалуйста, обновите страницу F5.