Paginated UI

Paginated UI support in Open Waves is based on two components: PaginationViewModel class and Pager control.

PaginationViewModel

PaginationViewModel provides various information about a page set using PageViewModel class to represent each of the pages. PageViewModel contains information about a page and gives access to page items.
PaginationViewModel.Create(...) ensures that all PageViewModels in PaginationViewModel can be compared for equality using == operator.

using OpenWaves.Collections;
using OpenWaves.Web.Pagination

..

var viewModel = PaginationViewModel.Create(pages, currentPageIndex);

var currentPage = viewModel.CurrentPage;   // 
var firstPage = viewModel.FirstPage;              // Can be null if pages is empty
var lastPage = viewModel.LastPage;               //

var pageNumber = currentPage.Number;

var showPrevPage = currentPage.IsFirst == false;  // Same as currentPage == viewModel.FirstPage
var showNextPage = currentPage.IsLast == false; // Same as currentPage == viewModel.LastPage;

var info = String.Format("Showing items {0} - {1}", currentPage.FirstItemNumber, currentPage.LastItemNumber);

var availableVisiblePages = viewModel.VisiblePages.Where(p=>p.IsCurrent == false);

var itemsToDisplay = currentPage.Items;   // Lazy, can be accessed multiple times without incurring additional enumerations of the input list 

Pager control

Pager control makes it easy to create various styles of pagination buttons. It is designed to work with PaginationViewModel class.
<ow:Pager runat="server">
    <Template>
        <ow:PageButton runat="server" Page="<%# Model.PreviousPage%>" Text="Previous" />

        <ow:PageRepeater runat="server" DataSource="<%# Model.VisiblePages%>">
            <ItemTemplate>
                <ow:PageButton runat="server" Page="<%#Container.Item%>" />
            </ItemTemplate>
        </ow:PageRepeater>

        <ow:PageButton runat="server" Page="<%# Model.NextPage%>" Text="Next" />
    </Template>

    <PageButtonTemplate>
        <!-- 
            This button template is used for regular pages and as a fall back template if other templates are not provided.
            Use <%# Container.Text %> etc, to access properties of the button.
            Use <%# Container.Page.Number %> etc, to access properties of a PageViewModel bound to the button.
        -->
    </PageButtonTemplate>

    <UnavailablePageButtonTemplate>
        <!-- 
             This button template is used for unavailable pages (buttons with Page property bound to null), for example PreviousPage when CurrentPage.IsFirst.
            <%# Container.Text %> etc, still can be used to access properties of the button.             
             If this template is not provided unavailable pages are not rendered.
         -->
    </UnavailablePageButtonTemplate>

    <SelectedPageButtonTemplate>
        <!-- 
             This button template is used for currently selected page.
             If this template is not provided PageButtonTemplate is used (can use Container.Page.IsCurrent to alter the markup for selected page).
         -->
    </SelectedPageButtonTemplate>
</ow:Pager> 

Rendering current page items

After PaginationViewModel is created, any control can be used to render the items of the current page. Example below uses regular repeater. Watch for CurrentPage == null when the input collection is empty.
...
<asp:Repeater runat="server" DataSource="<%# Model.CurrentPage.Items %>">
    <ItemTemplate>
        <li><%# Container.DataItem %></li>
    </ItemTemplate>
</asp:Repeater>
...

Pagination flavors

Pagination support in Open Waves comes in two flavors.
The most often used is based on GET requests with current page number passed in query string. This is the preferred implementation of listings, search results, etc as it supports browser Back button better and is easier to cache (output cache, proxy, etc).
In specific scenarios where pagination based on POST requests is needed (AJAX/UpdatePanel), current page index needs to be stored in some sort of cross-request persistent store (ViewState, HiddenInput, etc).

GET based pagination

In most cases, in GET based scenario you will want to disable view state for the pager control as well as the list of items rendered. This is an example of a code-behind that can be used to create paged UI:

    public partial class GetBasedPagination : System.Web.UI.Page
    {
        private readonly IEnumerable<int> items = Enumerable.Range(0, 100);

        protected PaginationViewModel<int> Numbers;
        protected PaginationController PaginationController;

        protected override void OnLoad(EventArgs e)
        {
            this.PaginationController = new PaginationController(this.Request);

            // this.Numbers is the view model you will bind to in the markup
            this.Numbers = this.PaginationController.Paginate(this.items);
            
            base.OnDataBinding(e);
        }
    }

POST based pagination

    public partial class PostBasedPaginationTests : System.Web.UI.Page
    {
        private readonly IEnumerable<int> items = Enumerable.Range(0, 100);

        protected PaginationViewModel<int> Numbers;

        protected int CurrentPageIndex
        {
            get { return ((int?) this.ViewState["CurrentPageIndex"]) ?? 0; }
            set { this.ViewState["CurrentPageIndex"] = value; }
        }

        protected override void OnLoad(EventArgs e)
        {
            //
            // Depending on your preference you can either leave view state enabled for 
            // both pager and items controls and only DataBind when IsPostBack == false
            //
            // Or, as in this example, disable the view state and data bind on all requests 
            // (be careful not to disable view state for the whole page - this is where CurrentPageIndex is stroed)
            //
            this.DataBind();
        }

        protected override void OnDataBinding(EventArgs e)
        {
            // this.Numbers is the view model you will bind to in the markup
            this.Numbers = this.items.Paginate(10, this.CurrentPageIndex);

            base.OnDataBinding(e);
        }

        protected void OnPageCommand(object sender, PaginationCommandEventArgs e)
        {
            if (e.CommandName == "ChangePage")
            {
                this.CurrentPageIndex = Convert.ToInt32(e.CommandArgument);
                this.DataBind();
            }
        }
    }

Last edited Aug 23, 2011 at 1:36 PM by mgrzyb, version 5

Comments

No comments yet.