WPF 实现 DataGrid/ListView 分页控件

简介: 原文:WPF 实现 DataGrid/ListView 分页控件在WPF中,通常会选用DataGrid/ListView进行数据展示,如果数据量不多,可以直接一个页面显示出来。如果数据量很大,2000条数据,一次性显示在一个页面中,不仅消耗资源,而且用户体验也很糟糕。
原文: WPF 实现 DataGrid/ListView 分页控件

在WPF中,通常会选用DataGrid/ListView进行数据展示,如果数据量不多,可以直接一个页面显示出来。如果数据量很大,2000条数据,一次性显示在一个页面中,不仅消耗资源,而且用户体验也很糟糕。这篇博客将介绍如何创建一个分页控件。

为了简单起见,这个分页控件目前只有 首页/上一页/下一页/末页/总页数/第几页 等功能。实现思路,首页/上一页/下一页/末页 这四个通过路由事件来实现,在使用时可以使用命令进行绑定,或者直接使用均可。总页数和第几页通过依赖属性来实现,使用时将页数进行绑定显示即可。示例代码如下:

Pager控件:

    <UserControl.Resources>
        <Style TargetType="{x:Type Button}">
            <Setter Property="Width" Value="22"/>
            <Setter Property="Height" Value="22"/>
        </Style>
    </UserControl.Resources>
    <Grid>
        <StackPanel Orientation="Horizontal">
            <Button x:Name="FirstPageButton" Margin="5,0" Click="FirstPageButton_Click">
                <Path Width="7" Height="10" Data="M0,0L0,10 M0,5L6,2 6,8 0,5" Stroke="Black" StrokeThickness="1" Fill="Black" VerticalAlignment="Center" HorizontalAlignment="Center" />
            </Button>
            <Button x:Name="PreviousPageButton" Margin="0,0,5,0" Click="PreviousPageButton_Click">
                <Path Width="8" Height="8" Data="M0,4L8,0 8,8z" Stroke="Black" Fill="Black" VerticalAlignment="Center" HorizontalAlignment="Center" />
            </Button>
            <TextBlock VerticalAlignment="Center">
                <Run Text="第"/>
                <Run x:Name="rCurrent" Text="0"/>
                <Run Text="页"/>
            </TextBlock>
            <Button Margin="5,0" x:Name="NextPageButton" Click="NextPageButton_Click">
                <Path Width="8" Height="8" Data="M0,4L8,0 8,8z" Stroke="Black" Fill="Black" VerticalAlignment="Center" HorizontalAlignment="Center">
                    <Path.RenderTransform>
                        <RotateTransform Angle="180" CenterX="4" CenterY="4" />
                    </Path.RenderTransform>
                </Path>
            </Button>
            <Button Margin="0,0,5,0" x:Name="LastPageButton" Click="LastPageButton_Click">
                <Path x:Name="MainPath" Width="7" Height="10" Data="M0,0L0,10 M0,5 L6,2 6,8 0,5" Stroke="Black" StrokeThickness="1" Fill="Black" VerticalAlignment="Center" HorizontalAlignment="Center">
                    <Path.RenderTransform>
                        <RotateTransform Angle="180" CenterX="3" CenterY="5" />
                    </Path.RenderTransform>
                </Path>
            </Button>
            <TextBlock VerticalAlignment="Center">
                <Run Text="共"/>
                <Run x:Name="rTotal" Text="0"/>
                <Run Text="页"/>
            </TextBlock>
        </StackPanel>     
    </Grid>

