Merge pull request #155 from irihitech/page

Update Pagination
This commit is contained in:
Dong Bin
2024-03-16 16:55:40 +08:00
committed by GitHub
8 changed files with 132 additions and 70 deletions

View File

@@ -13,11 +13,16 @@
<viewModels:PaginationDemoViewModel /> <viewModels:PaginationDemoViewModel />
</Design.DataContext> </Design.DataContext>
<StackPanel> <StackPanel>
<TextBlock Text="{Binding #page.CurrentPage}" /> <StackPanel Orientation="Horizontal">
<TextBlock Text="Current Page: "></TextBlock>
<TextBlock Text="{Binding #page.CurrentPage}" />
</StackPanel>
<ToggleSwitch Name="pageSizeSelector" Content="Show Page Size Selector" /> <ToggleSwitch Name="pageSizeSelector" Content="Show Page Size Selector" />
<ToggleSwitch Name="quickJumperSelector" Content="Show Quick Jumper"></ToggleSwitch>
<u:Pagination <u:Pagination
Name="page" Name="page"
PageSizeOptions="10, 20, 50, 100" PageSizeOptions="10, 20, 50, 100"
ShowQuickJump="{Binding #quickJumperSelector.IsChecked}"
ShowPageSizeSelector="{Binding #pageSizeSelector.IsChecked}" ShowPageSizeSelector="{Binding #pageSizeSelector.IsChecked}"
TotalCount="600" /> TotalCount="600" />
</StackPanel> </StackPanel>

View File

@@ -34,7 +34,7 @@ public class MenuViewModel: ViewModelBase
// new() { MenuHeader = "Number Displayer", Key = MenuKeys.MenuKeyNumberDisplayer, Status = "New" }, // new() { MenuHeader = "Number Displayer", Key = MenuKeys.MenuKeyNumberDisplayer, Status = "New" },
new() { MenuHeader = "Numeric UpDown", Key = MenuKeys.MenuKeyNumericUpDown }, new() { MenuHeader = "Numeric UpDown", Key = MenuKeys.MenuKeyNumericUpDown },
new() { MenuHeader = "NumPad", Key = MenuKeys.MenuKeyNumPad, Status = "New" }, new() { MenuHeader = "NumPad", Key = MenuKeys.MenuKeyNumPad, Status = "New" },
new() { MenuHeader = "Pagination", Key = MenuKeys.MenuKeyPagination }, new() { MenuHeader = "Pagination", Key = MenuKeys.MenuKeyPagination, Status = "Updated" },
new() { MenuHeader = "RangeSlider", Key = MenuKeys.MenuKeyRangeSlider }, new() { MenuHeader = "RangeSlider", Key = MenuKeys.MenuKeyRangeSlider },
new() { MenuHeader = "Scroll To", Key = MenuKeys.MenuKeyScrollToButton, Status = "New" }, new() { MenuHeader = "Scroll To", Key = MenuKeys.MenuKeyScrollToButton, Status = "New" },
new() { MenuHeader = "Selection List", Key = MenuKeys.MenuKeySelectionList, Status = "New" }, new() { MenuHeader = "Selection List", Key = MenuKeys.MenuKeySelectionList, Status = "New" },

View File

@@ -35,6 +35,7 @@
MinWidth="0" MinWidth="0"
HorizontalContentAlignment="Stretch" HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch" VerticalContentAlignment="Stretch"
ShowButtonSpinner="{TemplateBinding ShowButtonSpinner}"
AllowSpin="{TemplateBinding AllowSpin}" AllowSpin="{TemplateBinding AllowSpin}"
Background="{TemplateBinding Background}" Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}" BorderBrush="{TemplateBinding BorderBrush}"

View File

