WPF基于MVVM实现自定义分页控件

本文主要用于介绍WPF基于MVVM实现自定义分页控件的代码实现。

主要功能: 自定义页码,当前页/总页数, 上一页,下一页,返回首页,返回最后一页, 利用FontAwesome作为FontFamily实现icon的功能。

本代码中的ViewModelBase的实现请参照另一篇文章:WPF MVVM ViewModelBase(造轮子实现类似MVVMLight的功能)

1. 创建PaginationControl

1.1 Color Brush
xaml 复制代码
<SolidColorBrush x:Key="HighlightBrush" Color="#E84315" />
 <SolidColorBrush x:Key="DisabledForegroundBrush" Color="#888" />
1.2 PaginationControl.xaml.cs

不用更改任何代码

1.3 PaginationControl.xaml
xaml 复制代码
<UserControl x:Class="MES.UI.Views.CustomControls.PaginationControl"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:i="http://schemas.microsoft.com/xaml/behaviors"
             xmlns:local="clr-namespace:MES.UI.Views.CustomControls"
             mc:Ignorable="d" Height="45">
    <UserControl.Resources>
        <Style x:Key="paginationTB" TargetType="{x:Type TextBlock}">
            <Setter Property="FontSize" Value="20"/>
            <Setter Property="FontFamily" Value="pack://application:,,,/Resources/#FontAwesome"/>
            <Setter Property="Margin" Value="5,15,5,10"/>
            <Setter Property="Foreground" Value="{StaticResource HighlightBrush}"/>
            <Setter Property="Cursor" Value="Hand"/>
            <Style.Triggers>
                <Trigger Property="IsEnabled" Value="True">
                    <Setter Property="Foreground" Value="{StaticResource HighlightBrush}"/>
                </Trigger>
                <Trigger Property="IsEnabled" Value="False">
                    <Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </UserControl.Resources>
    <StackPanel Orientation="Horizontal" VerticalAlignment="Center">
        <TextBlock Text="Page Size:" Margin="5,15,5,10"></TextBlock>
        <ComboBox x:Name="PageSizeComboBox" 
                  ItemsSource="{Binding PageSizes}" 
                  SelectedItem="{Binding PageSize}" 
                  Width="70"
                  VerticalAlignment="Center" 
                  HorizontalAlignment="Center"/>
        <TextBlock Style="{StaticResource paginationTB}" IsEnabled="{Binding EnabledPreviousButton}" Text="&#xf100;" x:Name="btnFirstPage" ToolTip="First Page">
           <i:Interaction.Triggers>
               <i:EventTrigger EventName="MouseLeftButtonDown">
                   <i:InvokeCommandAction Command="{Binding FirstPageCommand}"/>
               </i:EventTrigger>
           </i:Interaction.Triggers>
        </TextBlock>

        <TextBlock Style="{StaticResource paginationTB}" IsEnabled="{Binding EnabledPreviousButton}" Text="&#xf104;" x:Name="btnPrevious" ToolTip="Previouse">
              <i:Interaction.Triggers>
                  <i:EventTrigger EventName="MouseLeftButtonDown">
                      <i:InvokeCommandAction Command="{Binding PreviousPageCommand}"/>
                  </i:EventTrigger>
              </i:Interaction.Triggers>
        </TextBlock>

        <TextBlock Name="txtCurrentPageTextBlock" Text="{Binding CurrentPageText}"  Margin="5,15,5,10" VerticalAlignment="Center" />
        <TextBlock Style="{StaticResource paginationTB}" IsEnabled="{Binding EnabledNextButton}" Text="&#xf105;" x:Name="btnNext" ToolTip="Next" >
           <i:Interaction.Triggers>
               <i:EventTrigger EventName="MouseLeftButtonDown">
                   <i:InvokeCommandAction Command="{Binding NextPageCommand}"/>
               </i:EventTrigger>
           </i:Interaction.Triggers>
        </TextBlock>

        <TextBlock Style="{StaticResource paginationTB}" IsEnabled="{Binding EnabledNextButton}" Text="&#xf101;" x:Name="btnLastPage" ToolTip="Last Page" >
           <i:Interaction.Triggers>
               <i:EventTrigger EventName="MouseLeftButtonDown">
                   <i:InvokeCommandAction Command="{Binding LastPageCommand}"/>
               </i:EventTrigger>
           </i:Interaction.Triggers>
        </TextBlock>
    </StackPanel>
</UserControl>

2. 创建PaginationControlViewModel

