分页控件(Pagination)自定义样式
以下是分页控件自定义样式资源部分代码
xmlns:controls="clr-namespace:Wallpaper.CustomControls"是Pagination.cs的地址
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:controls="clr-namespace:Wallpaper.CustomControls"> <Style x:Key="WD.PageListBoxStyleKey" TargetType="{x:Type ListBox}"> <Setter Property="Background" Value="Transparent" /> <Setter Property="BorderThickness" Value="0" /> <Setter Property="Padding" Value="0" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ListBox}"> <Border Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" SnapsToDevicePixels="True"> <ScrollViewer Padding="{TemplateBinding Padding}" Focusable="False"> <ItemsPresenter SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" /> </ScrollViewer> </Border> <ControlTemplate.Triggers> <Trigger Property="IsGrouping" Value="True"> <Setter Property="ScrollViewer.CanContentScroll" Value="False" /> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <Style x:Key="WD.PageListBoxItemStyleKey" TargetType="{x:Type ListBoxItem}"> <Setter Property="MinWidth" Value="32" /> <Setter Property="Height" Value="30" /> <Setter Property="HorizontalContentAlignment" Value="Center" /> <Setter Property="VerticalContentAlignment" Value="Center" /> <Setter Property="BorderThickness" Value="1" /> <Setter Property="Padding" Value="5,0" /> <Setter Property="Margin" Value="3,0" /> <Setter Property="Background" Value="#fedce1" /> <Setter Property="BorderBrush" Value="Transparent" /> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ListBoxItem}"> <Border Padding="{TemplateBinding Padding}" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" CornerRadius="12" SnapsToDevicePixels="True"> <ContentPresenter x:Name="PART_ContentPresenter" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" RecognizesAccessKey="True" TextElement.Foreground="{TemplateBinding Foreground}" /> </Border> </ControlTemplate> </Setter.Value> </Setter> <Style.Triggers> <DataTrigger Binding="{Binding .}" Value="···"> <Setter Property="IsEnabled" Value="False" /> <Setter Property="FontWeight" Value="Bold" /> </DataTrigger> <Trigger Property="IsMouseOver" Value="True"> <Setter Property="BorderBrush" Value="{DynamicResource WD.DefaultBorderBrushSolidColorBrush}" /> <Setter Property="Background" Value="{DynamicResource WD.DefaultBackgroundSolidColorBrush}" /> <Setter Property="Foreground" Value="{DynamicResource WD.PrimaryNormalSolidColorBrush}" /> </Trigger> <Trigger Property="IsSelected" Value="True"> <Setter Property="Background" Value="#fa77b1" /> <Setter Property="TextElement.Foreground" Value="White" /> </Trigger> </Style.Triggers> </Style> <ControlTemplate x:Key="WD.LitePagerControlTemplate" TargetType="{x:Type controls:Pagination}"> <Border Padding="{TemplateBinding Padding}" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="10" /> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="10" /> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="5" /> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="5" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <TextBlock VerticalAlignment="Center" Text="{Binding Count, StringFormat=共 {0} 条, RelativeSource={RelativeSource TemplatedParent}}" /> <TextBox x:Name="PART_CountPerPageTextBox" Grid.Column="2" Width="60" MinWidth="0" VerticalContentAlignment="Center" TextAlignment="Center" /> <TextBlock Grid.Column="3" VerticalAlignment="Center" Text=" 条 / 页" /> <Button Grid.Column="5" Command="{x:Static controls:Pagination.PrevCommand}"> <Path Width="7" Height="10" Data="{StaticResource WD.PreviousGeometry}" Fill="{Binding Foreground, RelativeSource={RelativeSource AncestorType=Button}}" Stretch="Fill" /> </Button> <TextBox x:Name="PART_JumpPageTextBox" Grid.Column="7" Width="60" MinWidth="0" VerticalContentAlignment="Center" TextAlignment="Center"> <TextBox.ToolTip> <TextBlock> <TextBlock.Text> <MultiBinding StringFormat="{}{0}/{1}"> <Binding Path="Current" RelativeSource="{RelativeSource TemplatedParent}" /> <Binding Path="PageCount" RelativeSource="{RelativeSource TemplatedParent}" /> </MultiBinding> </TextBlock.Text> </TextBlock> </TextBox.ToolTip> </TextBox> <Button Grid.Column="9" Command="{x:Static controls:Pagination.NextCommand}"> <Path Width="7" Height="10" Data="{StaticResource WD.NextGeometry}" Fill="{Binding Foreground, RelativeSource={RelativeSource AncestorType=Button}}" Stretch="Fill" /> </Button> </Grid> </Border> </ControlTemplate> <Style x:Key="WD.Pagination" TargetType="{x:Type controls:Pagination}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type controls:Pagination}"> <Border Padding="{TemplateBinding Padding}" Background="{TemplateBinding Background}" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}"> <Grid> <Grid.ColumnDefinitions> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="*" /> <ColumnDefinition Width="Auto" /> <ColumnDefinition Width="Auto" /> </Grid.ColumnDefinitions> <TextBlock Margin="0,0,15,0" VerticalAlignment="Center" Text="{Binding Count, StringFormat=共 {0} 条, RelativeSource={RelativeSource TemplatedParent}}" Visibility="Collapsed" /> <StackPanel Grid.Column="1" Margin="0,0,15,0" Orientation="Horizontal" Visibility="Collapsed"> <TextBlock VerticalAlignment="Center" Text="每页 " /> <TextBox x:Name="PART_CountPerPageTextBox" Width="60" MinWidth="0" VerticalContentAlignment="Center" FontSize="{TemplateBinding FontSize}" TextAlignment="Center" /> <TextBlock VerticalAlignment="Center" Text=" 条" /> </StackPanel> <Button Grid.Column="2" Padding="0" Background="Transparent" BorderBrush="Transparent" Command="{x:Static controls:Pagination.PrevCommand}"> <!--<Path Width="7" Height="10" Data="{TemplateBinding LeftIcon}" Fill="{Binding Foreground, RelativeSource={RelativeSource AncestorType=Button}}" Stretch="Fill" />--> <Image Width="30" Height="25" Source="{TemplateBinding LeftIcon}" Stretch="Uniform" /> </Button> <ListBox x:Name="PART_ListBox" Grid.Column="3" Margin="5,0" ItemContainerStyle="{StaticResource WD.PageListBoxItemStyleKey}" ItemsSource="{TemplateBinding Pages}" ScrollViewer.HorizontalScrollBarVisibility="Hidden" ScrollViewer.VerticalScrollBarVisibility="Hidden" SelectedIndex="0" Style="{StaticResource WD.PageListBoxStyleKey}"> <ListBox.ItemsPanel> <ItemsPanelTemplate> <UniformGrid Rows="1" /> </ItemsPanelTemplate> </ListBox.ItemsPanel> </ListBox> <Button Grid.Column="4" Padding="0" Background="Transparent" BorderBrush="Transparent" Command="{x:Static controls:Pagination.NextCommand}"> <!--<Path Width="7" Height="10" Data="{TemplateBinding RightIcon}" Fill="{Binding Foreground, RelativeSource={RelativeSource AncestorType=Button}}" Stretch="Fill" />--> <Image Width="30" Height="25" Source="{TemplateBinding RightIcon}" Stretch="Uniform" /> </Button> <StackPanel Grid.Column="5" Margin="10,0,0,0" Orientation="Horizontal"> <TextBlock VerticalAlignment="Center" Text=" 前往 " /> <Border BorderBrush="#feb9c9" VerticalAlignment="Center" HorizontalAlignment="Center" BorderThickness="1" Width="40" Height="35" Padding="0" CornerRadius="7"> <TextBox x:Name="PART_JumpPageTextBox" Width="60" MinWidth="0" VerticalContentAlignment="Center" HorizontalAlignment="Center" Background="Transparent" BorderBrush="Transparent" ContextMenu="{x:Null}" FontSize="{TemplateBinding FontSize}" TextAlignment="Center" /> </Border> <TextBlock VerticalAlignment="Center" Text=" 页" /> </StackPanel> </Grid> </Border> </ControlTemplate> </Setter.Value> </Setter> <Style.Triggers> <Trigger Property="IsLite" Value="True"> <Setter Property="Template" Value="{StaticResource WD.LitePagerControlTemplate}" /> </Trigger> </Style.Triggers> </Style> <Style x:Key="NPPagination" BasedOn="{StaticResource WD.Pagination}" TargetType="{x:Type controls:Pagination}" /> </ResourceDictionary>
后台绑定代码
[TemplatePart(Name = CountPerPageTextBoxTemplateName, Type = typeof(TextBox))] [TemplatePart(Name = JustPageTextBoxTemplateName, Type = typeof(TextBox))] [TemplatePart(Name = ListBoxTemplateName, Type = typeof(ListBox))] public class Pagination : Control { private const string CountPerPageTextBoxTemplateName = "PART_CountPerPageTextBox"; private const string JustPageTextBoxTemplateName = "PART_JumpPageTextBox"; private const string ListBoxTemplateName = "PART_ListBox"; private const string Ellipsis = "···"; private static readonly Type _typeofSelf = typeof(Pagination); private TextBox _countPerPageTextBox; private TextBox _jumpPageTextBox; private ListBox _listBox; static Pagination() { InitializeCommands(); DefaultStyleKeyProperty.OverrideMetadata(_typeofSelf, new FrameworkPropertyMetadata(_typeofSelf)); } #region Override public override void OnApplyTemplate() { base.OnApplyTemplate(); UnsubscribeEvents(); _countPerPageTextBox = GetTemplateChild(CountPerPageTextBoxTemplateName) as TextBox; if (_countPerPageTextBox != null) { _countPerPageTextBox.ContextMenu = null; _countPerPageTextBox.PreviewTextInput += _countPerPageTextBox_PreviewTextInput; _countPerPageTextBox.PreviewKeyDown += _countPerPageTextBox_PreviewKeyDown; } _jumpPageTextBox = GetTemplateChild(JustPageTextBoxTemplateName) as TextBox; if (_jumpPageTextBox != null) { _jumpPageTextBox.ContextMenu = null; _jumpPageTextBox.PreviewTextInput += _countPerPageTextBox_PreviewTextInput; _jumpPageTextBox.PreviewKeyDown += _countPerPageTextBox_PreviewKeyDown; } _listBox = GetTemplateChild(ListBoxTemplateName) as ListBox; Init(); SubscribeEvents(); } private void _countPerPageTextBox_PreviewKeyDown(object sender, KeyEventArgs e) { if (Key.Space == e.Key || Key.V == e.Key && e.KeyboardDevice.Modifiers == ModifierKeys.Control) e.Handled = true; } private void _countPerPageTextBox_PreviewTextInput(object sender, TextCompositionEventArgs e) { //e.Handled = ControlsHelper.IsNumber(e.Text); } #endregion #region Command private static void InitializeCommands() { PrevCommand = new RoutedCommand("Prev", _typeofSelf); NextCommand = new RoutedCommand("Next", _typeofSelf); CommandManager.RegisterClassCommandBinding(_typeofSelf, new CommandBinding(PrevCommand, OnPrevCommand, OnCanPrevCommand)); CommandManager.RegisterClassCommandBinding(_typeofSelf, new CommandBinding(NextCommand, OnNextCommand, OnCanNextCommand)); } public static RoutedCommand PrevCommand { get; private set; } public static RoutedCommand NextCommand { get; private set; } private static void OnPrevCommand(object sender, RoutedEventArgs e) { var ctrl = sender as Pagination; ctrl.Current--; } private static void OnCanPrevCommand(object sender, CanExecuteRoutedEventArgs e) { var ctrl = sender as Pagination; e.CanExecute = ctrl.Current > 1; } private static void OnNextCommand(object sender, RoutedEventArgs e) { var ctrl = sender as Pagination; ctrl.Current++; } private static void OnCanNextCommand(object sender, CanExecuteRoutedEventArgs e) { var ctrl = sender as Pagination; e.CanExecute = ctrl.Current < ctrl.PageCount; } #endregion #region Properties public ImageSource LeftIcon { get { return (ImageSource)GetValue(LeftIconProperty); } set { SetValue(LeftIconProperty, value); } } // Using a DependencyProperty as the backing store for LeftIcon. This enables animation, styling, binding, etc... public static readonly DependencyProperty LeftIconProperty = DependencyProperty.Register("LeftIcon", typeof(ImageSource), typeof(Pagination), new PropertyMetadata(null)); public ImageSource RightIcon { get { return (ImageSource)GetValue(RightIconProperty); } set { SetValue(RightIconProperty, value); } } // Using a DependencyProperty as the backing store for RightIcon. This enables animation, styling, binding, etc... public static readonly DependencyProperty RightIconProperty = DependencyProperty.Register("RightIcon", typeof(ImageSource), typeof(Pagination), new PropertyMetadata(null)); private static readonly DependencyPropertyKey PagesPropertyKey = DependencyProperty.RegisterReadOnly("Pages", typeof(IEnumerable<string>), _typeofSelf, new PropertyMetadata(null)); public static readonly DependencyProperty PagesProperty = PagesPropertyKey.DependencyProperty; public IEnumerable<string> Pages => (IEnumerable<string>)GetValue(PagesProperty); private static readonly DependencyPropertyKey PageCountPropertyKey = DependencyProperty.RegisterReadOnly("PageCount", typeof(int), _typeofSelf, new PropertyMetadata(1, OnPageCountPropertyChanged)); public static readonly DependencyProperty PageCountProperty = PageCountPropertyKey.DependencyProperty; public int PageCount => (int)GetValue(PageCountProperty); private static void OnPageCountPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var ctrl = d as Pagination; var pageCount = (int)e.NewValue; /* if (ctrl._jumpPageTextBox != null) ctrl._jumpPageTextBox.Maximum = pageCount; */ } public static readonly DependencyProperty IsLiteProperty = DependencyProperty.Register("IsLite", typeof(bool), _typeofSelf, new PropertyMetadata(false)); public bool IsLite { get => (bool)GetValue(IsLiteProperty); set => SetValue(IsLiteProperty, value); } public static readonly DependencyProperty CountProperty = DependencyProperty.Register("Count", typeof(int), _typeofSelf, new PropertyMetadata(0, OnCountPropertyChanged, CoerceCount)); public int Count { get => (int)GetValue(CountProperty); set => SetValue(CountProperty, value); } private static object CoerceCount(DependencyObject d, object value) { var count = (int)value; return Math.Max(count, 0); } private static void OnCountPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var ctrl = d as Pagination; var count = (int)e.NewValue; ctrl.SetValue(PageCountPropertyKey, (int)Math.Ceiling(count * 1.0 / ctrl.CountPerPage)); ctrl.UpdatePages(); } public static readonly DependencyProperty CountPerPageProperty = DependencyProperty.Register("CountPerPage", typeof(int), _typeofSelf, new PropertyMetadata(50, OnCountPerPagePropertyChanged, CoerceCountPerPage)); public int CountPerPage { get => (int)GetValue(CountPerPageProperty); set => SetValue(CountPerPageProperty, value); } private static object CoerceCountPerPage(DependencyObject d, object value) { var countPerPage = (int)value; return Math.Max(countPerPage, 1); } private static void OnCountPerPagePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var ctrl = d as Pagination; var countPerPage = (int)e.NewValue; if (ctrl._countPerPageTextBox != null) ctrl._countPerPageTextBox.Text = countPerPage.ToString(); ctrl.SetValue(PageCountPropertyKey, (int)Math.Ceiling(ctrl.Count * 1.0 / countPerPage)); if (ctrl.Current != 1) ctrl.Current = 1; else ctrl.UpdatePages(); } public static readonly DependencyProperty CurrentProperty = DependencyProperty.Register("Current", typeof(int), _typeofSelf, new PropertyMetadata(1, OnCurrentPropertyChanged, CoerceCurrent)); public int Current { get => (int)GetValue(CurrentProperty); set => SetValue(CurrentProperty, value); } private static object CoerceCurrent(DependencyObject d, object value) { var current = (int)value; var ctrl = d as Pagination; return Math.Max(current, 1); } private static void OnCurrentPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var ctrl = d as Pagination; var current = (int)e.NewValue; if (ctrl._listBox != null) ctrl._listBox.SelectedItem = current.ToString(); if (ctrl._jumpPageTextBox != null) ctrl._jumpPageTextBox.Text = current.ToString(); ctrl.UpdatePages(); } #endregion #region Event /// <summary> /// 分页 /// </summary> private void OnCountPerPageTextBoxChanged(object sender, TextChangedEventArgs e) { if (int.TryParse(_countPerPageTextBox.Text, out var _ountPerPage)) CountPerPage = _ountPerPage; } /// <summary> /// 跳转页 /// </summary> private void OnJumpPageTextBoxChanged(object sender, TextChangedEventArgs e) { if (int.TryParse(_jumpPageTextBox.Text, out var _current)) Current = _current; } /// <summary> /// 选择页 /// </summary> private void OnSelectionChanged(object sender, SelectionChangedEventArgs e) { if (_listBox.SelectedItem == null) return; Current = int.Parse(_listBox.SelectedItem.ToString()); } #endregion #region Private private void Init() { SetValue(PageCountPropertyKey, (int)Math.Ceiling(Count * 1.0 / CountPerPage)); _jumpPageTextBox.Text = Current.ToString(); //_jumpPageTextBox.Maximum = PageCount; _countPerPageTextBox.Text = CountPerPage.ToString(); if (_listBox != null) _listBox.SelectedItem = Current.ToString(); } private void UnsubscribeEvents() { if (_countPerPageTextBox != null) _countPerPageTextBox.TextChanged -= OnCountPerPageTextBoxChanged; if (_jumpPageTextBox != null) _jumpPageTextBox.TextChanged -= OnJumpPageTextBoxChanged; if (_listBox != null) _listBox.SelectionChanged -= OnSelectionChanged; } private void SubscribeEvents() { if (_countPerPageTextBox != null) _countPerPageTextBox.TextChanged += OnCountPerPageTextBoxChanged; if (_jumpPageTextBox != null) _jumpPageTextBox.TextChanged += OnJumpPageTextBoxChanged; if (_listBox != null) _listBox.SelectionChanged += OnSelectionChanged; } private void UpdatePages() { SetValue(PagesPropertyKey, GetPagers(Count, Current)); if (_listBox != null && _listBox.SelectedItem == null) _listBox.SelectedItem = Current.ToString(); } private IEnumerable<string> GetPagers(int count, int current) { if (count == 0) return null; if (PageCount <= 7) return Enumerable.Range(1, PageCount).Select(p => p.ToString()).ToArray(); if (current <= 4) return new[] { "1", "2", "3", "4", "5", Ellipsis, PageCount.ToString() }; if (current >= PageCount - 3) return new[] { "1", Ellipsis, (PageCount - 4).ToString(), (PageCount - 3).ToString(), (PageCount - 2).ToString(), (PageCount - 1).ToString(), PageCount.ToString() }; return new[] { "1", Ellipsis, (current - 1).ToString(), current.ToString(), (current + 1).ToString(), Ellipsis, PageCount.ToString() }; } #endregion }
xaml页面调用分页控件
<custom:Pagination Grid.Column="1" Width="400" HorizontalAlignment="Left" Count="{Binding Count, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" CountPerPage="{Binding CountPerPage, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" Current="{Binding Current, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" LeftIcon="pack://application:,,,/Images/ic_zuo@2x.png" RightIcon="pack://application:,,,/Images/ic_you@2x.png" Style="{StaticResource NPPagination}" />
与之绑定的要翻页的容器如listbox
<ListBox Height="200" Padding="0" Background="Transparent" BorderBrush="Transparent" ItemContainerStyle="{StaticResource ListBoxItem_MousePointer}" ItemsSource="{Binding MousePointerCollections, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" ScrollViewer.HorizontalScrollBarVisibility="Disabled"> <ListBox.ItemsPanel> <ItemsPanelTemplate> <WrapPanel Background="Transparent" Orientation="Horizontal" /> </ItemsPanelTemplate> </ListBox.ItemsPanel> </ListBox>
ViewModel部分
private int _count; public int Count { get { return _count; } set { _count = value; this.RaisePropertyChanged("Count"); CurrentPageChanged(); } } private int _countPerPage = 4; public int CountPerPage { get { return _countPerPage; } set { _countPerPage = value; this.RaisePropertyChanged("CountPerPage"); CurrentPageChanged(); } } private int _current = 1; public int Current { get { return _current; } set { _current = value; this.RaisePropertyChanged("Current"); CurrentPageChanged(); } } private void CurrentPageChanged() { MousePointerCollections.Clear(); foreach (var i in _sourceList.Skip((Current - 1) * CountPerPage).Take(CountPerPage)) { MousePointerCollections.Add(i); } }
public ObservableCollection<MousePointerCollection> MousePointerCollections { get; set; } = new ObservableCollection<MousePointerCollection>();
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
· AI Agent开发,如何调用三方的API Function,是通过提示词来发起调用的吗