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>
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));
}
}
在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);
}
}
绑定到UI的数据源只是需要显示的数据,不会把所有数据都取出来。当选择显示页数时,只需要将新的数据源附上即可。