Paging

Paging

Page selector for a set of Total items. It exposes PageNumber and Capacity (items per page), and the range currently displayed. Pages are elided with a separator when there are more than seven of them.

<nav:Paging Total="123" />
Property Type Default Purpose
Total int -1 Number of items in the whole set. -1 hides the range label and leaves the page count unbounded.
PageNumber int 1 Current page, 1-based. Clamped between 1 and the last page.
Capacity int 10 Items per page, picked from AvailableCapacities (5, 10, 25, 50, 100, 200 by default).
PageMax int (read-only) — Last page, derived from Total and Capacity.
IntervalMin / IntervalMax int (read-only) — Bounds of the range currently displayed, shown as 1-10 of 123.

Paged data grid

Paging only tells you which slice to display, it never touches the data itself. Bind Total to the number of items in the whole set, then bind PageNumber and Capacity two way so the view model is notified of every change and can load the matching slice.

<DataGrid ItemsSource="{Binding PageItems}" AutoGenerateColumns="False">
    <DataGrid.Columns>
        <DataGridTextColumn Header="Id" Binding="{Binding Id}" />
        <DataGridTextColumn Header="Name" Binding="{Binding Name}" />
    </DataGrid.Columns>
</DataGrid>
<nav:Paging
    Total="{Binding Total}"
    PageNumber="{Binding PageNumber, Mode=TwoWay}"
    Capacity="{Binding Capacity, Mode=TwoWay}" />
// The view model reacts to any paging change from its own setters.
public int PageNumber
{
    get => _pageNumber;
    set { if (SetProperty(ref _pageNumber, value)) UpdatePage(); }
}

public int Capacity
{
    get => _capacity;
    set { if (SetProperty(ref _capacity, value)) UpdatePage(); }
}

private void UpdatePage()
{
    // Query only the current page (Skip/Take, SQL OFFSET/FETCH, API page parameters, …)
    PageItems.Clear();
    foreach (var item in _source.Skip((PageNumber - 1) * Capacity).Take(Capacity))
        PageItems.Add(item);
}

Mode=TwoWay is required: neither PageNumber nor Capacity binds two way by default, so without it the control would never push the new page back to the view model.

Without a known total

Leave Total at its -1 default when the size of the set is unknown (a cursor-based API, a stream). The range label is hidden and the page list grows one page ahead of the current one, so the user can always move forward:

<nav:Paging PageNumber="{Binding PageNumber, Mode=TwoWay}" Capacity="{Binding Capacity, Mode=TwoWay}" />

Set Total as soon as you know it and the control switches back to a bounded list.

Reacting without bindings

For code-behind, the control raises PagingChange with the new page number and capacity every time either changes:

paging.PagingChange += (pageNumber, capacity) => UpdatePage(pageNumber, capacity);