C#:

    public partial class Pager : UserControl
    {
        public static RoutedEvent FirstPageEvent;
        public static RoutedEvent PreviousPageEvent;
        public static RoutedEvent NextPageEvent;
        public static RoutedEvent LastPageEvent;

        public static readonly DependencyProperty CurrentPageProperty;
        public static readonly DependencyProperty TotalPageProperty;

        public string CurrentPage
        {
            get { return (string)GetValue(CurrentPageProperty); }
            set { SetValue(CurrentPageProperty, value); }
        }

        public string TotalPage
        {
            get { return (string)GetValue(TotalPageProperty); }
            set { SetValue(TotalPageProperty, value); }
        }

        public Pager()
        {
            InitializeComponent();
        }

        static Pager()
        {
            FirstPageEvent = EventManager.RegisterRoutedEvent("FirstPage", RoutingStrategy.Direct, typeof(RoutedEventHandler), typeof(Pager));
            PreviousPageEvent = EventManager.RegisterRoutedEvent("PreviousPage", RoutingStrategy.Direct, typeof(RoutedEventHandler), typeof(Pager));
            NextPageEvent = EventManager.RegisterRoutedEvent("NextPage", RoutingStrategy.Direct, typeof(RoutedEventHandler), typeof(Pager));
            LastPageEvent = EventManager.RegisterRoutedEvent("LastPage", RoutingStrategy.Direct, typeof(RoutedEventHandler), typeof(Pager));

            CurrentPageProperty = DependencyProperty.Register("CurrentPage", typeof(string), typeof(Pager), new PropertyMetadata(string.Empty,new PropertyChangedCallback(OnCurrentPageChanged)));
            TotalPageProperty = DependencyProperty.Register("TotalPage", typeof(string), typeof(Pager), new PropertyMetadata(string.Empty,new PropertyChangedCallback(OnTotalPageChanged)));
        }

        public event RoutedEventHandler FirstPage
        {
            add { AddHandler(FirstPageEvent, value); }
            remove { RemoveHandler(FirstPageEvent, value); }
        }

        public event RoutedEventHandler PreviousPage
        {
            add { AddHandler(PreviousPageEvent, value); }
            remove { RemoveHandler(PreviousPageEvent, value); }
        }

        public event RoutedEventHandler NextPage
        {
            add { AddHandler(NextPageEvent, value); }
            remove { RemoveHandler(NextPageEvent, value); }
        }

        public event RoutedEventHandler LastPage
        {
            add { AddHandler(LastPageEvent, value); }
            remove { RemoveHandler(LastPageEvent, value); }
        }

        public static void OnTotalPageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            Pager p = d as Pager;

            if(p != null)
            {
                Run rTotal = (Run)p.FindName("rTotal");

                rTotal.Text = (string)e.NewValue;
            }
        }

        private static void OnCurrentPageChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            Pager p = d as Pager;

            if(p != null)
            {
                Run rCurrrent = (Run)p.FindName("rCurrent");

                rCurrrent.Text = (string)e.NewValue;
            }
        }

        private void FirstPageButton_Click(object sender, RoutedEventArgs e)
        {
            RaiseEvent(new RoutedEventArgs(FirstPageEvent, this));
        }

        private void PreviousPageButton_Click(object sender, RoutedEventArgs e)
        {
            RaiseEvent(new RoutedEventArgs(PreviousPageEvent, this));
        }

        private void NextPageButton_Click(object sender, RoutedEventArgs e)
        {
            RaiseEvent(new RoutedEventArgs(NextPageEvent, this));
        }

        private void LastPageButton_Click(object sender, RoutedEventArgs e)
        {
            RaiseEvent(new RoutedEventArgs(LastPageEvent, this));
        }
    }
View Code

在MainWindow中,
XAML:

    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height="*"/>
            <RowDefinition Height="Auto"/>
        </Grid.RowDefinitions>

        <DataGrid Grid.Row="0" ItemsSource="{Binding FakeSource}" AutoGenerateColumns="False" CanUserAddRows="False">
            <DataGrid.Columns>
                <DataGridTextColumn Header="Item Id" Binding="{Binding Id}" Width="80"/>
                <DataGridTextColumn Header="Item Name" Binding="{Binding ItemName}" Width="180"/>
            </DataGrid.Columns>
        </DataGrid>

        <local:Pager TotalPage="{Binding TotalPage}"
                     CurrentPage="{Binding CurrentPage, Mode=TwoWay}" 
                     HorizontalAlignment="Center"
                     Grid.Row="1">
            <i:Interaction.Triggers>
                <i:EventTrigger EventName="FirstPage">
                    <i:InvokeCommandAction Command="{Binding FirstPageCommand}" />
                </i:EventTrigger>
                <i:EventTrigger EventName="PreviousPage">
                    <i:InvokeCommandAction Command="{Binding PreviousPageCommand}"/>
                </i:EventTrigger>
                <i:EventTrigger EventName="NextPage">
                    <i:InvokeCommandAction Command="{Binding NextPageCommand}" />
                </i:EventTrigger>
                <i:EventTrigger EventName="LastPage">
                    <i:InvokeCommandAction Command="{Binding LastPageCommand}"/>
                </i:EventTrigger>
            </i:Interaction.Triggers>
        </local:Pager>
    </Grid>

