稳扎稳打Silverlight(61) - 4.0通信之WCF RIA Services: 自定义服务端排序和分页
稳扎稳打Silverlight(61) - 4.0通信之WCF RIA Services: 自定义服务端排序和分页
作者:webabcd
介绍
Silverlight 4.0 之 WCF RIA Services:实现自定义的服务端排序和分页
在线DEMO
http://www.cnblogs.com/webabcd/archive/2010/08/09/1795417.html
示例
演示如何在 WCF RIA Services 框架中实现自定义的服务端排序和分页
1、服务端
MyDomainService.cs
代码
/*
* 一个 Domain Service 服务
*/
namespace Silverlight40.Web.Service
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Linq;
using System.ServiceModel.DomainServices.EntityFramework;
using System.ServiceModel.DomainServices.Hosting;
using System.ServiceModel.DomainServices.Server;
using Silverlight40.Web.Model;
/*
* 用 LinqToEntities 实现 Domain Service 则继承 LinqToSqlDomainService<NorthwindEntities>;用 LinqToSql 实现 Domain Service 则继承 LinqToSqlDomainService<NorthwindEntities>
* Domain Service 内所有对客户端可见的方法都应该是 public 的,Domain Service 内的方法不支持重载
* 对客户端可见的方法要满足命名约定,或为其指定对应的 Attribute。当前支持 6 种数据操作:Query, Update, Insert, Delete, Invoke, Named Update, 详见文档
*
* [EnableClientAccess()] - 该类对客户端可见
* [EnableClientAccess(RequiresSecureEndpoint = true)] - 使用 HTTPS 协议
* [Ignore] - 指定的方法不作为服务而公开
* [Query(IsComposable=true)] - 支持客户端的 linq 查询,而且此查询会被在服务端执行
*
* 在多个 Domain Services 间共享实体:通过 [ExternalReference], [Association()], Context.AddReference() 实现,详见文档
*/
// 服务端的类名为:MyDomainService,则其生成的客户端上下文的类名为:MyDomainContext
[EnableClientAccess()]
public class MyDomainService : LinqToEntitiesDomainService<NorthwindEntities>
{
[Query(IsDefault = true)]
public IQueryable<Category> GetCategories()
{
return this.ObjectContext.Categories;
}
public void InsertCategory(Category category)
{
if ((category.EntityState != EntityState.Detached))
{
this.ObjectContext.ObjectStateManager.ChangeObjectState(category, EntityState.Added);
}
else
{
this.ObjectContext.Categories.AddObject(category);
}
}
public void UpdateCategory(Category currentCategory)
{
this.ObjectContext.Categories.AttachAsModified(currentCategory, this.ChangeSet.GetOriginal(currentCategory));
}
public void DeleteCategory(Category category)
{
if ((category.EntityState == EntityState.Detached))
{
this.ObjectContext.Categories.Attach(category);
}
this.ObjectContext.Categories.DeleteObject(category);
}
[Query(IsDefault = true)]
public IQueryable<Product> GetProducts()
{
return this.ObjectContext.Products;
}
public IQueryable<Product> GetProductsBySort(string sort)
{
return ObjectContext.Products.OrderBy(sort);
}
public void InsertProduct(Product product)
{
if ((product.EntityState != EntityState.Detached))
{
this.ObjectContext.ObjectStateManager.ChangeObjectState(product, EntityState.Added);
}
else
{
this.ObjectContext.Products.AddObject(product);
}
}
public void UpdateProduct(Product currentProduct)
{
this.ObjectContext.Products.AttachAsModified(currentProduct, this.ChangeSet.GetOriginal(currentProduct));
}
public void DeleteProduct(Product product)
{
if ((product.EntityState == EntityState.Detached))
{
this.ObjectContext.Products.Attach(product);
}
this.ObjectContext.Products.DeleteObject(product);
}
public IQueryable<Product> GetProductsByCategoryId(int categoryId)
{
return this.ObjectContext.Products.Where(p => p.CategoryID == categoryId);
}
// 通过验证的并且角色属于“管理员”的用户才有权限调用此方法
[Invoke]
[RequiresAuthentication()]
[RequiresRole("管理员")]
public string GetAuthenticationStatus()
{
return "看到了这条信息,就证明你有权限";
}
}
}
* 一个 Domain Service 服务
*/
namespace Silverlight40.Web.Service
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Data;
using System.Linq;
using System.ServiceModel.DomainServices.EntityFramework;
using System.ServiceModel.DomainServices.Hosting;
using System.ServiceModel.DomainServices.Server;
using Silverlight40.Web.Model;
/*
* 用 LinqToEntities 实现 Domain Service 则继承 LinqToSqlDomainService<NorthwindEntities>;用 LinqToSql 实现 Domain Service 则继承 LinqToSqlDomainService<NorthwindEntities>
* Domain Service 内所有对客户端可见的方法都应该是 public 的,Domain Service 内的方法不支持重载
* 对客户端可见的方法要满足命名约定,或为其指定对应的 Attribute。当前支持 6 种数据操作:Query, Update, Insert, Delete, Invoke, Named Update, 详见文档
*
* [EnableClientAccess()] - 该类对客户端可见
* [EnableClientAccess(RequiresSecureEndpoint = true)] - 使用 HTTPS 协议
* [Ignore] - 指定的方法不作为服务而公开
* [Query(IsComposable=true)] - 支持客户端的 linq 查询,而且此查询会被在服务端执行
*
* 在多个 Domain Services 间共享实体:通过 [ExternalReference], [Association()], Context.AddReference() 实现,详见文档
*/
// 服务端的类名为:MyDomainService,则其生成的客户端上下文的类名为:MyDomainContext
[EnableClientAccess()]
public class MyDomainService : LinqToEntitiesDomainService<NorthwindEntities>
{
[Query(IsDefault = true)]
public IQueryable<Category> GetCategories()
{
return this.ObjectContext.Categories;
}
public void InsertCategory(Category category)
{
if ((category.EntityState != EntityState.Detached))
{
this.ObjectContext.ObjectStateManager.ChangeObjectState(category, EntityState.Added);
}
else
{
this.ObjectContext.Categories.AddObject(category);
}
}
public void UpdateCategory(Category currentCategory)
{
this.ObjectContext.Categories.AttachAsModified(currentCategory, this.ChangeSet.GetOriginal(currentCategory));
}
public void DeleteCategory(Category category)
{
if ((category.EntityState == EntityState.Detached))
{
this.ObjectContext.Categories.Attach(category);
}
this.ObjectContext.Categories.DeleteObject(category);
}
[Query(IsDefault = true)]
public IQueryable<Product> GetProducts()
{
return this.ObjectContext.Products;
}
public IQueryable<Product> GetProductsBySort(string sort)
{
return ObjectContext.Products.OrderBy(sort);
}
public void InsertProduct(Product product)
{
if ((product.EntityState != EntityState.Detached))
{
this.ObjectContext.ObjectStateManager.ChangeObjectState(product, EntityState.Added);
}
else
{
this.ObjectContext.Products.AddObject(product);
}
}
public void UpdateProduct(Product currentProduct)
{
this.ObjectContext.Products.AttachAsModified(currentProduct, this.ChangeSet.GetOriginal(currentProduct));
}
public void DeleteProduct(Product product)
{
if ((product.EntityState == EntityState.Detached))
{
this.ObjectContext.Products.Attach(product);
}
this.ObjectContext.Products.DeleteObject(product);
}
public IQueryable<Product> GetProductsByCategoryId(int categoryId)
{
return this.ObjectContext.Products.Where(p => p.CategoryID == categoryId);
}
// 通过验证的并且角色属于“管理员”的用户才有权限调用此方法
[Invoke]
[RequiresAuthentication()]
[RequiresRole("管理员")]
public string GetAuthenticationStatus()
{
return "看到了这条信息,就证明你有权限";
}
}
}
MyDomainService.metadata.cs
代码
/*
* [MetadataTypeAttribute()] - 指定类的元数据对象
* [Include] - 生成的客户端上下文包含此字段
* [Exclude] - 生成的客户端上下文不包含此字段
* [Composition] - 指定字段为关联数据,即父实体增删改查时,此关联数据也会做相应的变化。指定此 Attribute 后,需显式指定其为 [Include]
* 注:如果使用 DomainDataSource 则不能将字段设置为 [Composition]
* [Editable(true)], [Editable(false)] - 指定字段是否可编辑
*
* 支持 Data Annotation 方式的数据验证:DataTypeAttribute(具有很多常用的数据类型的验证), RangeAttribute, RegularExpressionAttribute, RequiredAttribute, StringLengthAttribute 等
* 注:如果需要将自定义的验证既作用于服务端又作用于客户端,则需要把自定义的验证逻辑代码设置为 shared 模式
*/
namespace Silverlight40.Web.Model
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Data.Objects.DataClasses;
using System.Linq;
using System.ServiceModel.DomainServices.Hosting;
using System.ServiceModel.DomainServices.Server;
using Silverlight40.Web.Service.Validation;
[MetadataTypeAttribute(typeof(Category.CategoryMetadata))]
[CustomValidation(typeof(CategoryValidator), "ValidateCategory")] // 演示通过自定义的实体验证器来实现验证
public partial class Category
{
internal sealed class CategoryMetadata
{
private CategoryMetadata()
{
}
[Key()]
public int CategoryID { get; set; }
[Display(Name = "类别名称")] // 显示用
[Numeric(ErrorMessage = "字段“{0}”必须是数字")] // 演示通过继承 RegularExpressionAttribute 实现字段的自定义验证
[StartsWith(ErrorMessage = "{0}")] // 演示通过继承 ValidationAttribute 实现字段的自定义验证
[CustomValidation(typeof(EndsWidthValidator), "ValidateCategoryName")] // 演示通过自定义的字段验证器来实现验证
public string CategoryName { get; set; }
public string Description { get; set; }
[Exclude]
public byte[] Picture { get; set; }
[Include]
[Composition]
public EntityCollection<Product> Products { get; set; }
}
}
[MetadataTypeAttribute(typeof(Product.ProductMetadata))]
public partial class Product
{
internal sealed class ProductMetadata
{
private ProductMetadata()
{
}
public Category Category { get; set; }
public Nullable<int> CategoryID { get; set; }
public bool Discontinued { get; set; }
public EntityCollection<Order_Detail> Order_Details { get; set; }
[Display(Order = 0, Name = "产品ID")]
public int ProductID { get; set; }
[Display(Order = 1, Name = "产品名称")]
public string ProductName { get; set; }
public string QuantityPerUnit { get; set; }
public Nullable<short> ReorderLevel { get; set; }
public Supplier Supplier { get; set; }
public Nullable<int> SupplierID { get; set; }
public Nullable<decimal> UnitPrice { get; set; }
public Nullable<short> UnitsInStock { get; set; }
public Nullable<short> UnitsOnOrder { get; set; }
}
}
}
* [MetadataTypeAttribute()] - 指定类的元数据对象
* [Include] - 生成的客户端上下文包含此字段
* [Exclude] - 生成的客户端上下文不包含此字段
* [Composition] - 指定字段为关联数据,即父实体增删改查时,此关联数据也会做相应的变化。指定此 Attribute 后,需显式指定其为 [Include]
* 注:如果使用 DomainDataSource 则不能将字段设置为 [Composition]
* [Editable(true)], [Editable(false)] - 指定字段是否可编辑
*
* 支持 Data Annotation 方式的数据验证:DataTypeAttribute(具有很多常用的数据类型的验证), RangeAttribute, RegularExpressionAttribute, RequiredAttribute, StringLengthAttribute 等
* 注:如果需要将自定义的验证既作用于服务端又作用于客户端,则需要把自定义的验证逻辑代码设置为 shared 模式
*/
namespace Silverlight40.Web.Model
{
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Data.Objects.DataClasses;
using System.Linq;
using System.ServiceModel.DomainServices.Hosting;
using System.ServiceModel.DomainServices.Server;
using Silverlight40.Web.Service.Validation;
[MetadataTypeAttribute(typeof(Category.CategoryMetadata))]
[CustomValidation(typeof(CategoryValidator), "ValidateCategory")] // 演示通过自定义的实体验证器来实现验证
public partial class Category
{
internal sealed class CategoryMetadata
{
private CategoryMetadata()
{
}
[Key()]
public int CategoryID { get; set; }
[Display(Name = "类别名称")] // 显示用
[Numeric(ErrorMessage = "字段“{0}”必须是数字")] // 演示通过继承 RegularExpressionAttribute 实现字段的自定义验证
[StartsWith(ErrorMessage = "{0}")] // 演示通过继承 ValidationAttribute 实现字段的自定义验证
[CustomValidation(typeof(EndsWidthValidator), "ValidateCategoryName")] // 演示通过自定义的字段验证器来实现验证
public string CategoryName { get; set; }
public string Description { get; set; }
[Exclude]
public byte[] Picture { get; set; }
[Include]
[Composition]
public EntityCollection<Product> Products { get; set; }
}
}
[MetadataTypeAttribute(typeof(Product.ProductMetadata))]
public partial class Product
{
internal sealed class ProductMetadata
{
private ProductMetadata()
{
}
public Category Category { get; set; }
public Nullable<int> CategoryID { get; set; }
public bool Discontinued { get; set; }
public EntityCollection<Order_Detail> Order_Details { get; set; }
[Display(Order = 0, Name = "产品ID")]
public int ProductID { get; set; }
[Display(Order = 1, Name = "产品名称")]
public string ProductName { get; set; }
public string QuantityPerUnit { get; set; }
public Nullable<short> ReorderLevel { get; set; }
public Supplier Supplier { get; set; }
public Nullable<int> SupplierID { get; set; }
public Nullable<decimal> UnitPrice { get; set; }
public Nullable<short> UnitsInStock { get; set; }
public Nullable<short> UnitsOnOrder { get; set; }
}
}
}
2、客户端
ReloadEventArgs.cs
代码
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel;
namespace Silverlight40.WCFRIAServices.CustomSortingPaging
{
// 刷新事件的事件参数
public class ReloadEventArgs : EventArgs
{
public SortDescriptionCollection SortDescriptions { get; set; }
}
}
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel;
namespace Silverlight40.WCFRIAServices.CustomSortingPaging
{
// 刷新事件的事件参数
public class ReloadEventArgs : EventArgs
{
public SortDescriptionCollection SortDescriptions { get; set; }
}
}
SortableCollectionView.cs
代码
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Collections.Generic;
using System.Linq;
namespace Silverlight40.WCFRIAServices.CustomSortingPaging
{
// 为了实现排序功能,所以需要实现 ICollectionView 接口(仅实现其中的排序功能部分)
// 为了集合具有改变通知的功能,所以需要继承自 ObservableCollection<T>
public class SortableCollectionView<T> : ObservableCollection<T>, ICollectionView
{
public SortableCollectionView()
{
_currentItem = null;
_currentPosition = -1;
}
protected override void ClearItems()
{
base.ClearItems();
// 触发 CurrentChanging 事件
OnCurrentChanging();
}
public bool CanFilter
{
get { return false; }
}
public bool CanGroup
{
get { return false; }
}
// 允许通过 SortDescriptions 属性进行排序
public bool CanSort
{
get { return true; }
}
public System.Globalization.CultureInfo Culture { get; set; }
// 当前项
private object _currentItem;
public object CurrentItem
{
get { return _currentItem; }
}
// 当前项的索引
private int _currentPosition;
public int CurrentPosition
{
get { return _currentPosition; }
}
public Predicate<object> Filter { get; set; }
public ObservableCollection<GroupDescription> GroupDescriptions
{
get { return null; }
}
public ReadOnlyObservableCollection<object> Groups
{
get { return null; }
}
// 当前项是否超出了集合的末尾
public bool IsCurrentAfterLast
{
get
{
if (!IsEmpty)
return CurrentPosition >= base.Count;
return true;
}
}
// 当前项是否超出了集合的开头
public bool IsCurrentBeforeFirst
{
get
{
if (!IsEmpty)
return CurrentPosition < 0;
return true;
}
}
// 集合是否为空
public bool IsEmpty
{
get { return base.Count == 0; }
}
// System.ComponentModel.SortDescription 类型对象的集合,其用于描述集合中的数据的排序方式
private SortDescriptionCollection _sortDescriptions;
public SortDescriptionCollection SortDescriptions
{
get
{
if (_sortDescriptions == null)
_sortDescriptions = new SortDescriptionCollection();
return _sortDescriptions;
}
}
public System.Collections.IEnumerable SourceCollection
{
get { return this; }
}
// 当前项更改后所触发的事件
public event EventHandler CurrentChanged;
// 当前项准备更改之前所触发的事件
public event CurrentChangingEventHandler CurrentChanging;
// 指定的项是否属于此集合
public bool Contains(object item)
{
return Contains((T)item);
}
// 延迟刷新
public IDisposable DeferRefresh()
{
return new DeferRefreshHelper(() => Refresh());
}
private class DeferRefreshHelper : IDisposable
{
private Action _callback;
public DeferRefreshHelper(Action callback)
{
_callback = callback;
}
public void Dispose()
{
_callback();
}
}
// 将指定项设置为当前项
public bool MoveCurrentTo(object item)
{
if (object.Equals(CurrentItem, item) && (item != null || IsCurrentInView))
return IsCurrentInView;
int index = base.IndexOf((T)item);
return MoveCurrentToPosition(index);
}
// 将第一项设置为当前项
public bool MoveCurrentToFirst()
{
return MoveCurrentToPosition(0);
}
// 将最后一项设置为当前项
public bool MoveCurrentToLast()
{
return MoveCurrentToPosition(base.Count - 1);
}
// 将当前项的下一项设置为当前项
public bool MoveCurrentToNext()
{
return CurrentPosition < base.Count && MoveCurrentToPosition(CurrentPosition + 1);
}
// 将指定位置的项设置为当前项
public bool MoveCurrentToPosition(int position)
{
if (position < -1 || position > base.Count)
throw new Exception("参数“position”不在范围内");
if (position != CurrentPosition)
{
bool isCurrentAfterLast = IsCurrentAfterLast;
bool isCurrentBeforeFirst = IsCurrentBeforeFirst;
OnCurrentChanging();
ChangeCurrentToPosition(position);
OnCurrentChanged();
if (IsCurrentAfterLast != isCurrentAfterLast)
OnPropertyChanged("IsCurrentAfterLast");
if (IsCurrentBeforeFirst != isCurrentBeforeFirst)
OnPropertyChanged("IsCurrentBeforeFirst");
OnPropertyChanged("CurrentPosition");
OnPropertyChanged("CurrentItem");
}
return IsCurrentInView;
}
// 将当前项的上一项设置为当前项
public bool MoveCurrentToPrevious()
{
return CurrentPosition >= 0 && MoveCurrentToPosition(CurrentPosition - 1);
}
public event EventHandler<ReloadEventArgs> Reload;
// 刷新集合
public void Refresh()
{
if (Reload != null)
Reload(this, new ReloadEventArgs() { SortDescriptions = SortDescriptions });
}
protected virtual void OnCurrentChanged()
{
if (CurrentChanged != null)
CurrentChanged(this, EventArgs.Empty);
}
protected virtual void OnCurrentChanging(CurrentChangingEventArgs args)
{
if (args == null)
throw new Exception("参数“args”不能为 null");
if (CurrentChanging != null)
CurrentChanging(this, args);
}
protected void OnCurrentChanging()
{
_currentPosition = -1;
OnCurrentChanging(new CurrentChangingEventArgs(false));
}
protected virtual void OnPropertyChanged(string propertyName)
{
base.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
// 获取集合内指定位置的对象
private object GetItemAt(int index)
{
if (index >= 0 && index < base.Count)
return this[index];
return null;
}
// 当前项是否在集合的范围之内
private bool IsCurrentInView
{
get { return CurrentPosition >= 0 && CurrentPosition < base.Count; }
}
// 将指定位置的项设置为当前项
private void ChangeCurrentToPosition(int position)
{
if (position < 0)
{
_currentItem = null;
_currentPosition = -1;
}
else if (position >= base.Count)
{
_currentItem = null;
_currentPosition = base.Count;
}
else
{
_currentItem = this[position];
_currentPosition = position;
}
}
}
}
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Collections.Generic;
using System.Linq;
namespace Silverlight40.WCFRIAServices.CustomSortingPaging
{
// 为了实现排序功能,所以需要实现 ICollectionView 接口(仅实现其中的排序功能部分)
// 为了集合具有改变通知的功能,所以需要继承自 ObservableCollection<T>
public class SortableCollectionView<T> : ObservableCollection<T>, ICollectionView
{
public SortableCollectionView()
{
_currentItem = null;
_currentPosition = -1;
}
protected override void ClearItems()
{
base.ClearItems();
// 触发 CurrentChanging 事件
OnCurrentChanging();
}
public bool CanFilter
{
get { return false; }
}
public bool CanGroup
{
get { return false; }
}
// 允许通过 SortDescriptions 属性进行排序
public bool CanSort
{
get { return true; }
}
public System.Globalization.CultureInfo Culture { get; set; }
// 当前项
private object _currentItem;
public object CurrentItem
{
get { return _currentItem; }
}
// 当前项的索引
private int _currentPosition;
public int CurrentPosition
{
get { return _currentPosition; }
}
public Predicate<object> Filter { get; set; }
public ObservableCollection<GroupDescription> GroupDescriptions
{
get { return null; }
}
public ReadOnlyObservableCollection<object> Groups
{
get { return null; }
}
// 当前项是否超出了集合的末尾
public bool IsCurrentAfterLast
{
get
{
if (!IsEmpty)
return CurrentPosition >= base.Count;
return true;
}
}
// 当前项是否超出了集合的开头
public bool IsCurrentBeforeFirst
{
get
{
if (!IsEmpty)
return CurrentPosition < 0;
return true;
}
}
// 集合是否为空
public bool IsEmpty
{
get { return base.Count == 0; }
}
// System.ComponentModel.SortDescription 类型对象的集合,其用于描述集合中的数据的排序方式
private SortDescriptionCollection _sortDescriptions;
public SortDescriptionCollection SortDescriptions
{
get
{
if (_sortDescriptions == null)
_sortDescriptions = new SortDescriptionCollection();
return _sortDescriptions;
}
}
public System.Collections.IEnumerable SourceCollection
{
get { return this; }
}
// 当前项更改后所触发的事件
public event EventHandler CurrentChanged;
// 当前项准备更改之前所触发的事件
public event CurrentChangingEventHandler CurrentChanging;
// 指定的项是否属于此集合
public bool Contains(object item)
{
return Contains((T)item);
}
// 延迟刷新
public IDisposable DeferRefresh()
{
return new DeferRefreshHelper(() => Refresh());
}
private class DeferRefreshHelper : IDisposable
{
private Action _callback;
public DeferRefreshHelper(Action callback)
{
_callback = callback;
}
public void Dispose()
{
_callback();
}
}
// 将指定项设置为当前项
public bool MoveCurrentTo(object item)
{
if (object.Equals(CurrentItem, item) && (item != null || IsCurrentInView))
return IsCurrentInView;
int index = base.IndexOf((T)item);
return MoveCurrentToPosition(index);
}
// 将第一项设置为当前项
public bool MoveCurrentToFirst()
{
return MoveCurrentToPosition(0);
}
// 将最后一项设置为当前项
public bool MoveCurrentToLast()
{
return MoveCurrentToPosition(base.Count - 1);
}
// 将当前项的下一项设置为当前项
public bool MoveCurrentToNext()
{
return CurrentPosition < base.Count && MoveCurrentToPosition(CurrentPosition + 1);
}
// 将指定位置的项设置为当前项
public bool MoveCurrentToPosition(int position)
{
if (position < -1 || position > base.Count)
throw new Exception("参数“position”不在范围内");
if (position != CurrentPosition)
{
bool isCurrentAfterLast = IsCurrentAfterLast;
bool isCurrentBeforeFirst = IsCurrentBeforeFirst;
OnCurrentChanging();
ChangeCurrentToPosition(position);
OnCurrentChanged();
if (IsCurrentAfterLast != isCurrentAfterLast)
OnPropertyChanged("IsCurrentAfterLast");
if (IsCurrentBeforeFirst != isCurrentBeforeFirst)
OnPropertyChanged("IsCurrentBeforeFirst");
OnPropertyChanged("CurrentPosition");
OnPropertyChanged("CurrentItem");
}
return IsCurrentInView;
}
// 将当前项的上一项设置为当前项
public bool MoveCurrentToPrevious()
{
return CurrentPosition >= 0 && MoveCurrentToPosition(CurrentPosition - 1);
}
public event EventHandler<ReloadEventArgs> Reload;
// 刷新集合
public void Refresh()
{
if (Reload != null)
Reload(this, new ReloadEventArgs() { SortDescriptions = SortDescriptions });
}
protected virtual void OnCurrentChanged()
{
if (CurrentChanged != null)
CurrentChanged(this, EventArgs.Empty);
}
protected virtual void OnCurrentChanging(CurrentChangingEventArgs args)
{
if (args == null)
throw new Exception("参数“args”不能为 null");
if (CurrentChanging != null)
CurrentChanging(this, args);
}
protected void OnCurrentChanging()
{
_currentPosition = -1;
OnCurrentChanging(new CurrentChangingEventArgs(false));
}
protected virtual void OnPropertyChanged(string propertyName)
{
base.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
}
// 获取集合内指定位置的对象
private object GetItemAt(int index)
{
if (index >= 0 && index < base.Count)
return this[index];
return null;
}
// 当前项是否在集合的范围之内
private bool IsCurrentInView
{
get { return CurrentPosition >= 0 && CurrentPosition < base.Count; }
}
// 将指定位置的项设置为当前项
private void ChangeCurrentToPosition(int position)
{
if (position < 0)
{
_currentItem = null;
_currentPosition = -1;
}
else if (position >= base.Count)
{
_currentItem = null;
_currentPosition = base.Count;
}
else
{
_currentItem = this[position];
_currentPosition = position;
}
}
}
}
PageableSortableCollectionView.cs
代码
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Collections.ObjectModel;
namespace Silverlight40.WCFRIAServices.CustomSortingPaging
{
// 为了实现分页功能,所以需要实现 IPagedCollectionView 接口
// 为了集合具有排序的功能,所以需要继承自 SortableCollectionView<T>
public class PageableSortableCollectionView<T> : SortableCollectionView<T>, IPagedCollectionView
{
// 是否允许分页
public bool CanChangePage
{
get { return true; ; }
}
// 页索引是否正在改变
private bool _isPageChanging;
public bool IsPageChanging
{
get { return _isPageChanging; }
private set
{
if (_isPageChanging != value)
{
_isPageChanging = value;
OnPropertyChanged("IsPageChanging");
}
}
}
// 项总数
public int ItemCount
{
get { return TotalItemCount; }
set { TotalItemCount = value; }
}
// 页索引
private int _pageIndex;
public int PageIndex
{
get { return _pageIndex; }
}
// 页大小
private int _pageSize = 10;
public int PageSize
{
get { return _pageSize; }
set
{
if (value != _pageSize && value >= 1)
{
_pageSize = value;
OnPropertyChanged("PageSize");
}
}
}
// 项总数。未知的话则返回 -1
private int _totalItemCount;
public int TotalItemCount
{
get { return _totalItemCount; }
set
{
if (value != _totalItemCount)
{
_totalItemCount = value;
OnPropertyChanged("TotalItemCount");
OnPropertyChanged("ItemCount");
}
}
}
// PageIndex 改变之后所触发的事件
public event EventHandler<EventArgs> PageChanged;
// PageIndex 改变之前所触发的事件
public event EventHandler<PageChangingEventArgs> PageChanging;
// 将第一页设置为当前页
public bool MoveToFirstPage()
{
return MoveToPage(0);
}
// 将最后一页设置为当前页
public bool MoveToLastPage()
{
return TotalItemCount != -1 && PageSize > 0 && MoveToPage(PageCount - 1);
}
// 将当前页的下一页设置为当前页
public bool MoveToNextPage()
{
return MoveToPage(_pageIndex + 1);
}
// 将指定的页索引设置为当前页
public bool MoveToPage(int pageIndex)
{
if (pageIndex < -1)
return false;
if (pageIndex == -1 && PageSize > 0)
return false;
if (pageIndex >= PageCount || _pageIndex == pageIndex)
return false;
try
{
IsPageChanging = true;
PageChangingEventArgs args = new PageChangingEventArgs(pageIndex);
// 触发 PageChanging 事件
OnPageChanging(new PageChangingEventArgs(pageIndex));
// 监听者的 PageChanging 事件处理完成后继续往下执行
if (args.Cancel)
return false;
_pageIndex = pageIndex;
IsPageChanging = false;
OnPropertyChanged("PageIndex");
OnPageChanged(EventArgs.Empty);
base.Refresh();
return true;
}
finally
{
IsPageChanging = false;
}
}
// 将当前页的上一页设置为当前页
public bool MoveToPreviousPage()
{
return MoveToPage(_pageIndex - 1);
}
protected virtual void OnPageChanging(PageChangingEventArgs args)
{
if (PageChanging != null)
PageChanging(this, args);
}
protected virtual void OnPageChanged(EventArgs args)
{
if (PageChanged != null)
PageChanged(this, args);
}
// 页总数
public int PageCount
{
get
{
if (_pageSize <= 0)
return 0;
return Math.Max(1, (int)Math.Ceiling((double)ItemCount / (double)_pageSize));
}
}
}
}
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel;
using System.Collections.ObjectModel;
namespace Silverlight40.WCFRIAServices.CustomSortingPaging
{
// 为了实现分页功能,所以需要实现 IPagedCollectionView 接口
// 为了集合具有排序的功能,所以需要继承自 SortableCollectionView<T>
public class PageableSortableCollectionView<T> : SortableCollectionView<T>, IPagedCollectionView
{
// 是否允许分页
public bool CanChangePage
{
get { return true; ; }
}
// 页索引是否正在改变
private bool _isPageChanging;
public bool IsPageChanging
{
get { return _isPageChanging; }
private set
{
if (_isPageChanging != value)
{
_isPageChanging = value;
OnPropertyChanged("IsPageChanging");
}
}
}
// 项总数
public int ItemCount
{
get { return TotalItemCount; }
set { TotalItemCount = value; }
}
// 页索引
private int _pageIndex;
public int PageIndex
{
get { return _pageIndex; }
}
// 页大小
private int _pageSize = 10;
public int PageSize
{
get { return _pageSize; }
set
{
if (value != _pageSize && value >= 1)
{
_pageSize = value;
OnPropertyChanged("PageSize");
}
}
}
// 项总数。未知的话则返回 -1
private int _totalItemCount;
public int TotalItemCount
{
get { return _totalItemCount; }
set
{
if (value != _totalItemCount)
{
_totalItemCount = value;
OnPropertyChanged("TotalItemCount");
OnPropertyChanged("ItemCount");
}
}
}
// PageIndex 改变之后所触发的事件
public event EventHandler<EventArgs> PageChanged;
// PageIndex 改变之前所触发的事件
public event EventHandler<PageChangingEventArgs> PageChanging;
// 将第一页设置为当前页
public bool MoveToFirstPage()
{
return MoveToPage(0);
}
// 将最后一页设置为当前页
public bool MoveToLastPage()
{
return TotalItemCount != -1 && PageSize > 0 && MoveToPage(PageCount - 1);
}
// 将当前页的下一页设置为当前页
public bool MoveToNextPage()
{
return MoveToPage(_pageIndex + 1);
}
// 将指定的页索引设置为当前页
public bool MoveToPage(int pageIndex)
{
if (pageIndex < -1)
return false;
if (pageIndex == -1 && PageSize > 0)
return false;
if (pageIndex >= PageCount || _pageIndex == pageIndex)
return false;
try
{
IsPageChanging = true;
PageChangingEventArgs args = new PageChangingEventArgs(pageIndex);
// 触发 PageChanging 事件
OnPageChanging(new PageChangingEventArgs(pageIndex));
// 监听者的 PageChanging 事件处理完成后继续往下执行
if (args.Cancel)
return false;
_pageIndex = pageIndex;
IsPageChanging = false;
OnPropertyChanged("PageIndex");
OnPageChanged(EventArgs.Empty);
base.Refresh();
return true;
}
finally
{
IsPageChanging = false;
}
}
// 将当前页的上一页设置为当前页
public bool MoveToPreviousPage()
{
return MoveToPage(_pageIndex - 1);
}
protected virtual void OnPageChanging(PageChangingEventArgs args)
{
if (PageChanging != null)
PageChanging(this, args);
}
protected virtual void OnPageChanged(EventArgs args)
{
if (PageChanged != null)
PageChanged(this, args);
}
// 页总数
public int PageCount
{
get
{
if (_pageSize <= 0)
return 0;
return Math.Max(1, (int)Math.Ceiling((double)ItemCount / (double)_pageSize));
}
}
}
}
ProductViewModel.cs
代码
using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel;
using Silverlight40.Web.Model;
using Silverlight40.Web.Service;
using System.ServiceModel.DomainServices.Client;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Silverlight40.WCFRIAServices.CustomSortingPaging
{
public class ProductViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
// 数据
public PageableSortableCollectionView<Product> Data { get; private set; }
// 页大小
public int PageSize
{
get { return Data.PageSize; }
set
{
if (Data.PageSize == value)
return;
Data.PageSize = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("PageSize"));
}
}
public ProductViewModel()
{
Data = new PageableSortableCollectionView<Product>();
Data.PageChanging += new EventHandler<PageChangingEventArgs>(Data_PageChanging);
Data.PageChanged += new EventHandler<EventArgs>(Data_PageChanged);
Data.Reload += new EventHandler<ReloadEventArgs>(Data_OnReload);
GetData();
}
void Data_PageChanging(object sender, PageChangingEventArgs e)
{
}
void Data_PageChanged(object sender, EventArgs e)
{
}
void Data_OnReload(object sender, ReloadEventArgs e)
{
// 排序或页索引有变化则从服务端获取新的数据
GetData();
}
private void GetData()
{
MyDomainContext context = new MyDomainContext();
string sort = "";
// 构造排序表达式
foreach (SortDescription sortDesc in Data.SortDescriptions)
{
// “it”是 ObjectQuery<T> 的默认名称
sort += string.Format("it.{0} {1},", sortDesc.PropertyName, sortDesc.Direction == ListSortDirection.Ascending ? "asc" : "desc");
}
if (sort == "")
sort = "it.ProductId asc";
else
sort = sort.TrimEnd(',');
int take = Data.PageSize;
int skip = Data.PageIndex * Data.PageSize;
// 为了获取数据记录总数,所以需要在这里写分页逻辑
EntityQuery<Product> query = context.GetProductsBySortQuery(sort).Skip(skip).Take(take);
// 返回数据中是否包含数据记录总数(前提,分页逻辑要写在 EntityQuery<Product> 上)
query.IncludeTotalCount = true;
LoadOperation<Product> lo = context.Load(query);
lo.Completed += new EventHandler(delegate
{
Data.Clear();
// 数据记录总数
Data.ItemCount = lo.TotalEntityCount;
// 当前页的数据集合
foreach (Product p in lo.Entities)
{
Data.Add(p);
}
});
}
}
}
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.ComponentModel;
using Silverlight40.Web.Model;
using Silverlight40.Web.Service;
using System.ServiceModel.DomainServices.Client;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Silverlight40.WCFRIAServices.CustomSortingPaging
{
public class ProductViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
// 数据
public PageableSortableCollectionView<Product> Data { get; private set; }
// 页大小
public int PageSize
{
get { return Data.PageSize; }
set
{
if (Data.PageSize == value)
return;
Data.PageSize = value;
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs("PageSize"));
}
}
public ProductViewModel()
{
Data = new PageableSortableCollectionView<Product>();
Data.PageChanging += new EventHandler<PageChangingEventArgs>(Data_PageChanging);
Data.PageChanged += new EventHandler<EventArgs>(Data_PageChanged);
Data.Reload += new EventHandler<ReloadEventArgs>(Data_OnReload);
GetData();
}
void Data_PageChanging(object sender, PageChangingEventArgs e)
{
}
void Data_PageChanged(object sender, EventArgs e)
{
}
void Data_OnReload(object sender, ReloadEventArgs e)
{
// 排序或页索引有变化则从服务端获取新的数据
GetData();
}
private void GetData()
{
MyDomainContext context = new MyDomainContext();
string sort = "";
// 构造排序表达式
foreach (SortDescription sortDesc in Data.SortDescriptions)
{
// “it”是 ObjectQuery<T> 的默认名称
sort += string.Format("it.{0} {1},", sortDesc.PropertyName, sortDesc.Direction == ListSortDirection.Ascending ? "asc" : "desc");
}
if (sort == "")
sort = "it.ProductId asc";
else
sort = sort.TrimEnd(',');
int take = Data.PageSize;
int skip = Data.PageIndex * Data.PageSize;
// 为了获取数据记录总数,所以需要在这里写分页逻辑
EntityQuery<Product> query = context.GetProductsBySortQuery(sort).Skip(skip).Take(take);
// 返回数据中是否包含数据记录总数(前提,分页逻辑要写在 EntityQuery<Product> 上)
query.IncludeTotalCount = true;
LoadOperation<Product> lo = context.Load(query);
lo.Completed += new EventHandler(delegate
{
Data.Clear();
// 数据记录总数
Data.ItemCount = lo.TotalEntityCount;
// 当前页的数据集合
foreach (Product p in lo.Entities)
{
Data.Add(p);
}
});
}
}
}
Demo.xaml
代码
<navigation:Page x:Class="Silverlight40.WCFRIAServices.CustomSortingPaging.Demo"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
xmlns:vm="clr-namespace:Silverlight40.WCFRIAServices.CustomSortingPaging"
Title="Demo Page">
<Grid x:Name="LayoutRoot">
<StackPanel>
<!--
在 View 中引入 ViewModel
-->
<StackPanel.DataContext>
<vm:ProductViewModel />
</StackPanel.DataContext>
<sdk:DataGrid x:Name="dataGrid" AutoGenerateColumns="True" ItemsSource="{Binding Data}" />
<sdk:DataPager x:Name="dataPager" Source="{Binding Data}" PageSize="{Binding PageSize}" DisplayMode="FirstLastPreviousNext" IsEnabled="True" IsTotalItemCountFixed="True" />
</StackPanel>
</Grid>
</navigation:Page>
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
xmlns:vm="clr-namespace:Silverlight40.WCFRIAServices.CustomSortingPaging"
Title="Demo Page">
<Grid x:Name="LayoutRoot">
<StackPanel>
<!--
在 View 中引入 ViewModel
-->
<StackPanel.DataContext>
<vm:ProductViewModel />
</StackPanel.DataContext>
<sdk:DataGrid x:Name="dataGrid" AutoGenerateColumns="True" ItemsSource="{Binding Data}" />
<sdk:DataPager x:Name="dataPager" Source="{Binding Data}" PageSize="{Binding PageSize}" DisplayMode="FirstLastPreviousNext" IsEnabled="True" IsTotalItemCountFixed="True" />
</StackPanel>
</Grid>
</navigation:Page>
OK
[源码下载]