@@ -12,7 +12,7 @@
<Setter Property="Template"> <Setter Property="Template">
<ControlTemplate TargetType="u:Pagination"> <ControlTemplate TargetType="u:Pagination">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<u:PaginationButton Name="{x:Static u:Pagination.PART_PreviousButton}"> <u:PaginationButton u:DisabledAdorner.IsEnabled="True" Name="{x:Static u:Pagination.PART_PreviousButton}">
<PathIcon <PathIcon
Width="12" Width="12"
Height="12" Height="12"
@@ -20,15 +20,25 @@
Foreground="{DynamicResource PaginationButtonIconForeground}" /> Foreground="{DynamicResource PaginationButtonIconForeground}" />
</u:PaginationButton> </u:PaginationButton>
<StackPanel Name="{x:Static u:Pagination.PART_ButtonPanel}" Orientation="Horizontal" /> <StackPanel Name="{x:Static u:Pagination.PART_ButtonPanel}" Orientation="Horizontal" />
<u:PaginationButton Name="{x:Static u:Pagination.PART_NextButton}"> <u:PaginationButton u:DisabledAdorner.IsEnabled="True" Name="{x:Static u:Pagination.PART_NextButton}">
<PathIcon <PathIcon
Width="12" Width="12"
Height="12" Height="12"
Data="{DynamicResource PaginationForwardGlyph}" Data="{DynamicResource PaginationForwardGlyph}"
Foreground="{DynamicResource PaginationButtonIconForeground}" /> Foreground="{DynamicResource PaginationButtonIconForeground}" />
</u:PaginationButton> </u:PaginationButton>
<StackPanel Orientation="Horizontal" IsVisible="{TemplateBinding ShowQuickJump}">
<TextBlock
Margin="4 0"
VerticalAlignment="Center"
Text="{DynamicResource STRING_PAGINATION_JUMP_TO}" />
<u:NumericIntUpDown x:Name="{x:Static u:Pagination.PART_QuickJumpInput}" ShowButtonSpinner="False" Width="50"></u:NumericIntUpDown>
<TextBlock
Margin="4 0"
VerticalAlignment="Center"
Text="{DynamicResource STRING_PAGINATION_PAGE}" />
</StackPanel>
<ComboBox <ComboBox
Name="{x:Static u:Pagination.PART_SizeChangerComboBox}"
IsVisible="{TemplateBinding ShowPageSizeSelector}" IsVisible="{TemplateBinding ShowPageSizeSelector}"
ItemsSource="{TemplateBinding PageSizeOptions}" ItemsSource="{TemplateBinding PageSizeOptions}"
SelectedItem="{TemplateBinding PageSize, SelectedItem="{TemplateBinding PageSize,
@@ -114,9 +124,9 @@
</Style> </Style>
</Style> </Style>
<Style Selector="^:selected"> <Style Selector="^:selected">
<Setter Property="u:PaginationButton.Background" Value="{DynamicResource PaginationButtonSelectedBackground}" /> <Setter Property="Background" Value="{DynamicResource PaginationButtonSelectedBackground}" />
<Setter Property="u:PaginationButton.Foreground" Value="{DynamicResource PaginationButtonSelectedForeground}" /> <Setter Property="Foreground" Value="{DynamicResource PaginationButtonSelectedForeground}" />
<Setter Property="u:PaginationButton.FontWeight" Value="600" /> <Setter Property="FontWeight" Value="600" />
</Style> </Style>
</ControlTheme> </ControlTheme>

View File

@@ -10,4 +10,6 @@
<x:String x:Key="STRING_MENU_DIALOG_YES">Yes</x:String> <x:String x:Key="STRING_MENU_DIALOG_YES">Yes</x:String>
<x:String x:Key="STRING_MENU_DIALOG_NO">No</x:String> <x:String x:Key="STRING_MENU_DIALOG_NO">No</x:String>
<x:String x:Key="STRING_MENU_DIALOG_CLOSE">Close</x:String> <x:String x:Key="STRING_MENU_DIALOG_CLOSE">Close</x:String>
<x:String x:Key="STRING_PAGINATION_JUMP_TO">Jump to page</x:String>
<x:String x:Key="STRING_PAGINATION_PAGE"> </x:String>
</ResourceDictionary> </ResourceDictionary>

View File

@@ -10,4 +10,6 @@
<x:String x:Key="STRING_MENU_DIALOG_YES">是</x:String> <x:String x:Key="STRING_MENU_DIALOG_YES">是</x:String>
<x:String x:Key="STRING_MENU_DIALOG_NO">否</x:String> <x:String x:Key="STRING_MENU_DIALOG_NO">否</x:String>
<x:String x:Key="STRING_MENU_DIALOG_CLOSE">关闭</x:String> <x:String x:Key="STRING_MENU_DIALOG_CLOSE">关闭</x:String>
<x:String x:Key="STRING_PAGINATION_JUMP_TO">跳至</x:String>
<x:String x:Key="STRING_PAGINATION_PAGE">页</x:String>
</ResourceDictionary> </ResourceDictionary>

View File

@@ -110,6 +110,15 @@ public abstract class NumericUpDown : TemplatedControl, IClearControl
set => SetValue(AllowSpinProperty, value); set => SetValue(AllowSpinProperty, value);
} }
public static readonly StyledProperty<bool> ShowButtonSpinnerProperty =
ButtonSpinner.ShowButtonSpinnerProperty.AddOwner<NumericUpDown>();
public bool ShowButtonSpinner
{
get => GetValue(ShowButtonSpinnerProperty);
set => SetValue(ShowButtonSpinnerProperty, value);
}
public event EventHandler<SpinEventArgs>? Spinned; public event EventHandler<SpinEventArgs>? Spinned;
static NumericUpDown() static NumericUpDown()