csharp 复制代码
    /// <summary>
    /// Pagination Control ViewModel
    /// </summary>
    public class PaginationControlViewModel : ViewModelBase
    {
        #region Pagination paramters

        private int _currentPage;
        private int _totalItems;
        private int _pageSize = 1;

        public int TotalItems
        {
            get => _totalItems;
            set
            {
                _totalItems = value;
                OnPropertyChanged(nameof(TotalItems));
                OnPropertyChanged(nameof(TotalPages));
                OnPropertyChanged(nameof(CurrentPageText));
                SetButtonStatus();
            }
        }

        public int PageSize
        {
            get => _pageSize;
            set
            {
                _pageSize = value;
                OnPropertyChanged(nameof(PageSize));
                OnPageChanged();
            }
        }

        public int CurrentPage
        {
            get => _currentPage;
            set
            {
                if (_currentPage != value)
                {
                    _currentPage = value;
                    OnPropertyChanged(nameof(CurrentPage));
                    OnPageChanged();
                }
            }
        }

        public string CurrentPageText
        {
            get
            {
                return $"Page {CurrentPage} of {TotalPages}";
            }
        }

        public int TotalPages => (TotalItems + PageSize - 1) / PageSize;

        private ObservableCollection<int> _pageSizes;

        public ObservableCollection<int> PageSizes
        {
            get { return _pageSizes; }
            set
            {
                _pageSizes = value;
                OnPropertyChanged(nameof(PageSizes));
            }
        }

        private bool _enabledPreviousButton;

        public bool EnabledPreviousButton
        {
            get { return _enabledPreviousButton; }
            set { _enabledPreviousButton = value; OnPropertyChanged(nameof(EnabledPreviousButton)); }
        }


        private bool _enabledNextButton;

        public bool EnabledNextButton
        {
            get { return _enabledNextButton; }
            set { _enabledNextButton = value; OnPropertyChanged(nameof(EnabledNextButton)); }
        }


        #endregion


        public ICommand FirstPageCommand { get; }
        public ICommand PreviousPageCommand { get; }
        public ICommand NextPageCommand { get; }
        public ICommand LastPageCommand { get; }

        public event EventHandler PageChanged;

        private bool isInitialized = false;// avoid call OnPageChanged in initial
        public PaginationControlViewModel()
        {
            _pageSizes = new ObservableCollection<int> { 1, 20, 50, 100, 200, 300, 500 };
            CurrentPage = 1; // Initialize to the first page
            PageSize = AgencyCRMConstants.DefaultPageSize;
            FirstPageCommand = new RelayCommand(_ => CurrentPage = 1);
            PreviousPageCommand = new RelayCommand(_ => { if (CurrentPage > 1) CurrentPage--; });
            NextPageCommand = new RelayCommand(_ => { if (CurrentPage < TotalPages) CurrentPage++; });
            LastPageCommand = new RelayCommand(_ => CurrentPage = TotalPages);
            isInitialized = true;
        }

        protected virtual void OnPageChanged()
        {
            if (isInitialized)
            {
                PageChanged?.Invoke(this, EventArgs.Empty);
            }
        }

        private void SetButtonStatus()
        {
            if (isInitialized)
            {
                if (CurrentPage > 1)
                {
                    EnabledPreviousButton = true;
                }
                else
                {
                    EnabledPreviousButton = false;
                }
                if (CurrentPage >= 1 && CurrentPage < TotalPages)
                {
                    EnabledNextButton = true;
                }
                else
                {
                    EnabledNextButton = false;
                }
            }
            else
            {
                EnabledNextButton = false;
                EnabledPreviousButton = false;
            }
        }
    }

3. 调用

3.1 在CompanyListViewModel中引用PaginationControlViewModel
csharp 复制代码
public class CompanyListViewModel : ViewModelBase, IViewModelDispose
{
    #region Agency.CRM API Urls

    private const string GetByCompanyNameAsync = "api/Company/GetByCompanyName?CompanyName={0}&pageNumber={1}&pageSize={2}";

    #endregion

    #region Models

    private readonly IMESHttpClientService _mesHttpClientService;
    private BackgroundWorker _backgroundWorker;
    public PaginationControlViewModel PaginationViewModel;

    private string? _CompanyName;
    public string? CompanyName
    {
        get { return _CompanyName; }
        set
        {
            _CompanyName = value;
            OnPropertyChanged(nameof(CompanyName));
        }
    }


    private bool _isEnableExport;
    public bool IsEnableExport
    {
        get { return _isEnableExport; }
        set
        {
            _isEnableExport = value;
            OnPropertyChanged(nameof(IsEnableExport));
        }
    }

    private CompanyDto? _selectedCompany;
    public CompanyDto? SelectedCompany
    {
        get { return _selectedCompany; }
        set
        {
            _selectedCompany = value;
            OnPropertyChanged(nameof(SelectedCompany));
        }
    }

    private ObservableCollection<CompanyDto> _CompanyList;

    public ObservableCollection<CompanyDto> CompanyList
    {
        get { return _CompanyList; }
        set
        {
            _CompanyList = value;
            OnPropertyChanged(nameof(CompanyList));
        }
    }

    private int pageSize = MESConstants.DefaultPageSize;
    private int currentPage = 1;


    #endregion


    #region ctor