MainViewModel类:

public class MainViewModel : ViewModel
    {
        private ICommand _firstPageCommand;

        public ICommand FirstPageCommand
        {
            get
            {
                return _firstPageCommand;
            }

            set
            {
                _firstPageCommand = value;
            }
        }

        private ICommand _previousPageCommand;

        public ICommand PreviousPageCommand
        {
            get
            {
                return _previousPageCommand;
            }

            set
            {
                _previousPageCommand = value;
            }
        }

        private ICommand _nextPageCommand;

        public ICommand NextPageCommand
        {
            get
            {
                return _nextPageCommand;
            }

            set
            {
                _nextPageCommand = value;
            }
        }

        private ICommand _lastPageCommand;

        public ICommand LastPageCommand
        {
            get
            {
                return _lastPageCommand;
            }

            set
            {
                _lastPageCommand = value;
            }
        }

        private int _pageSize;

        public int PageSize
        {
            get
            {
                return _pageSize;
            }
            set
            {
                if(_pageSize != value)
                {
                    _pageSize = value;
                    OnPropertyChanged("PageSize");
                }
            }
        }

        private int _currentPage;

        public int CurrentPage
        {
            get
            {
                return _currentPage;
            }

            set
            {
                if(_currentPage != value)
                {
                    _currentPage = value;
                    OnPropertyChanged("CurrentPage");
                }
            }
        }

        private int _totalPage;

        public int TotalPage
        {
            get
            {
                return _totalPage;
            }

            set
            {
                if(_totalPage != value)
                {
                    _totalPage = value;
                    OnPropertyChanged("TotalPage");
                }
            }
        }

        private ObservableCollection<FakeDatabase> _fakeSoruce;

        public ObservableCollection<FakeDatabase> FakeSource
        {
            get
            {
                return _fakeSoruce;
            }
            set
            {
                if(_fakeSoruce != value)
                {
                    _fakeSoruce = value;
                    OnPropertyChanged("FakeSource");
                }
            }
        }

        private List<FakeDatabase> _source;

        public MainViewModel()
        {
            _currentPage = 1;

            _pageSize = 20;

            FakeDatabase fake = new FakeDatabase();

            _source = fake.GenerateFakeSource();

            _totalPage = _source.Count / _pageSize;

            _fakeSoruce = new ObservableCollection<FakeDatabase>();

            List<FakeDatabase> result = _source.Take(20).ToList();

            _fakeSoruce.Clear();

            _fakeSoruce.AddRange(result);

            _firstPageCommand = new DelegateCommand(FirstPageAction);

            _previousPageCommand = new DelegateCommand(PreviousPageAction);

            _nextPageCommand = new DelegateCommand(NextPageAction);

            _lastPageCommand = new DelegateCommand(LastPageAction);
        }

        private void FirstPageAction()
        {
            CurrentPage = 1;

            var result = _source.Take(_pageSize).ToList();

            _fakeSoruce.Clear();

            _fakeSoruce.AddRange(result);
        }

        private void PreviousPageAction()
        {
            if(CurrentPage == 1)
            {
                return;
            }

            List<FakeDatabase> result = new List<FakeDatabase>();

            if(CurrentPage == 2)
            {
                result = _source.Take(_pageSize).ToList();
            }
            else
            {
                result = _source.Skip((CurrentPage - 2) * _pageSize).Take(_pageSize).ToList();
            }

            _fakeSoruce.Clear();

            _fakeSoruce.AddRange(result);

            CurrentPage--;
        }

        private void NextPageAction()
        {
            if(CurrentPage == _totalPage)
            {
                return;
            }

            List<FakeDatabase> result = new List<FakeDatabase>();

            result = _source.Skip(CurrentPage * _pageSize).Take(_pageSize).ToList();

            _fakeSoruce.Clear();

            _fakeSoruce.AddRange(result);

            CurrentPage++;
        }

        private void LastPageAction()
        {
            CurrentPage = TotalPage;

            int skipCount = (_totalPage - 1) * _pageSize;
            int takeCount = _source.Count - skipCount;

            var result = _source.Skip(skipCount).Take(takeCount).ToList();

            _fakeSoruce.Clear();

            _fakeSoruce.AddRange(result);
        }
    }
