Files
radzen-blazor/Radzen.Blazor/PagedDataBoundComponent.cs

586 lines
17 KiB
C#

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Components;
using Radzen.Blazor;
namespace Radzen
{
/// <summary>
/// Base classes of components that support paging.
/// </summary>
/// <typeparam name="T">The type of the data item</typeparam>
public class PagedDataBoundComponent<T> : RadzenComponent
{
/// <summary>
/// Gets or sets the pager position. Set to <c>PagerPosition.Bottom</c> by default.
/// </summary>
/// <value>The pager position.</value>
[Parameter]
public PagerPosition PagerPosition { get; set; } = PagerPosition.Bottom;
/// <summary>
/// Gets or sets a value indicating whether pager is visible even when not enough data for paging.
/// </summary>
/// <value><c>true</c> if pager is visible even when not enough data for paging otherwise, <c>false</c>.</value>
[Parameter]
public bool PagerAlwaysVisible { get; set; }
/// <summary>
/// Gets or sets the horizontal align.
/// </summary>
/// <value>The horizontal align.</value>
[Parameter]
public HorizontalAlign PagerHorizontalAlign { get; set; } = HorizontalAlign.Justify;
/// <summary>
/// Gets or sets a value indicating pager density.
/// </summary>
[Parameter]
public Density Density { get; set; } = Density.Default;
/// <summary>
/// Gets or sets a value indicating whether paging is allowed. Set to <c>false</c> by default.
/// </summary>
/// <value><c>true</c> if paging is allowed; otherwise, <c>false</c>.</value>
[Parameter]
public bool AllowPaging { get; set; }
int pageSizeField = 10;
/// <summary>
/// Gets or sets the size of the page.
/// </summary>
/// <value>The size of the page.</value>
[Parameter]
public int PageSize
{
get
{
return pageSize ?? pageSizeField;
}
set
{
if (pageSizeField != value)
{
pageSizeField = value;
InvokeAsync(() => OnPageSizeChanged(value));
}
}
}
internal int GetPageSize()
{
return pageSizeField;
}
internal void SetPageSize(int value)
{
pageSizeField = value;
}
/// <summary>
/// Gets or sets the page numbers count.
/// </summary>
/// <value>The page numbers count.</value>
[Parameter]
public int PageNumbersCount { get; set; } = 5;
/// <summary>
/// Gets or sets the count.
/// </summary>
/// <value>The count.</value>
[Parameter]
public int Count { get; set; }
/// <summary>
/// Gets or sets the current page.
/// </summary>
/// <value>The current page.</value>
public int CurrentPage { get; set; }
/// <summary>
/// Gets or sets the template.
/// </summary>
/// <value>The template.</value>
[Parameter]
public RenderFragment<T>? Template { get; set; }
/// <summary>
/// Gets or sets the loading template.
/// </summary>
/// <value>The loading template.</value>
[Parameter]
public RenderFragment? LoadingTemplate { get; set; }
/// <summary>
/// The data
/// </summary>
IEnumerable<T>? _data;
/// <summary>
/// Gets or sets the data.
/// </summary>
/// <value>The data.</value>
[Parameter]
public IEnumerable<T>? Data
{
get
{
return _data;
}
set
{
if (_data != value)
{
_data = value;
if (_data != null && _data is INotifyCollectionChanged)
{
((INotifyCollectionChanged)_data).CollectionChanged += OnCollectionChanged;
}
OnDataChanged();
StateHasChanged();
}
}
}
/// <summary>
/// Called when INotifyCollectionChanged CollectionChanged is raised.
/// </summary>
protected virtual void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs args)
{
}
/// <inheritdoc />
public override void Dispose()
{
base.Dispose();
if (_data != null && _data is INotifyCollectionChanged)
{
((INotifyCollectionChanged)_data).CollectionChanged -= OnCollectionChanged;
}
topPager?.Dispose();
bottomPager?.Dispose();
GC.SuppressFinalize(this);
}
/// <summary>
/// Gets or sets the page size options.
/// </summary>
/// <value>The page size options.</value>
[Parameter]
public IEnumerable<int>? PageSizeOptions { get; set; }
/// <summary>
/// Gets or sets the page size description text.
/// </summary>
/// <value>The page size description text.</value>
[Parameter]
public string? PageSizeText { get; set; } = "items per page";
/// <summary>
/// Gets or sets the pager summary visibility.
/// </summary>
/// <value>The pager summary visibility.</value>
[Parameter]
public bool ShowPagingSummary { get; set; } = false;
/// <summary>
/// Gets or sets the pager summary format. <see cref="PagingSummaryTemplate" /> has preference over this property.
/// </summary>
/// <value>The pager summary format.</value>
[Parameter]
public string? PagingSummaryFormat { get; set; } = "Page {0} of {1} ({2} items)";
#nullable enable
/// <summary>
/// Gets or sets the pager summary template. Has preference over <see cref="PagingSummaryFormat" />.
/// </summary>
[Parameter]
public RenderFragment<PagingInformation>? PagingSummaryTemplate { get; set; }
#nullable restore
/// <summary>
/// Gets or sets the pager's first page button's title attribute.
/// </summary>
[Parameter]
public string? FirstPageTitle { get; set; } = "First page.";
/// <summary>
/// Gets or sets the pager's first page button's aria-label attribute.
/// </summary>
[Parameter]
public string? FirstPageAriaLabel { get; set; } = "Go to first page.";
/// <summary>
/// Gets or sets the pager's optional previous page button's label text.
/// </summary>
[Parameter]
public string? PrevPageLabel { get; set; }
/// <summary>
/// Gets or sets the pager's previous page button's title attribute.
/// </summary>
[Parameter]
public string? PrevPageTitle { get; set; } = "Previous page";
/// <summary>
/// Gets or sets the pager's previous page button's aria-label attribute.
/// </summary>
[Parameter]
public string? PrevPageAriaLabel { get; set; } = "Go to previous page.";
/// <summary>
/// Gets or sets the pager's last page button's title attribute.
/// </summary>
[Parameter]
public string? LastPageTitle { get; set; } = "Last page";
/// <summary>
/// Gets or sets the pager's last page button's aria-label attribute.
/// </summary>
[Parameter]
public string? LastPageAriaLabel { get; set; } = "Go to last page.";
/// <summary>
/// Gets or sets the pager's optional next page button's label text.
/// </summary>
[Parameter]
public string? NextPageLabel { get; set; }
/// <summary>
/// Gets or sets the pager's next page button's title attribute.
/// </summary>
[Parameter]
public string? NextPageTitle { get; set; } = "Next page";
/// <summary>
/// Gets or sets the pager's next page button's aria-label attribute.
/// </summary>
[Parameter]
public string? NextPageAriaLabel { get; set; } = "Go to next page.";
/// <summary>
/// Gets or sets the pager's numeric page number buttons' title attributes.
/// </summary>
[Parameter]
public string? PageTitleFormat { get; set; } = "Page {0}";
/// <summary>
/// Gets or sets the pager's numeric page number buttons' aria-label attributes.
/// </summary>
[Parameter]
public string? PageAriaLabelFormat { get; set; } = "Go to page {0}.";
internal IQueryable<T>? _view;
/// <summary>
/// Gets the paged view.
/// </summary>
/// <value>The paged view.</value>
public virtual IQueryable<T> PagedView
{
get
{
if (_view == null)
{
_view = (AllowPaging && !LoadData.HasDelegate ? View.Skip(skip).Take(PageSize) : View).ToList().AsQueryable();
}
return _view;
}
}
/// <summary>
/// Gets the view.
/// </summary>
/// <value>The view.</value>
public virtual IQueryable<T> View
{
get
{
return Data != null ? Data.AsQueryable() : Enumerable.Empty<T>().AsQueryable();
}
}
/// <summary>
/// Gets or sets the load data.
/// </summary>
/// <value>The load data.</value>
[Parameter]
public EventCallback<Radzen.LoadDataArgs> LoadData { get; set; }
/// <summary>
/// Reloads this instance.
/// </summary>
public async virtual Task Reload()
{
_view = null;
if (Data != null && !LoadData.HasDelegate)
{
Count = View.Count();
}
await LoadData.InvokeAsync(new Radzen.LoadDataArgs() { Skip = skip, Top = PageSize });
CalculatePager();
if (!LoadData.HasDelegate)
{
StateHasChanged();
}
}
/// <summary>
/// Called when [data changed].
/// </summary>
protected virtual void OnDataChanged()
{
}
/// <summary>
/// Set parameters as an asynchronous operation.
/// </summary>
/// <param name="parameters">The parameters.</param>
/// <returns>A Task representing the asynchronous operation.</returns>
public override async Task SetParametersAsync(ParameterView parameters)
{
bool pageSizeChanged = parameters.DidParameterChange(nameof(PageSize), PageSize) &&
PageSize != pageSize;
bool visibleChanged = parameters.DidParameterChange(nameof(Visible), Visible);
bool wasVisible = Visible;
await base.SetParametersAsync(parameters);
if (pageSizeChanged && !firstRender)
{
await InvokeAsync(Reload);
}
// Reset to first page when becoming visible again
if (visibleChanged && !firstRender && Visible && !wasVisible)
{
skip = 0;
CurrentPage = 0;
_view = null;
}
}
/// <summary>
/// Called when [parameters set asynchronous].
/// </summary>
/// <returns>Task.</returns>
protected override async Task OnParametersSetAsync()
{
if (Visible && !LoadData.HasDelegate)
{
await InvokeAsync(Reload);
}
else
{
CalculatePager();
}
await base.OnParametersSetAsync();
}
/// <summary>
/// The first render
/// </summary>
bool firstRender = true;
/// <summary>
/// Called when [after render asynchronous].
/// </summary>
/// <param name="firstRender">if set to <c>true</c> [first render].</param>
/// <returns>Task.</returns>
protected override async Task OnAfterRenderAsync(bool firstRender)
{
this.firstRender = firstRender;
if (firstRender)
{
await ReloadOnFirstRender();
}
await base.OnAfterRenderAsync(firstRender);
}
internal virtual async Task ReloadOnFirstRender()
{
if (firstRender && Visible && (LoadData.HasDelegate && Data == null))
{
await InvokeAsync(Reload);
StateHasChanged();
}
}
/// <summary>
/// The skip
/// </summary>
protected int skip;
/// <summary>
/// The top pager
/// </summary>
protected RadzenPager topPager = default!;
/// <summary>
/// The bottom pager
/// </summary>
protected RadzenPager bottomPager = default!;
/// <summary>
/// Gets or sets the page callback.
/// </summary>
/// <value>The page callback.</value>
[Parameter]
public EventCallback<PagerEventArgs> Page { get; set; }
/// <summary>
/// Handles the page changed event.
/// </summary>
/// <param name="args">The <see cref="PagerEventArgs"/> instance containing the event data.</param>
protected async Task OnPageChanged(PagerEventArgs args)
{
ArgumentNullException.ThrowIfNull(args);
skip = args.Skip;
CurrentPage = args.PageIndex;
await Page.InvokeAsync(args);
await InvokeAsync(Reload);
}
internal int? pageSize;
/// <summary>
/// Called when [page size changed].
/// </summary>
/// <param name="value">The value.</param>
protected virtual async Task OnPageSizeChanged(int value)
{
if (pageSize != value && !this.firstRender)
{
pageSize = value;
await InvokeAsync(Reload);
}
}
/// <summary>
/// Calculates the pager.
/// </summary>
protected void CalculatePager()
{
if (topPager != null)
{
topPager.SetCount(Count);
topPager.SetCurrentPage(CurrentPage);
topPager.ChangeState();
}
if (bottomPager != null)
{
bottomPager.SetCount(Count);
bottomPager.SetCurrentPage(CurrentPage);
bottomPager.ChangeState();
}
}
/// <summary>
/// Goes to page.
/// </summary>
/// <param name="page">The page.</param>
/// <param name="forceReload">if set to <c>true</c> [force reload].</param>
public async Task GoToPage(int page, bool forceReload = false)
{
if (topPager != null)
{
await topPager.GoToPage(page, forceReload);
}
if (bottomPager != null)
{
await bottomPager.GoToPage(page, forceReload);
}
}
/// <summary>
/// Firsts the page.
/// </summary>
/// <param name="forceReload">if set to <c>true</c> [force reload].</param>
public async Task FirstPage(bool forceReload = false)
{
var shouldReload = forceReload && CurrentPage == 0;
if (topPager != null)
{
await topPager.FirstPage();
}
if (bottomPager != null)
{
await bottomPager.FirstPage();
}
if (shouldReload)
{
await InvokeAsync(Reload);
}
}
/// <summary>
/// Previouses the page.
/// </summary>
public async Task PrevPage()
{
if (topPager != null)
{
await topPager.PrevPage();
}
if (bottomPager != null)
{
await bottomPager.PrevPage();
}
}
/// <summary>
/// Nexts the page.
/// </summary>
public async Task NextPage()
{
if (topPager != null)
{
await topPager.NextPage();
}
if (bottomPager != null)
{
await bottomPager.NextPage();
}
}
/// <summary>
/// Lasts the page.
/// </summary>
public async Task LastPage()
{
if (topPager != null)
{
await topPager.LastPage();
}
if (bottomPager != null)
{
await bottomPager.LastPage();
}
}
}
}