基于WPF系统框架设计(10)-分页控件设计
背景
最近要求项目组成员开发一个通用的分页组件,要求是这个组件简单易用,通用性,兼容现有框架MVVM模式,可是最后给我提交的成果勉强能够用,却欠少灵活性和框架兼容性。
设计的基本思想
传入数据源,总页数,当前页码,每页记录数,达到分页显示数据的功能。
优化
我把原本不支持MVVM的源码改善了一下,可能还可以再优化得好些,支持MVVM模式,较果如下图:
添加一解决方案:TLAgent.Pager
- 设计DataPager类,继承UserControl, INotifyPropertyChanged ,参考如下代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.ComponentModel; namespace TLAgent.Pager {
/// <summary>
/// DataPager.xaml 的交互逻辑
/// </summary>
public partial class DataPager : UserControl, INotifyPropertyChanged {
public DataPager() {
InitializeComponent();
} #region 依赖属性和事件
public int PageSize {
get { return (int)GetValue(PageSizeProperty); }
set { SetValue(PageSizeProperty, value); }
} // Using a DependencyProperty as the backing store for PageSize. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PageSizeProperty =
DependencyProperty.Register("PageSize", typeof(int), typeof(DataPager), new UIPropertyMetadata(10)); public int Total {
get { return (int)GetValue(TotalProperty); }
set { SetValue(TotalProperty, value); }
} // Using a DependencyProperty as the backing store for Total. This enables animation, styling, binding, etc...
public static readonly DependencyProperty TotalProperty =
DependencyProperty.Register("Total", typeof(int), typeof(DataPager), new UIPropertyMetadata(0)); public int PageIndex {
get { return (int)GetValue(PageIndexProperty); }
set { SetValue(PageIndexProperty, value); }
} // Using a DependencyProperty as the backing store for PageIndex. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PageIndexProperty =
DependencyProperty.Register("PageIndex", typeof(int), typeof(DataPager), new UIPropertyMetadata(1)); public string PageSizeList {
get { return (string)GetValue(PageSizeListProperty); }
set { SetValue(PageSizeListProperty, value); }
} // Using a DependencyProperty as the backing store for PageSizeList. This enables animation, styling, binding, etc...
public static readonly DependencyProperty PageSizeListProperty =
DependencyProperty.Register("PageSizeList", typeof(string), typeof(DataPager), new UIPropertyMetadata("5,10,20", (s, e) => {
DataPager dp = s as DataPager;
if (dp.PageSizeItems == null) dp.PageSizeItems = new List<int>();
else dp.PageSizeItems.Clear();
dp.RaisePropertyChanged("PageSizeItems");
})); public IEnumerable<object> ItemsSource {
get { return (IEnumerable<object>)GetValue(ItemsSourceProperty); }
set { SetValue(ItemsSourceProperty, value); }
} /// <summary>
/// ItemsSource数据源
/// </summary>
public static DependencyProperty ItemsSourceProperty =
DependencyProperty.Register("ItemsSource", typeof(IEnumerable<object>), typeof(DataPager), new UIPropertyMetadata(null)); public static readonly RoutedEvent PageChangedEvent = EventManager.RegisterRoutedEvent("PageChanged", RoutingStrategy.Bubble, typeof(PageChangedEventHandler), typeof(DataPager));
/// <summary>
/// 分页更改事件
/// </summary>
public event PageChangedEventHandler PageChanged {
add {
AddHandler(PageChangedEvent, value);
}
remove {
RemoveHandler(PageChangedEvent, value);
}
}
#endregion
public ICommand PageChangedCommand { get; set; } #region 通知属性
private List<int> _pageSizeItems;
/// <summary>
/// 显示每页记录数集合
/// </summary>
public List<int> PageSizeItems {
get {
if (_pageSizeItems == null) {
_pageSizeItems = new List<int>();
}
if (PageSizeList != null) {
List<string> strs = PageSizeList.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToList();
_pageSizeItems.Clear();
strs.ForEach(c => {
_pageSizeItems.Add(Convert.ToInt32(c));
});
}
return _pageSizeItems;
}
set {
if (_pageSizeItems != value) {
_pageSizeItems = value;
RaisePropertyChanged("PageSizeItems");
}
}
} private int _pageCount;
/// <summary>
/// 总页数
/// </summary>
public int PageCount {
get { return _pageCount; }
set {
if (_pageCount != value) {
_pageCount = value;
RaisePropertyChanged("PageCount");
}
}
} private int _start;
/// <summary>
/// 开始记录数
/// </summary>
public int Start {
get { return _start; }
set {
if (_start != value) {
_start = value;
RaisePropertyChanged("Start");
}
}
} private int _end;
/// <summary>
/// 结束记录数
/// </summary>
public int End {
get { return _end; }
set {
if (_end != value) {
_end = value;
RaisePropertyChanged("End");
}
}
} public event PropertyChangedEventHandler PropertyChanged;
public void RaisePropertyChanged(string propertyName) {
if (PropertyChanged != null) {
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
#endregion #region 字段、属性、委托
public delegate void PageChangedEventHandler(object sender, PageChangedEventArgs args);
private PageChangedEventArgs pageChangedEventArgs;
#endregion #region 引发分页更改事件
/// <summary>
/// 引发分页更改事件
/// </summary>
private void RaisePageChanged() {
if (pageChangedEventArgs == null) {
pageChangedEventArgs = new PageChangedEventArgs(PageChangedEvent, PageSize, PageIndex);
} else {
pageChangedEventArgs.PageSize = this.PageSize;
pageChangedEventArgs.PageIndex = this.PageIndex;
}
RaiseEvent(pageChangedEventArgs);
//calc start、end
if (ItemsSource != null) {
int curCount = ItemsSource.Count();
Start = (PageIndex - 1) * PageSize + 1;
End = Start + curCount - 1; if (Total % PageSize != 0) {
PageCount = Total / PageSize + 1;
} else {
PageCount = Total / PageSize;
}
} else {
Start = End = PageCount = Total = 0;
} //调整图片的显示
btnFirst.IsEnabled = btnPrev.IsEnabled = (PageIndex != 1);
btnNext.IsEnabled = btnLast.IsEnabled = (PageIndex != PageCount);
}
#endregion #region 分页操作事件
void DataPager_Loaded(object sender, RoutedEventArgs e) {
RaisePageChanged();
} private void cbpPageSize_SelectionChanged(object sender, SelectionChangedEventArgs e) {
if (this.IsLoaded) {
PageSize = (int)cboPageSize.SelectedItem;
RaisePageChanged();
}
} private void btnFirst_Click(object sender, RoutedEventArgs e) {
PageIndex = 1;
RaisePageChanged();
} private void btnPrev_Click(object sender, RoutedEventArgs e) {
if (PageIndex > 1) {
--PageIndex;
}
RaisePageChanged();
} private void btnNext_Click(object sender, RoutedEventArgs e) {
if (Total % PageSize != 0) {
PageCount = Total / PageSize + 1;
} else {
PageCount = Total / PageSize;
} if (PageIndex < PageCount) {
++PageIndex;
}
RaisePageChanged();
} private void btnLast_Click(object sender, RoutedEventArgs e) {
if (Total % PageSize != 0) {
PageCount = Total / PageSize + 1;
} else {
PageCount = Total / PageSize;
}
PageIndex = PageCount;
RaisePageChanged();
}
private void btnRefresh_Click(object sender, RoutedEventArgs e) {
RaisePageChanged();
} private void tbPageIndex_PreviewKeyDown(object sender, KeyEventArgs e) {
if (e.Key == Key.Enter) {
tbPageIndex_LostFocus(sender, null);
}
} private void tbPageIndex_LostFocus(object sender, RoutedEventArgs e) {
int pIndex = 0;
try {
pIndex = Convert.ToInt32(tbPageIndex.Text);
} catch { pIndex = 1; } if (pIndex < 1) PageIndex = 1;
else if (pIndex > PageCount) PageIndex = PageCount;
else PageIndex = pIndex; RaisePageChanged();
}
#endregion } /// <summary>
/// 分页更改参数
/// </summary>
public class PageChangedEventArgs : RoutedEventArgs {
public int PageSize { get; set; }
public int PageIndex { get; set; } public PageChangedEventArgs(RoutedEvent routeEvent, int pageSize, int pageIndex)
: base(routeEvent) {
this.PageSize = pageSize;
this.PageIndex = pageIndex;
}
}
}
- 添加一支持泛型类,可以传入任何类型对象
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text; namespace TLAgent.Pager
{
public class DataResult<T> : INotifyPropertyChanged
{ public int _total;
public int Total
{
get
{
return _total;
}
set
{
if (_total != value)
{
_total = value;
RaisePropertyChanged("Total");
}
}
} private List<T> _dataSource;
public List<T> DataSource
{
get
{
return _dataSource;
}
set
{
if (_dataSource != value)
{
_dataSource = value;
RaisePropertyChanged("DataSource");
}
}
} public DataResult()
{
DataSource = new List<T>();
} public void RaisePropertyChanged(string propertyName)
{
if (PropertyChanged != null)
{
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
} public event PropertyChangedEventHandler PropertyChanged;
}
}
测试
添加一个测试项目:TLAgent.Pager.Test
- 自定义一个命令InteractiveCommand,支持传入参数,代码参考:
// -----------------------------------------------------------------------
// <copyright file="InteractiveCommand.cs" company="M&M">
// TODO: Update copyright text.
// </copyright>
// ----------------------------------------------------------------------- namespace TLAgent.Pager.Test
{
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Input;
using System.Windows.Interactivity;
using System.Reflection; /// <summary>
/// TODO: Update summary.
/// </summary>
public class InteractiveCommand : TriggerAction<DependencyObject>
{
protected override void Invoke(object parameter)
{
if (base.AssociatedObject != null)
{
ICommand command = this.ResolveCommand();
object[] tempObj = { parameter, CommandParameter };
if ((command != null) && command.CanExecute(tempObj))
{
command.Execute(tempObj);
}
}
} public object CommandParameter
{
get { return GetValue(CommandParameterProperty); }
set { SetValue(CommandParameterProperty, value); }
} public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.Register
("CommandParameter", typeof(object), typeof(InteractiveCommand), new PropertyMetadata(null, OnCommandParameterChanged)); private static void OnCommandParameterChanged
(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
InteractiveCommand ic = sender as InteractiveCommand;
if (ic != null)
{
ic.SynchronizeElementState();
}
} private void SynchronizeElementState()
{
ICommand command = Command;
if (command != null)
{
FrameworkElement associatedObject = AssociatedObject as FrameworkElement;
if (associatedObject != null)
{
associatedObject.IsEnabled = command.CanExecute(CommandParameter);
}
}
} private ICommand ResolveCommand()
{
ICommand command = null;
if (this.Command != null)
{
return this.Command;
}
if (base.AssociatedObject != null)
{
foreach (PropertyInfo info in base.AssociatedObject.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
{
if (typeof(ICommand).IsAssignableFrom(info.PropertyType) && string.Equals(info.Name, this.CommandName, StringComparison.Ordinal))
{
command = (ICommand)info.GetValue(base.AssociatedObject, null);
}
}
}
return command;
} private string commandName;
public string CommandName
{
get
{
base.ReadPreamble();
return this.commandName;
}
set
{
if (this.CommandName != value)
{
base.WritePreamble();
this.commandName = value;
base.WritePostscript();
}
}
} #region Command
public ICommand Command
{
get { return (ICommand)GetValue(CommandProperty); }
set { SetValue(CommandProperty, value); }
}
public static readonly DependencyProperty CommandProperty =
DependencyProperty.Register("Command", typeof(ICommand), typeof(InteractiveCommand), new UIPropertyMetadata(null));
#endregion }
}
在MainWindow.xaml中,添一DataGrid和一个DataPager,参考如下代码:
<Window x:Class="TLAgent.Pager.Test.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:lib="clr-namespace:TLAgent.Pager;assembly=TLAgent.Pager"
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
xmlns:cmd="clr-namespace:TLAgent.Pager.Test"
xmlns:viewModels="clr-namespace:TLAgent.Pager.Test.ViewModels"
Name="self"
Title="MainWindow" Height="350" Width="525">
<Window.DataContext>
<viewModels:MainWindowViewModel/>
</Window.DataContext> <Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<DataGrid ItemsSource="{Binding Path=ItemsSource,ElementName=dataPager}">
<!--<DataGrid.Columns>
<DataGridTextColumn Header="Name" Binding="{Binding Name}" />
<DataGridTextColumn Header="Age" Binding="{Binding Age}" />
<DataGridTextColumn Header="Gender" Binding="{Binding Gender}" />
</DataGrid.Columns>-->
</DataGrid>
<lib:DataPager Grid.Row="1" Name="dataPager" PageSizeList="10,20,30,50"
ItemsSource="{Binding Result.DataSource,Mode=TwoWay}"
Total="{Binding Result.Total,Mode=TwoWay}"
>
<i:Interaction.Triggers>
<i:EventTrigger EventName="PageChanged">
<cmd:InteractiveCommand Command="{Binding PageChangedCommand}" CommandName="PageChangedCommand"/>
</i:EventTrigger>
</i:Interaction.Triggers> </lib:DataPager>
</Grid>
</Window>添加一个ViewModel:MainWindowViewModel,继承NotificationObject:
using System.Collections.Generic;
using System.Linq;
using System.Windows.Input;
using Microsoft.Practices.Prism.Commands;
using Microsoft.Practices.Prism.ViewModel; namespace TLAgent.Pager.Test.ViewModels
{
public class MainWindowViewModel : NotificationObject
{ private DelegateCommand<object[]> _commandWithEventArgs;
public List<Student> _dataSource;//声明数据 public MainWindowViewModel()
{
Result = new DataResult<Student>();//此处Student可以传入任何数据类型
_dataSource = Controller.GetData();//获取得数据
Result.DataSource = _dataSource;//给数据源赋值
Query(10, 1);
} private DataResult<Student> _result;
public DataResult<Student> Result//公开给View作数据源绑定
{
get { return _result; } set
{
_result = value;
RaisePropertyChanged("Result");
}
} public void Query(int size, int pageIndex)
{
Result.Total = _dataSource.Count;//给页总数赋值
Result.DataSource = _dataSource.Skip((pageIndex - 1) * size).Take(size).ToList();//改变数据源赋值
}
/// <summary>
///
/// </summary>
public ICommand PageChangedCommand
{
get { return _commandWithEventArgs ?? (_commandWithEventArgs = new DelegateCommand<object[]>(ShowData)); }
} private void ShowData(object[] objParam)
{
PageChangedEventArgs args = (PageChangedEventArgs)objParam[0];//View把PageChangedEventArgs事件传过来,此事件带来页码和页序号
Query(args.PageSize, args.PageIndex);
}
}
}
添加一个Controller类,从此类中获取测试数据来源:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; namespace TLAgent.Pager.Test
{
public class Controller
{ public static List<Student> GetData()
{
List<Student> Students;
Students = new List<Student>(); Random random = new Random();
int count = random.Next(20, 200);
for (int i = 1; i <= count; i++)
{
Student stu = new Student
{
Name = "Name" + i,
Age = random.Next(20, 50),
Gender = (i % 3 != 0),
Desc = "Desc " + i,
};
Students.Add(stu);
}
return Students;
}
}
}
添加一个Student类,只是作为测试用,项目中可以按实际需要,DataResult<T>是支持多种类型的数据。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Practices.Prism.ViewModel; namespace TLAgent.Pager.Test {
public class Student : NotificationObject
{
public string Name { get; set; }
public int Age { get; set; }
public bool Gender { get; set; }
public string Desc { get; set; }
}
}
到此一个分页控件设计就完成了,在此做一些总结。
基于WPF系统框架设计(10)-分页控件设计的更多相关文章
- 基于WPF系统框架设计(5)-Ribbon整合Avalondock 2.0实现多文档界面设计(二)
AvalonDock 是一个.NET库,用于在停靠模式布局(docking)中排列一系列WPF/WinForm控件.最新发布的版本原生支持MVVM框架.Aero Snap特效并具有更好的性能. Ava ...
- 基于avalon+jquery做的bootstrap分页控件
刚开始学习avalon,项目需要就尝试写了个分页控件Pager.js:基于BootStrap样式这个大家都很熟悉 在这里推荐下国产前端神器avalon:确实好用,帮我解决了很多前端问题. 不多说了,代 ...
- WPF自定义控件与样式(10)-进度控件ProcessBar自定义样
一.前言 申明:WPF自定义控件与样式是一个系列文章,前后是有些关联的,但大多是按照由简到繁的顺序逐步发布的等,若有不明白的地方可以参考本系列前面的文章,文末附有部分文章链接. 本文主要内容: Pro ...
- 基于WPF系统框架设计(4)-Ribbon整合Avalondock 2.0实现多文档界面设计(一)
前些时间研究了WPF的一些框架,感觉基于Prism框架的MVVM模式对系统的UI与逻辑分离很好,所以就按照之前Winform的框架设计,用WPF做了一套,感觉比Winform要强很多. MVVM模式和 ...
- 基于WPF系统框架设计(6)-整合MVVM框架(Prism)
应用场景 我们基础的框架已经搭建起来了,现在整合MVVM框架Prism,在ViewModel做一些逻辑处理,真正把界面设计分离出来. 这样方便我们系统开发分工合作,同时提高系统可维护性和灵活性. 具体 ...
- 基于WPF系统框架设计(8)-PasswordBox传值到ViewMode
应用场景 我要做一个系统登录功能,需要传用户名和密码到ViewModel中,可是PasswordBox传值到ViewModel中好像跟TextBox等控件不一样.这里需要用到附加属性. 附加属性:一个 ...
- 基于WPF系统框架设计(7)-TextBox/PasswordBox在ViewModel中支持回车命令
应用场景 我现在做一个系统登录功能,要求在PasswordBox上输完密码后回车,能够响应Enter事件,并执行ViewModel中对应的方法.如果登录成功则隐藏当前窗口显示主窗体,登录失败则焦点返回 ...
- 基于WPF系统框架设计(3)-Fluent Ribbon界面布局
一个系统框架除了功能菜单导航,有系统内容显示区域,系统状态栏. Silver: Blue: Black: 系统界面设计,就不进行技术细节介绍了,主题以框架设计为主,Xaml源码参考: <Flue ...
- 基于WPF系统框架设计(9)-多值绑定之IMultiValueConverter
应用场景 我想把View层的一个布局控件和功能按钮传到ViewModel层,达到动态变更布局,同时灵活获取功能按钮的属性,让View和ViewModel完全分离,而不受View层影响. 最后我想到使用 ...
随机推荐
- 云计算之路-阿里云上:用上了开放缓存服务OCS
你知道在我们使用的云服务器中哪台最贵吗?跑memcached的缓存服务器(12G内存).你知道保证网站访问速度的功臣之一是谁吗?跑memcached的缓存服务器. 用云服务器这么高贵的内存跑memca ...
- Singleton模式类 【微软面试100题 第七十二题】
题目要求: 实现C++单例模式,即只能生成一个实例的类. 题目分析: 1.一般情况:用构造函数私有化和静态函数实现. 2.如果考虑内存泄露:用智能指针+一般情况方法. 3.如果考虑线程安全:加锁. 代 ...
- 微信小程序-----校园头条整体概括
1.项目需求 为了让在校师生可以更加方便的了解学校信息,从而合理的安排自己的时间,避免发生冲突和错过事件,通过小程序的便利性,可以达到随手一查的功能. 2.项目布局 3.效果展示 3.1登录 3.2首 ...
- APM简介
1.什么是APM APM (应用性能管理) - Application Performance Management & Monitoring在信息科学和系统控制领域,APM致力于监控和管理应 ...
- python学习-- Django进阶之路 model的 objects对象 转 json
# objects_to_json: 将 model对象 转化成 json# json_to_objects: 将 将反序列化的json 转为 model 对象 def json_field(fiel ...
- Leetcode 655.输出二叉树
输出二叉树 在一个 m*n 的二维字符串数组中输出二叉树,并遵守以下规则: 行数 m 应当等于给定二叉树的高度. 列数 n 应当总是奇数. 根节点的值(以字符串格式给出)应当放在可放置的第一行正中间. ...
- Leetcode 488.祖玛游戏
祖玛游戏 回忆一下祖玛游戏.现在桌上有一串球,颜色有红色(R),黄色(Y),蓝色(B),绿色(G),还有白色(W). 现在你手里也有几个球. 每一次,你可以从手里的球选一个,然后把这个球插入到一串球中 ...
- [oldboy-django][6其他]微信二维码扫码登录注册
http://blog.csdn.net/maerdym/article/details/46380309 http://blog.csdn.net/jiang1013nan/article/deta ...
- Map的常用方法keySet()、entrySet()
Map是java中的接口,Map.Entry是Map的一个内部接口. Map提供了一些常用方法,如keySet().entrySet()等方法,keySet()方法返回值是Map中key值的集合:en ...
- webform登陆界面样式丢失
本文摘抄自:http://blog.csdn.net/sssix/article/details/16945347 请阅读原文. Forms验证——登录界面样式实效? <authenticati ...