View File

@@ -3,8 +3,10 @@ using Avalonia.Collections;
using Avalonia.Controls; using Avalonia.Controls;
using Avalonia.Controls.Metadata; using Avalonia.Controls.Metadata;
using Avalonia.Controls.Primitives; using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Interactivity; using Avalonia.Interactivity;
using Avalonia.Styling; using Avalonia.Styling;
using Irihi.Avalonia.Shared.Helpers;
namespace Ursa.Controls; namespace Ursa.Controls;
@@ -16,39 +18,23 @@ namespace Ursa.Controls;
[TemplatePart(PART_PreviousButton, typeof(PaginationButton))] [TemplatePart(PART_PreviousButton, typeof(PaginationButton))]
[TemplatePart(PART_NextButton, typeof(PaginationButton))] [TemplatePart(PART_NextButton, typeof(PaginationButton))]
[TemplatePart(PART_ButtonPanel, typeof(StackPanel))] [TemplatePart(PART_ButtonPanel, typeof(StackPanel))]
[TemplatePart(PART_SizeChangerComboBox, typeof(ComboBox))] [TemplatePart(PART_QuickJumpInput, typeof(NumericIntUpDown))]
public class Pagination: TemplatedControl public class Pagination: TemplatedControl
{ {
public const string PART_PreviousButton = "PART_PreviousButton"; public const string PART_PreviousButton = "PART_PreviousButton";
public const string PART_NextButton = "PART_NextButton"; public const string PART_NextButton = "PART_NextButton";
public const string PART_ButtonPanel = "PART_ButtonPanel"; public const string PART_ButtonPanel = "PART_ButtonPanel";
public const string PART_SizeChangerComboBox = "PART_SizeChangerComboBox"; public const string PART_QuickJumpInput = "PART_QuickJumpInput";
private PaginationButton? _previousButton; private PaginationButton? _previousButton;
private PaginationButton? _nextButton; private PaginationButton? _nextButton;
private StackPanel? _buttonPanel; private StackPanel? _buttonPanel;
private readonly PaginationButton[] _buttons = new PaginationButton[7]; private readonly PaginationButton[] _buttons = new PaginationButton[7];
private ComboBox? _sizeChangerComboBox; private NumericIntUpDown? _quickJumpInput;
protected override void OnApplyTemplate(TemplateAppliedEventArgs e) public static readonly StyledProperty<int?> CurrentPageProperty = AvaloniaProperty.Register<Pagination, int?>(
{
base.OnApplyTemplate(e);
if (_previousButton != null) _previousButton.Click -= OnButtonClick;
if (_nextButton != null) _nextButton.Click -= OnButtonClick;
_previousButton = e.NameScope.Find<PaginationButton>(PART_PreviousButton);
_nextButton = e.NameScope.Find<PaginationButton>(PART_NextButton);
_buttonPanel = e.NameScope.Find<StackPanel>(PART_ButtonPanel);
_sizeChangerComboBox = e.NameScope.Find<ComboBox>(PART_SizeChangerComboBox);
if (_previousButton != null) _previousButton.Click += OnButtonClick;
if (_nextButton != null) _nextButton.Click += OnButtonClick;
InitializePanelButtons();
UpdateButtons(0);
}
public static readonly StyledProperty<int> CurrentPageProperty = AvaloniaProperty.Register<Pagination, int>(
nameof(CurrentPage)); nameof(CurrentPage));
public int CurrentPage public int? CurrentPage
{ {
get => GetValue(CurrentPageProperty); get => GetValue(CurrentPageProperty);
set => SetValue(CurrentPageProperty, value); set => SetValue(CurrentPageProperty, value);
@@ -128,33 +114,78 @@ public class Pagination: TemplatedControl
set => SetValue(ShowQuickJumpProperty, value); set => SetValue(ShowQuickJumpProperty, value);
} }
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change) static Pagination()
{ {
base.OnPropertyChanged(change); PageSizeProperty.Changed.AddClassHandler<Pagination, int>((pagination, args)=>pagination.OnPageSizeChanged(args));
// When page size is updated, the selected current page must be updated. CurrentPageProperty.Changed.AddClassHandler<Pagination, int?>((pagination, args) =>
if (change.Property == PageSizeProperty) pagination.UpdateButtonsByCurrentPage(args.NewValue.Value));
TotalCountProperty.Changed.AddClassHandler<Pagination, int>((pagination, args) =>
pagination.UpdateButtonsByCurrentPage(pagination.CurrentPage));
}
private void OnPageSizeChanged(AvaloniaPropertyChangedEventArgs<int> args)
{
int pageCount = TotalCount / args.NewValue.Value;
int residue = TotalCount % args.NewValue.Value;
if (residue > 0)
{ {
int oldPageSize = change.GetOldValue<int>(); pageCount++;
int index = oldPageSize * CurrentPage;
UpdateButtons(index);
} }
else if (change.Property == TotalCountProperty || change.Property == CurrentPageProperty) PageCount = pageCount;
if (CurrentPage > PageCount)
{ {
int index = PageSize * CurrentPage; CurrentPage = null;
UpdateButtons(index);
} }
UpdateButtonsByCurrentPage(CurrentPage);
}
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
Button.ClickEvent.AddHandler(OnButtonClick, _previousButton, _nextButton);
_previousButton = e.NameScope.Find<PaginationButton>(PART_PreviousButton);
_nextButton = e.NameScope.Find<PaginationButton>(PART_NextButton);
_buttonPanel = e.NameScope.Find<StackPanel>(PART_ButtonPanel);
Button.ClickEvent.AddHandler(OnButtonClick, _previousButton, _nextButton);
KeyDownEvent.RemoveHandler(OnQuickJumpInputKeyDown, _quickJumpInput);
LostFocusEvent.RemoveHandler(OnQuickJumpInputLostFocus, _quickJumpInput);
_quickJumpInput = e.NameScope.Find<NumericIntUpDown>(PART_QuickJumpInput);
KeyDownEvent.AddHandler(OnQuickJumpInputKeyDown, _quickJumpInput);
LostFocusEvent.AddHandler(OnQuickJumpInputLostFocus, _quickJumpInput);
InitializePanelButtons();
UpdateButtonsByCurrentPage(0);
}
private void OnQuickJumpInputKeyDown(object sender, KeyEventArgs e)
{
if (e.Key is Key.Enter or Key.Return)
{
SyncQuickJumperValue();
}
}
private void OnQuickJumpInputLostFocus(object sender, RoutedEventArgs e)
{
SyncQuickJumperValue();
}
private void SyncQuickJumperValue()
{
if (_quickJumpInput is null) return;
var value = _quickJumpInput?.Value;
if (value is null) return;
value = Clamp(value.Value, 1, PageCount);
SetCurrentValue(CurrentPageProperty, value);
_quickJumpInput?.SetCurrentValue(NumericIntUpDown.ValueProperty, null);
} }
private void OnButtonClick(object? sender, RoutedEventArgs e) private void OnButtonClick(object? sender, RoutedEventArgs e)
{ {
if (Equals(sender, _previousButton)) var diff = Equals(sender, _previousButton) ? -1 : 1;
{ AddCurrentPage(diff);
AddCurrentPage(-1);
}
else
{
AddCurrentPage(1);
}
} }
private void InitializePanelButtons() private void InitializePanelButtons()
@@ -166,7 +197,7 @@ public class Pagination: TemplatedControl
var button = new PaginationButton() { Page = i, IsVisible = true }; var button = new PaginationButton() { Page = i, IsVisible = true };
_buttonPanel.Children.Add(button); _buttonPanel.Children.Add(button);
_buttons![i - 1] = button; _buttons![i - 1] = button;
button.Click+= OnPageButtonClick; Button.ClickEvent.AddHandler(OnPageButtonClick, button);
} }
} }
@@ -191,19 +222,26 @@ public class Pagination: TemplatedControl
private void AddCurrentPage(int pageChange) private void AddCurrentPage(int pageChange)
{ {
int newValue = CurrentPage + pageChange; int newValue = (CurrentPage ?? 0) + pageChange;
if (newValue <= 0) newValue = 1; newValue = Clamp(newValue, 1, PageCount);
else if(newValue>=PageCount) newValue = PageCount; ; SetCurrentValue(CurrentPageProperty, newValue);
CurrentPage = newValue;
} }
private void UpdateButtons(int index) private int Clamp(int value, int min, int max)
{
return value < min ? min : value > max ? max : value;
}
/// <summary>
/// Update Button Content and Visibility by current page.
/// </summary>
/// <param name="page"></param>
private void UpdateButtonsByCurrentPage(int? page)
{ {
if (_buttonPanel is null) return; if (_buttonPanel is null) return;
if (PageSize == 0) return; if (PageSize == 0) return;
int currentIndex = index; int? currentPage = CurrentPage;
int pageCount = TotalCount / PageSize; int pageCount = TotalCount / PageSize;
int residue = TotalCount % PageSize; int residue = TotalCount % PageSize;
if (residue > 0) if (residue > 0)
@@ -211,11 +249,6 @@ public class Pagination: TemplatedControl
pageCount++; pageCount++;
} }
PageCount = pageCount;
int currentPage = currentIndex/ PageSize;
if (currentPage == 0) currentPage++;
if (pageCount <= 7) if (pageCount <= 7)
{ {
for (int i = 0; i < 7; i++) for (int i = 0; i < 7; i++)
@@ -237,9 +270,8 @@ public class Pagination: TemplatedControl
{ {
_buttons[i].IsVisible = true; _buttons[i].IsVisible = true;
} }
int mid = currentPage; int mid = currentPage ?? 0;
if (mid < 4) mid = 4; mid = Clamp(mid, 4, pageCount - 3);
else if (mid > pageCount - 3) mid = pageCount - 3;
_buttons[3].Page = mid; _buttons[3].Page = mid;
_buttons[2].Page = mid - 1; _buttons[2].Page = mid - 1;
_buttons[4].Page = mid + 1; _buttons[4].Page = mid + 1;
@@ -247,7 +279,7 @@ public class Pagination: TemplatedControl
_buttons[6].Page = pageCount; _buttons[6].Page = pageCount;
if(mid>4) if(mid>4)
{ {
_buttons[1].SetStatus(0, false, true, false); _buttons[1].SetStatus(-1, false, true, false);
} }
else else
{ {
@@ -255,7 +287,7 @@ public class Pagination: TemplatedControl
} }
if(mid<pageCount-3) if(mid<pageCount-3)
{ {
_buttons[5].SetStatus(0, false, false, true); _buttons[5].SetStatus(-1, false, false, true);
} }
else else
{ {
@@ -275,8 +307,9 @@ public class Pagination: TemplatedControl
} }
} }
CurrentPage = currentPage; PageCount = pageCount;
if (_previousButton != null) _previousButton.IsEnabled = CurrentPage > 1; SetCurrentValue(CurrentPageProperty, currentPage);
if( _nextButton!=null) _nextButton.IsEnabled = CurrentPage < PageCount; if (_previousButton != null) _previousButton.IsEnabled = (CurrentPage??int.MaxValue) > 1;
if (_nextButton != null) _nextButton.IsEnabled = (CurrentPage ?? 0) < PageCount;
} }
} }