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#:
在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);
}
}
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); } }
绑定到UI的数据源只是需要显示的数据,不会把所有数据都取出来。当选择显示页数时,只需要将新的数据源附上即可。
总结:如果需要对该分页控件进行扩展,例如,增加每页显示条数功能,只需要在Pager控件中增加相应的依赖属性即可。
感谢您的阅读,代码点击这里下载。