自定义分页控件
tip: 该控件的样式用的是materialDesign库,需要下载Nuget包
Code
- Xaml
<UserControl
x:Class="TestTool.CustomControls.PagingControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
x:Name="pagingControl"
TextElement.FontSize="14"
mc:Ignorable="d">
<UserControl.Resources>
<Style TargetType="Button" BasedOn="{StaticResource MaterialDesignIconButton}">
<Setter Property="Width" Value="26" />
<Setter Property="Height" Value="26" />
</Style>
</UserControl.Resources>
<StackPanel DataContext="{Binding ElementName=pagingControl}" Orientation="Horizontal">
<Button
Click="Button_FirstPage_Click"
ToolTip="首页">
<materialDesign:PackIcon
Width="24"
Height="24"
VerticalAlignment="Center"
Kind="PageFirst" />
</Button>
<Button
Margin="10,0,10,0"
Click="Button_Previous_Click"
ToolTip="上一页">
<materialDesign:PackIcon
Width="20"
Height="20"
VerticalAlignment="Center"
Kind="ArrowLeftCircle" />
</Button>
<TextBlock VerticalAlignment="Center" Text="{Binding CurrentPageIndex}" />
<TextBlock VerticalAlignment="Center">/</TextBlock>
<TextBlock VerticalAlignment="Center" Text="{Binding TotalPageCount}" />
<Button
Width="26"
Height="26"
Margin="10,0,0,0"
Click="Button_Next_Click"
ToolTip="下一页">
<materialDesign:PackIcon
Width="20"
Height="20"
VerticalAlignment="Center"
Kind="ArrowRightCircle" />
</Button>
<Button
Margin="10,0,0,0"
Click="Button_Last_Click"
ToolTip="尾页">
<materialDesign:PackIcon
Width="24"
Height="24"
VerticalAlignment="Center"
Kind="PageLast" />
</Button>
<TextBlock Margin="20,0,0,0" VerticalAlignment="Center">共</TextBlock>
<TextBlock
Margin="5,0,0,0"
VerticalAlignment="Center"
Text="{Binding TotalCount}" />
<TextBlock Margin="5,0,0,0" VerticalAlignment="Center">条记录</TextBlock>
<Separator
Height="14"
Margin="10,0,0,0"
BorderThickness="0.5"
Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" />
<TextBlock Margin="10,0,0,0" VerticalAlignment="Center">每页数量</TextBlock>
<ComboBox
MinWidth="50"
Margin="5,0,0,0"
VerticalContentAlignment="Center"
ItemsSource="{Binding PagesSizes}"
SelectedIndex="0"
SelectedItem="{Binding CurrentPageSize}"
SelectionChanged="ComboBox_PageSize_SelectionChanged" />
<Separator
Height="14"
Margin="10,0,0,0"
BorderThickness="0.5"
Style="{StaticResource {x:Static ToolBar.SeparatorStyleKey}}" />
<TextBlock Margin="10,0,0,0" VerticalAlignment="Center">跳转到第</TextBlock>
<TextBox
Width="50"
Margin="5,0,5,0"
VerticalAlignment="Center"
HorizontalContentAlignment="Center"
Text="{Binding GotoPageIndex}" />
<TextBlock VerticalAlignment="Center">页</TextBlock>
<Button
Margin="5,0,0,0"
Click="Button_Goto_Click"
ToolTip="跳转">
<materialDesign:PackIcon
Width="24"
Height="24"
VerticalAlignment="Center"
Kind="ArrowRightBold" />
</Button>
</StackPanel>
</UserControl>
- C#
public partial class PagingControl : UserControl, INotifyPropertyChanged
{
// 用于防抖、节流
private readonly ConcurrentQueue<DateTime> _operationTime = new();
//用于防抖、节流,拦截两次时间少于1秒的操作
private readonly TimeSpan _ignoreOperationLessThen = TimeSpan.FromSeconds(1);
public event EventHandler CanExecuteChanged;
public event PropertyChangedEventHandler? PropertyChanged;
#region 普通属性
/// <summary>
/// 能否执行状态
/// </summary>
public bool CanExecute => QueryCommand?.CanExecute(null) ?? true;
//在load后是否立即触发命令和路由事件
public bool LoadedTrigger { get; set; } = false;
public ObservableCollection<int> PagesSizes { get; set; } = [15, 30, 60, 200, 500, 2000];
/// <summary>
/// 总页数
/// </summary>
private int _totalPageCount;
public int TotalPageCount
{
get => _totalPageCount;
set
{
if (_totalPageCount != value)
{
_totalPageCount = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(TotalPageCount)));
}
}
}
/// <summary>
/// 跳转页码
/// </summary>
private int _gotoPageIndex;
public int GotoPageIndex
{
get => _gotoPageIndex;
set
{
if (_gotoPageIndex != value)
{
_gotoPageIndex = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(GotoPageIndex)));
}
}
}
/// <summary>
/// 当前页大小
/// </summary>
private int _currentPageSize = 15;
public int CurrentPageSize
{
get => _currentPageSize;
set
{
if (_currentPageSize != value)
{
_currentPageSize = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CurrentPageSize)));
}
}
}
/// <summary>
/// 当前页码
/// </summary>
private int _currentPageIndex;
public int CurrentPageIndex
{
get => _currentPageIndex;
set
{
if (_currentPageIndex != value)
{
_currentPageIndex = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(CurrentPageIndex)));
}
}
}
//用于外部绑定命令
public ICommand PageQueryCommand { get; }
#endregion
#region 依赖属性
/// <summary>
/// 总数
/// </summary>
public int TotalCount
{
get { return (int)GetValue(TotalCountProperty); }
set { SetValue(TotalCountProperty, value); }
}
public ICommand QueryCommand
{
get { return (ICommand)GetValue(QueryCommandProperty); }
set { SetValue(QueryCommandProperty, value); }
}
public static readonly DependencyProperty TotalCountProperty =
DependencyProperty.Register("TotalCount", typeof(int), typeof(PagingControl), new FrameworkPropertyMetadata(15, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, TotalCountChangedCallback));
public static readonly DependencyProperty QueryCommandProperty =
DependencyProperty.Register("QueryCommand", typeof(ICommand), typeof(PagingControl), new PropertyMetadata(null, QueryCommandChangedCallback));
#endregion
public static readonly RoutedEvent PagingChangedEvent = EventManager.RegisterRoutedEvent("PagingChanged", RoutingStrategy.Bubble, typeof(EventHandler<PagingChangedEventArgs>), typeof(PagingControl));
public event RoutedEventHandler PagingChanged
{
add => AddHandler(PagingChangedEvent, value);
remove => RemoveHandler(PagingChangedEvent, value);
}
/// <summary>
/// 构造函数
/// </summary>
public PagingControl()
{
InitializeComponent();
TotalCount = 0;
GotoPageIndex = 1;
CurrentPageIndex = 0;
TotalPageCount = 0;
PageQueryCommand = new PageQueryCommand(this);
}
static void TotalCountChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
PagingControl pagingControl = (PagingControl)d;
pagingControl.ReLoad((int)e.NewValue);
}
static void QueryCommandChangedCallback(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var command = e.NewValue as ICommand;
if (command != null)
{
PagingControl pagingControl = (PagingControl)d;
command.CanExecuteChanged -= pagingControl.Command_CanExecuteChanged;
command.CanExecuteChanged += pagingControl.Command_CanExecuteChanged;
if (pagingControl.LoadedTrigger)
pagingControl.FirstPage();
}
}
private void Command_CanExecuteChanged(object? sender, EventArgs e)
{
CanExecuteChanged?.Invoke(this, e);
IsEnabled = QueryCommand.CanExecute(null);
}
#region 事件
/// <summary>
/// 每页大小 ComboBox 变化
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void ComboBox_PageSize_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
ReLoad(TotalCount);
CurrentPageIndex = 1;
OnPagingChanged(CurrentPageIndex);
e.Handled = true;
}
/// <summary>
/// 首页按钮点击事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Button_FirstPage_Click(object sender, RoutedEventArgs e)
{
FirstPage();
e.Handled = true;
}
/// <summary>
/// 尾页按钮点击事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Button_Last_Click(object sender, RoutedEventArgs e)
{
LastPage();
e.Handled = true;
}
/// <summary>
/// 上一页按钮点击事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Button_Previous_Click(object sender, RoutedEventArgs e)
{
PreviousPage();
e.Handled = true;
}
/// <summary>
/// 下一页按钮点击事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Button_Next_Click(object sender, RoutedEventArgs e)
{
NextPage();
e.Handled = true;
}
/// <summary>
/// 跳转按钮点击事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void Button_Goto_Click(object sender, RoutedEventArgs e)
{
GoToPage();
e.Handled = true;
}
#endregion
private void ReLoad(int totalCount)
{
TotalPageCount = (totalCount + CurrentPageSize - 1) / CurrentPageSize;
if (TotalPageCount == 0)
CurrentPageIndex = 0;
else if (TotalPageCount != 0 && CurrentPageIndex == 0)
CurrentPageIndex = 1;
}
private bool CheckCanExcetue()
{
if (_operationTime.TryDequeue(out var time))
{
if (DateTime.Now - time < _ignoreOperationLessThen)
return false;
}
_operationTime.Enqueue(DateTime.Now);
return CanExecute;
}
public void FirstPage()
{
if (!CheckCanExcetue()) return;
CurrentPageIndex = 1;
OnPagingChanged(CurrentPageIndex);
}
public void LastPage()
{
if (!CheckCanExcetue()) return;
if (TotalPageCount == 0)
{
OnPagingChanged(1);
}
else
{
CurrentPageIndex = TotalPageCount;
OnPagingChanged(CurrentPageIndex);
}
}
public void NextPage()
{
if (!CheckCanExcetue()) return;
if (CurrentPageIndex >= TotalPageCount)
{
OnPagingChanged(CurrentPageIndex);
}
else
{
CurrentPageIndex++;
OnPagingChanged(CurrentPageIndex);
}
}
public void PreviousPage()
{
if (!CheckCanExcetue()) return;
if (CurrentPageIndex > TotalPageCount)
{
CurrentPageIndex = TotalPageCount;
}
if (CurrentPageIndex > 1)
{
CurrentPageIndex--;
OnPagingChanged(CurrentPageIndex);
}
else
{
OnPagingChanged(1);
}
}
public void GoToPage()
{
if (!CheckCanExcetue()) return;
if (GotoPageIndex < 1 || GotoPageIndex > TotalPageCount)
return;
CurrentPageIndex = GotoPageIndex;
OnPagingChanged(CurrentPageIndex);
}
private void OnPagingChanged(int pageIndex)
{
PagingChangedEventArgs pagingChangedEventArgs = new(PagingChangedEvent, this,
new PagingInfo(pageIndex, CurrentPageSize));
RaiseEvent(pagingChangedEventArgs);
QueryCommand?.Execute(new PagingInfo(pageIndex, CurrentPageSize));
}
}
相关自定义类
public record class PagingInfo(int TargetPageIndex, int PageSize);
路由事件参数
public class PagingChangedEventArgs(RoutedEvent routedEvent, object source, PagingInfo pageInfo) : RoutedEventArgs(routedEvent, source)
{
public PagingInfo PageInfo { get; set; } = pageInfo;
}
外部绑定命令(用于MVVM模式下,需要其它地方触发分页控件命令)
public class PageQueryCommand : ICommand
{
private readonly PagingControl _pagingControl;
public event EventHandler? CanExecuteChanged;
public PageQueryCommand(PagingControl pagingControl)
{
_pagingControl = pagingControl;
_pagingControl.CanExecuteChanged += (o, e) => CanExecuteChanged?.Invoke(o, e);
}
public bool CanExecute(object? parameter)
{
return _pagingControl?.CanExecute ?? true;
}
public void Execute(object? parameter)
{
_pagingControl.FirstPage();
}
}
Demo
- 当点击分页控件按钮时会触发QueryCommand命令和路由事件,并传入相关参数,需要在查询完结果后将查询到的总数赋值给TotalCount以便更新UI显示的相关信息
- 当需要外部按钮来激发查询命令时,可绑定分页控件的pageQueryCommand
<Button
x:Name="buttonQuery"
Margin="10,0,0,0"
HorizontalAlignment="Stretch"
Content="查询"
Command="{Binding ElementName=pageControl, Path=PageQueryCommand}"/>
<customControls:PagingControl
x:Name="pageControl"
HorizontalAlignment="Right"
DockPanel.Dock="Bottom"
LoadedTrigger="False"
QueryCommand="{Binding PageQueryCommand}"
TotalCount="{Binding TotalCount}" />
[RelayCommand]
private async Task PageQuery(PagingInfo pagingInfo){
//通过数据库进行查询
TotalCount=查出结果数量
}
希望能给你带来帮助 --来自.net菜鸡粉丝的祝愿