[译]WCF RIA Services中的集合(2)
原文地址:http://www.silverlightshow.net/items/Working-with-collections-in-WCF-RIA-Services-part-two.aspx
這是本文的第二部分。
在第一部分中,我們討論了兩個相對簡單的集合類型:EntitySet和EntityList。在本文中,我們將更進一步的了解其他兩個更高級的類型:ICollectionView和DomainCollectionView。
?
ICollectionView
ICollectionView并不是一個新的接口,已經有大量的Silverlight控件對其進行了實現,如DataGrid。現在,我們可以直接在ViewModel中使用它。為了允許控件綁定到一個ICollectionView的實現(如我們熟悉的CollectionViewSource和PagedCollectionView),我們可以這樣做:
?
private ICollectionView CreateView(IEnumerable source) {CollectionViewSource cvs = new CollectionViewSource();cvs.Source = source;return cvs.View; }private ICollectionView _books; public ICollectionView Books {get {if (this._books == null) {this._books = CreateView(this.Context.Books);this._books.Filter = new Predicate<object>(BookCorrespondsToFilter);}return this._books;} }當載入Book數據時會自動反映到View中:
public CollectionViewViewModel() {InstantiateCommands();// load booksContext.Load<Book>(Context.GetBooksQuery().Take(10)); }?
ICollectionView:添加和移除數據
可以通過直接從Context添加和移除實體來完成,這些EntitySet的變化變化都會被CollectionViewSource跟蹤到。
那么,這么做的意義到底是什么?目前為止我們還沒看到這種方式與直接使用EntitySet的區別是吧?其實,ICollectionView的真正特別之處體現在可以添加過濾、排序和分組規則。
ICollectionView的過濾
ICollectionView定義了一個Predicate<object>類型的Filter屬性,讓我們略加修改我們的代碼:
private ICollectionView _books; public ICollectionView Books {get {if (this._books == null) {this._books = CreateView(this.Context.Books);this._books.Filter = new Predicate<object>(BookCorrespondsToFilter);}return this._books;} }public bool BookCorrespondsToFilter(object obj) {Book book = obj as Book;if (filterActive) {return book.Title.Contains("Silverlight");}return true; }BookCorrespondsToFilter方法執行時會檢查每一個Book的Title屬性是否包含“Silverlight”這個單詞,如果不包含,則它不會被顯示在View中。
當前代碼提供的功能僅當你明確知道過濾時機時使用,然而大部分的應用程序具有要用戶自己確定過濾時機的需求,那么我們再來進行一些改動:添加filterActive屬性,當用戶點擊Add Filter時它被置為true。
public bool BookCorrespondsToFilter(object obj) {Book book = obj as Book;if (filterActive) {return book.Title.Contains("Silverlight");}return true; }然當我們點擊按鈕的時候,我們會發現界面并沒有發生任何變化,為什么呢?
當我們針對過濾條件做出改變或Book實體發生變化時(如更改它的書名),ICollectionView的實現不回自動再次執行過濾:Filter方法僅在將實體添加到EntitySet時執行。這意味你不得不明確的通知它使用新的過濾條件重新檢查已經載入的實體,我們可以通過調用ICollection的Refresh()方法實現:
Refresh = new RelayCommand(() => {Books.Refresh(); });現在,View會被重新創建,這會讓所有的Book實體被重新過濾。當然這僅當我們改變過濾條件或EntitySet發生改變時才是必要的。
?
ICollectionView的排序和分組
ICollectionView具有SortDescriptions和GroupDescription這兩個有趣的屬性,可以使用它們定義針對EntitySet的排序和分組規則。
排序操作可以通過點擊綁定了ICollectionView的DataGrid列頭實現,但當我們使用其他的一些諸如ListBox一類沒有表頭的控件時則需要通過代碼的方式改變它們的排序規則:
AddSort = new RelayCommand(() => {Books.SortDescriptions.Add(new SortDescription("Title", ListSortDirection.Ascending)); });對集合的分組操作類似:
AddGrouping = new RelayCommand(() => {Books.GroupDescriptions.Add(new PropertyGroupDescription("Author")); });效果如圖:
有一點要注意,一旦對集合進行分組操作,UI虛擬化將會被關閉-所以在操作大量數據時要謹慎一些。當對大量數據進行分組時一般要和分頁相配合。
當我們要進行排序、過濾和分組操作時,ICollection是一個非常好的選擇。然而它只能作用于內存中的數據,這意味著所有的數據都必須載入到客戶端。這適合大部分的應用場景。而其他的情況我們可以通過DomainCollectionView解決。
?
DomainCollectionView
有很多的企業級應用中會有成千上萬甚至百萬千萬級的數據要進行排序、過濾和分組。面對這類場景,ICollectionView就不再適用了,原因上文已經說明。我們需要一個允許服務端排序、過濾、分組以及更重要的分頁操作的集合。
這就是DomainCollectionView的職責,你可以在WCF RIA Services Toolkit中的Microsoft.Windows.Data.DomainServices程序集中找到它(該程序集已經包含在示例代碼中)。使用DomainCollectionView需要我們進行相比其他集合更多的設置,不過一旦你掌握了這些設置你會發現它們依然十分簡單。DomainCollection初始化時需要Source和Loader(默認是CollectionViewLoader)屬性。
public DomainCollectionView<Book> Books {get { return this.view; } }Source定義了用于View的源實體(任意的實現了IEnumerable的集合),典型的例子就是實現了INotifyCollectionChanged的集合。
this.source = new EntityList<Book>(Context.Books);而Loader關注點在于數據的載入。當我們使用默認的CollectionViewLoader時需要同事傳入兩個回調:OnLoad和OnLoadCompleted,它們分別定義當數據必須被載入和載入操作完成時的發生的事件(當然如果你愿意的話,也可以用一個簡單些的LoadOperation代替CollectionViewLoader)。
this.loader = new DomainCollectionViewLoader<Book>(this.OnLoadBooks,this.OnLoadBooksCompleted); private LoadOperation<Book> OnLoadBooks() {return this.Context.Load(this.query.SortPageAndCount(this.view)); }private void OnLoadBooksCompleted(LoadOperation<Book> op) {if (op.HasError){op.MarkErrorAsHandled();}else if (!op.IsCanceled){this.source.Source = op.Entities;if (op.TotalEntityCount != -1){this.Books.SetTotalItemCount(op.TotalEntityCount);}} }正像你所看到的那樣,我們在OnLoadeBooks中確認請求執行時包含了排序(SortDescription)和分頁(僅當前頁所需數據被載入)以及數據總數(DataPager需要用到)。
當Books被載入時,Source集合會被設置成載入的Book實體,并通過TotalEntityCount得到實體總數賦給TotalItemCount屬性。
this.view = new DomainCollectionView<Book>(loader, source);其余的部分用來進行載入的初始化(如設置頁大小為5等):
using (this.view.DeferRefresh()) {this.view.PageSize = 5;this.view.MoveToFirstPage(); }(使用DeferRefresh()的可以讓view的刷新數據事件推遲到所有using包含的代碼段執行后再執行)
其實,當我們進行分頁、排序(以及其他的可能出發View刷新的操作)時,Loader都被執行并載入數據。一旦載入操作完成便會更新Source屬性并通過通知機制讓View獲知更新同時響應變化。
(注:在WCF RIA Services Toolkit的April版本中,SortPageAndCount已經改成了SortAndPageBy)
?
DomainCollectionView:添加和移除數據
代碼如下
AddBook = new RelayCommand(() => {// you can add books like this, but DCV is server side oriented: to get correct // behaviour, you should add it to the Context and submit the changes, after which// the next query will fetch the book you just added.Book book = Books.AddNew() as Book;book.Author = "Kevin Dockx";book.ASIN = "123456";book.Title = "Silverlight for dummies"; });DeleteBook = new RelayCommand(() => {// deleting an item can be done like this, but should be done directly on the context // & submitted to the serverBooks.RemoveAt(0); });然后這樣的操作會導致View的篩選、排序等規則不同步,畢竟DomainCollectionView在被設計工作在服務端的。
正確的添加和移除實體的方式是同時在服務端進行相應的操作,如:添加一個實體到Context中(或從Context中移除),調用SubmitChanges提交至服務端然后刷新你的DomainCollectionView。
DomainCollectionView:數據的過濾、排序及分組
接下來我們看一下如何對數據進行過濾。很簡單,只要在EntityQuery后面增加相應的Where條件即可,比如:
AddFilter = new RelayCommand(() => {// filters in DCV should be done by adding a Where clause to the query, as DCV is mainly used for// server side logicthis.query = Context.GetOrderedBooksQuery().Where(b => b.Title.Contains("Silverlight"));this.view.MoveToFirstPage();});接下來是排序和分組。當我們點擊列頭時,SortDesscription會被添加到Book集合中決定下次讀取數據的排序策略并自動重新獲取數據。
有一些應用程序中要求當排序規則變化后列表跳轉到第一頁。為對應這樣的需求,我們則要像這樣寫一條event handler:
INotifyCollectionChanged notifyingSortDescriptions = (INotifyCollectionChanged)this.Books.SortDescriptions;notifyingSortDescriptions.CollectionChanged += (sender, e) => {this.view.MoveToFirstPage(); };像使用ICollectionView一樣,我們也可以添加使用代碼向DomainCollectionView中添加自定義的SortDescription
AddSort = new RelayCommand(() => {Books.SortDescriptions.Add(new SortDescription("Title", ListSortDirection.Ascending));Books.Refresh(); });分組的方式類似:
AddGrouping = new RelayCommand(() => {Books.GroupDescriptions.Add(new PropertyGroupDescription("Author"));Books.Refresh(); });將以上內容整合一下,我們最后得到了一個服務端分頁、排序和分組的集合:
總結
WCF RIA Services SP1新增或增強了許多的集合類型。無論是更好的綁定選項還是服務端可分頁、排序的集合都讓其與MVVM的交互變得更加便捷。如果你在使用WCF RIA Services配合MVVM開發行業軟件或商用程序,那么對這些新的集合類型的了解就顯得十分必要了。
轉載于:https://www.cnblogs.com/024hi/archive/2011/07/19/2110186.html
總結
以上是生活随笔為你收集整理的[译]WCF RIA Services中的集合(2)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: eclipse奇淫技巧 (转)
- 下一篇: 关于“做一个聊天+信息分享客户端”的设想