View Code

绑定到UI的数据源只是需要显示的数据,不会把所有数据都取出来。当选择显示页数时,只需要将新的数据源附上即可。

总结:如果需要对该分页控件进行扩展,例如,增加每页显示条数功能,只需要在Pager控件中增加相应的依赖属性即可。

感谢您的阅读,代码点击这里下载。

目录
相关文章
|
4月前
|
C# 开发者 Windows
一款基于Fluent设计风格、现代化的WPF UI控件库
一款基于Fluent设计风格、现代化的WPF UI控件库
118 1
|
4月前
|
C# Windows
WPF中如何使用HandyCotrol控件库
WPF中如何使用HandyCotrol控件库
212 1
|
4月前
|
C# 前端开发 UED
WPF数据验证实战:内置控件与自定义规则,带你玩转前端数据验证,让你的应用程序更上一层楼!
【8月更文挑战第31天】在WPF应用开发中,数据验证是确保输入正确性的关键环节。前端验证能及时发现错误,提升用户体验和程序可靠性。本文对比了几种常用的WPF数据验证方法,并通过示例展示了如何使用内置验证控件(如`TextBox`)及自定义验证规则实现有效验证。内置控件结合`Validation`类可快速实现简单验证;自定义规则则提供了更灵活的复杂逻辑支持。希望本文能帮助开发者更好地进行WPF数据验证。
153 0
|
4月前
|
C# UED 定位技术
WPF控件大全:初学者必读,掌握控件使用技巧,让你的应用程序更上一层楼!
【8月更文挑战第31天】在WPF应用程序开发中,控件是实现用户界面交互的关键元素。WPF提供了丰富的控件库,包括基础控件(如`Button`、`TextBox`)、布局控件(如`StackPanel`、`Grid`)、数据绑定控件(如`ListBox`、`DataGrid`)等。本文将介绍这些控件的基本分类及使用技巧,并通过示例代码展示如何在项目中应用。合理选择控件并利用布局控件和数据绑定功能,可以提升用户体验和程序性能。
84 0
|
4月前
|
开发框架 前端开发 JavaScript
在WPF应用中实现DataGrid的分组显示,以及嵌套明细展示效果
在WPF应用中实现DataGrid的分组显示,以及嵌套明细展示效果
在WPF应用中实现DataGrid的分组显示,以及嵌套明细展示效果
|
4月前
|
前端开发 测试技术 C#
WPF/C#:在DataGrid中显示选择框
WPF/C#:在DataGrid中显示选择框
69 0
|
4月前
|
前端开发 C#
wpfui:一个开源免费具有现代化设计趋势的WPF控件库
wpfui:一个开源免费具有现代化设计趋势的WPF控件库
166 0
|
7月前
|
C# 开发者 Windows
基于Material Design风格开源、易用、强大的WPF UI控件库
基于Material Design风格开源、易用、强大的WPF UI控件库
401 0
|
7月前
|
C#
浅谈WPF之装饰器实现控件锚点
使用过visio的都知道,在绘制流程图时,当选择或鼠标移动到控件时,都会在控件的四周出现锚点,以便于修改大小,移动位置,或连接线等,那此功能是如何实现的呢?在WPF开发中,想要在控件四周实现锚点,可以通过装饰器来实现,今天通过一个简单的小例子,简述如何在WPF开发中,应用装饰器,仅供学习分享使用,如有不足之处,还请指正。
154 1
|
C# Windows
WPF技术之RichTextBox控件
WPF RichTextBox是Windows Presentation Foundation (WPF)中提供的一个强大的文本编辑控件,它可以显示富文本格式的文本,支持多种文本处理操作。
619 0