    public CompanyListViewModel()
    {
        _mesHttpClientService = NInjectBase.Kernel.Get<IMESHttpClientService>();

        _CompanyList = new();

        _backgroundWorker = new BackgroundWorker();
        _backgroundWorker.DoWork += BackgroundWorker_DoWork;
        _backgroundWorker.RunWorkerCompleted += BackgroundWorker_RunWorkerCompleted;

        InitialParameters();
    }

    private void InitialParameters()
    {
        _CompanyName = string.Empty;
        PaginationViewModel = new PaginationControlViewModel();
        PaginationViewModel.PageSize = MESConstants.DefaultPageSize;
        _selectedCompany = new();
        PaginationInitial();
    }

    #endregion


    #region ICommands

    public ICommand? RefreshRecordsCommand
    {
        get
        {
            return new DelegateCommand(() =>
            {
                RefreshRecords();
            });
        }
    }

    public ICommand? ResetFilterCommand
    {
        get
        {
            return new DelegateCommand(() =>
            {
                ResetFilterFunction();
            });
        }
    }

    #endregion


    #region Functions

    private void ResetFilterFunction()
    {
        InitialParameters();
        RefreshRecords();
    }

    public void RefreshRecords()
    {
        AppEvents.Instance.StartProgressBar(this, new ProgressBarDisplayEventArgs(AppConstants.ProgressBarMessages.Generic.LOADING));

        _backgroundWorker.RunWorkerAsync(new List<string>() { CompanyName, currentPage.ToString(), pageSize.ToString() });
    }

    private void BackgroundWorker_RunWorkerCompleted(object? sender, RunWorkerCompletedEventArgs e)
    {
        if (e.Error == null)
        {
            var result = e.Result as PagedResultDto<CompanyDto>;
            if (result != null)
            {
                int.TryParse(result.TotalCount.ToString(), out var totalCount);
                PaginationViewModel.TotalItems = totalCount;
                CompanyList = result.Items;
                this.IsEnableExport = CompanyList?.Count > 0;
            }
            else
            {
                this.IsEnableExport = false;
            }
        }
        AppEvents.Instance.StopProgressBar(this);
    }

    private void BackgroundWorker_DoWork(object? sender, DoWorkEventArgs e)
    {
        try
        {
            var parameters = e.Argument as List<string>;
            if (parameters == null || parameters.Count != 3) return;
            using var httpClient = _mesHttpClientService.GetHttpClient();
            string urlWithParameters = string.Format(GetByCompanyNameAsync, parameters[0], parameters[1], parameters[2]);
            var response = httpClient.GetAsync(urlWithParameters).Result;
            if (response.IsSuccessStatusCode)
            {
                var json = response.Content.ReadAsStringAsync().Result;
                var result = JsonConvert.DeserializeObject<PagedResultDto<CompanyDto>>(json);
                e.Result = result;
            }
        }
        catch (Exception ex)
        {
            Log.Error("Error loading CRM users", ex);
        }
    }

    public void DisposeRecordsAndEvents()
    {
        CompanyList = new();
        _backgroundWorker.DoWork -= BackgroundWorker_DoWork;
        _backgroundWorker.RunWorkerCompleted -= BackgroundWorker_RunWorkerCompleted;
        _backgroundWorker.Dispose();
        PaginationDispose();

    }

    #endregion


    #region Pagination

    private void PaginationInitial()
    {
        PaginationViewModel = new();
        PaginationViewModel.PageChanged += PaginationControlViewModel_PageChanged;
    }

    private void PaginationDispose()
    {
        PaginationViewModel.PageChanged -= PaginationControlViewModel_PageChanged;
    }

    private void PaginationControlViewModel_PageChanged(object? sender, EventArgs e)
    {
        pageSize = PaginationViewModel.PageSize;
        currentPage = PaginationViewModel.CurrentPage;

        RefreshRecords();
    }

    #endregion

}
3.2 CompanyList.xaml中添加PaginationControl
xaml 复制代码
 <CustomControls:PaginationControl x:Name="paginationControl"></CustomControls:PaginationControl>
3.3 CompanyList.xaml.cs中为PaginationControl指定DataContext
csharp 复制代码
private void UserControl_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
    paginationControl.DataContext = ViewModel.PaginationViewModel;
    ViewModel.RefreshRecords();
}

4. 最终实现效果

相关推荐
柒.梧.3 小时前
MyBatis一对多嵌套查询实战:SQL99式与分布式查询双视角解析
wpf
要记得喝水1 天前
某公司WPF面试题(含答案和解析)--3
wpf
zzyzxb2 天前
WPF中Adorner和Style异同
wpf
棉晗榜2 天前
WPF锚点页面,点击跳转到指定区域
wpf
zzyzxb2 天前
Style/Setter、Template 属性、ControlTemplate 三者的关系
wpf
要记得喝水2 天前
某公司WPF面试题(含答案和解析)--2
wpf
zzyzxb2 天前
WPF中Template、Style、Adorner异同
wpf
小股虫2 天前
数据一致性保障:从理论深度到架构实践的十年沉淀
架构·wpf
廋到被风吹走3 天前
【Spring】PlatformTransactionManager详解
java·spring·wpf