• SilverLight商业应用程序开发学习笔记(8)


    适用于SilverLight商业应用程序的几个重要的类

    尽管使用DomainDataSource控件很方便,但是使用该控件使得显示层与业务层呈紧耦合状态,因此在一般分层的应用开发中,很少直接使用DomainDataSource控件,而是选择集合视图作为显示层与数据层之间的桥梁,于是如下一些类就应运而生。这些类有两种,一种是完全在客户端执行逻辑,如LisCollectionView集合视图和PagedCollectionView集合视图,一种是完全在服务器端执行逻辑。在服务器端执行逻辑的好处是可以根据需要向客户端发送数据,从而减少了网络的流量。(比如DomainCollection的分页是在服务器端执行的,在网络传输的只是分页后指定页面的数据)。下面分别用实例给出各个类的用法。

    在进行深入讨论之前,首先创建一个视图模型类并绑定到DataGrid控件上,后面的各系列操作都是在此视图模型类下展开的:

    1)在SilverLight项目的View文件夹下创建一个视图:ProductListView.xaml;

    2)在View文件夹下创建一个新类:ProductListViewModel.cs

    using System.Collections.Generic; 
    using AdventureWorks.Web.Services; 
    using AdventureWorks.Web.Models; 
     
    namespace AdventureWorks.Views 
    { 
        public class ProductListViewModel 
        { 
            public IEnumerable<ProductSummary> Products { get; set; } 
     
            public ProductListViewModel() 
            { 
                ProductSummaryContext context = new ProductSummaryContext(); 
                var qry = context.GetProductSummaryListQuery(); 
                var op = context.Load(qry); 
                Products = op.Entities; 
            } 
        } 
    } 

    3)在视图的构造器(后置代码)里创建视图模型类的实例并赋给视力瓣DataContext属性:

    public ProductListView() 
    { 
        InitializeComponent(); 
     
        this.DataContext = new ProductListViewModel(); 
    }

    4)将视图中的控件绑定到视图模型上:

    <sdk:DataGrid ItemsSource="{Binding Products}" /> 

    1、ObservableCollection<T>泛型类

    SIlverlight支持许多通用的泛型类,包括List,Dictionary,LinkedList,Stack和Queue。但是最重要的泛型类还是ObservableCollecton<T>泛型类,该类实现了INotifyCollectionChanged接口,暴露CollectionChanged事件,在向集合添加或从集合移除项目时会引发该事件。当ListBox,DataGrid,ComboBox控件的ItemsSource属性绑定到这种泛型类的实例以后,由于CollectionChanged事件的作用,集合变更时会自动更新控件里的相应项目。可以实现显示与业务逻辑分离,解除之间的紧耦合。

    2、ListCollectionView/EnumerableCollectionView集合视图

    ListCollectionView/EnumerableCollectionView集合视图可以在内存中(在客户端)筛选,排序和分组所承载的集合。(但这两个集合视图不支持数据分页)这两个集合视图不能直接实例化(因为其构造器为internal),需要CollectionViewSource类充当集合视图的“代理”,然后才能实例化。首先需要定义CollectionViewSource为资源,将一个集合赋值给其Source属性。这样该资源的View属性就可以作为ListCollectionView/EnumerableCollectionView集合视图使用,然后使用Filter,GroupDescriptions和SortDescriptions属性实现筛选,排序和分组功能。ListCollectionView/EnumerableCollectionView集合视图适用于如下情况:

    • 已经将所有数据加载到客户端
    • 需要将集合封装到集合视图以便在XAML中使用
    • 不需要对UI的数据进行分页处理

    3、PagedCollectionView集合视图

    PagedCollectionView集合视图可以直接实例化,支持数据分页。该集合视图适合于如下场景:

    • 已经将所有数据加载到客户端
    • 需要在View model类中控制筛选、排序、分组和分页;
    • 需要对UI的数据进行分页

    实例:封装数据到PagedCollectionView,其实现步骤首先是在视图模型类里将集合封装为PagedCollectionView,然后将其作为视图模型暴露的属性以便进行绑定。

    1)在项目中添加对System.Windows.Data.dll 程序集的引用;

    2)在ProductListViewModel类上添加对System.Windows.Data的引用;

    3)向类中添加新的属性,类型为PagedCollectionView,命名为ProductCollectionView:

    public PagedCollectionView ProductCollectionView { get; set; }

    4)在视图模型的构造函数里将集合封装为PagedCollectionView,将结果赋值给ProductCollectionView属性:

    public ProductListViewModel() 
    { 
        ProductSummaryContext context = new ProductSummaryContext(); 
        var qry = context.GetProductSummaryListQuery();
        var op = context.Load(qry); 
        Products = op.Entities; 
     
        ProductCollectionView = new PagedCollectionView(Products); 
    }   

    5)在ProductListView.xaml视图里,就不需要在构造函数里实例化视图模型类了,而是直接将控件绑定到视图模型的PagedCollectionView类型的属性ProductCollectionView上:

    <sdk:DataGrid ItemsSource="{Binding ProductCollectionView}" /> 
     

    实例二、使用PagedCollectionView进行筛选操作:需要给PagedCollectionView对象的Filter属性指定回调方法,Filter属性的返回值为bool类型的一个委托,当其值设为一个委托时,该委托会在回调中对源进行排序,其返回的Bool值就表示显示还是隐藏。每次筛选执行后就根据Filter的返回值确定是否调用Refresh方法。在下面的示例里,在视图模型类里创建了一个SearchText属性,用以绑定到UI的查询文本框中。当SearchText属性变更时,就执行筛选回调方法刷新PagedCollectionView对象,这样前端的显示就随之进行了更新:

    1)添加SearchText属性,与自动属性不同之处是在每次设置后都要执行PagedCollectionView对象的Refresh方法:

    private string _searchText = ""; 
    public string SearchText 
    { 
        get { return _searchText; } 
        set 
        { 
            _searchText = value; 
            ProductCollectionView.Refresh(); 
        } 
    } 

    2)在视图模型的构造函数里,为ProductCollectionView集合视图指定筛选条件:

    ProductCollectionView = new PagedCollectionView(Products); 
    ProductCollectionView.Filter = item =>  
        ((ProductSummary)item).Name.IndexOf(SearchText,  
                                            StringComparison.InvariantCultureIgnoreCase) != -1; 

    3)在视图里设置TextBox的绑定:

    <TextBox Name="SearchTextBox" Grid.Column="1" Margin="0, 3"  
             Text="{Binding SearchText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />

    有关PagedCollectionView的动态多条件选择,参考http://www.cnblogs.com/626498301/archive/2010/08/18/1801974.html

    示例三:使用PagedCollectionView实现排序功能:需要实例化一个SortDescription对象,然后将该对象添加到PagedCollectionView的SortDescriptions集合属性即可:

    1)在视图模型类中添加引用:

    using System.ComponentModel;

    2)实例化SorDescription对象,并添加到SortDescriptions集合:

    ProductCollectionView = new PagedCollectionView(Products); 
    SortDescription sortBy = new SortDescription("Name", ListSortDirection.Ascending); 
    ProductCollectionView.SortDescriptions.Add(sortBy); 

    示例四:使用PagedCollectionView实现分组功能:需要实例化一个PropertyGroupDescription对象,然后将该对象添加到PagedCollectionView的GroupDescriptions集合属性即可:

    1)在视图模型类中添加引用:

    using Sysetm.Windows.Data;

    2)在实例化PagedCollectionView对象后,以下列代码用Model属性进行分组:

    ProductCollectionView = new PagedCollectionView(Products); 
    PropertyGroupDescription groupBy = new PropertyGroupDescription("Model"); 
    ProductCollectionView.GroupDescriptions.Add(groupBy); 
     

    在分组操作中可以提供值转换器作为PropertyGroupDescription构造器的参数,用于分组;这在使用外键数据进行分组时特别有用。比如想要使用Category来分组Products,但是Products对象只包含Category的Id值,如果使用Id作分组,分组标识就成了Id值,这显然不是我们想要的。如果想要使用Category的名称来排遣序,就需要编写一个值转换器,将CategoryId转换为CategoryName。

    示例五:使用PagedCollectionView实现分页:只需要将PagedCollectionView对象绑定到DataPager控件的Source属性即可:

    <sdk:DataPager PageSize="30" 
                   Source="{Binding ProductCollectionView}" /> 

    4、DomainDataSourceView集合视图

    DomainDataSourceView集合视图由DomainDataSource通过其DataView属性暴露。无法通过代码创建该集合视图,只能通过DomainDataSource控件创建。DomainDataSourceView集合视图实现的筛选,排序,分页等功能是在服务器端完成的。

    5、DomainCollectionView集合视图

    DomainCollectionView集合视图由WCF RIA Service ToolKit引入,与DomainDataSource所表现的行为相同(通过RIA服务获取数据,在服务器端筛选,排序和分页数据),不同之处在于实现了视图与域上下文类的分离,这样视图无需要知道数据是如何获取的。需要引用Microsoft.Windows.Data.DomainServices.dll程序集。

    使用DomainCollectionView集合视图需要三个关键组件:DomainCollectionView集合视图本身,需要封装的源集合以及“Loader(加载器)”,后者包括与服务器交互和更新源集合数据的类。DomainCollectionView 充当了UI与加载器之间的桥梁。关系如图所示:

    image

    WCF RIA Services Toolkit 已经实现了默认的加载器:DomainCollectionViewLoader,该加载器以方法委托作为构造器参数,当数据需要加载或加载完成时调用这些方法;

    DomainCollectionView 集合视图处理如下场景:

    • 使用RIA服务从服务器中获取数据
    • 使用MVVM设计模式

    实例:从DomainCollectionView获取数据,步骤如下:

    1)确保安装了WCF RIA Services Toolkit,并在SIlverlight项目中添加了Microsoft.Windows.Data.DomainServices.dll程序集的引用;

    2)在ProductListViewModel类上添加如下引用:

    using System.ServiceModel.DomainServices.Client;
    using Microsoft.Windows.Data.DomainServices;

    3)在类中添加类型为DomainCollectionView的属性:ProductCollectionView:

    public DomainCollectionView ProductCollectionView { get; set; } 

    4)在视图模型类中添加如下代码,确保内存中的上下文实例唯一:

    private ProductSummaryContext _context = new ProductSummaryContext(); 

    5)在视图模型类中添加如下两个方法:

    private LoadOperation<ProductSummary> LoadProductSummaryList() 
    { 
        EntityQuery<ProductSummary> query = _context.GetProductSummaryListQuery(); 
        return _context.Load(query); 
    } 
     
    private void OnLoadProductSummaryListCompleted(LoadOperation<ProductSummary> op) 
    { 
        if (op.HasError) 
        { 
            // NOTE: You should add some logic for handling errors here, and mark 
            //       the error as handled. 
            // op.MarkErrorAsHandled(); 
        } 
        else if (!op.IsCanceled) 
        { 
            ((EntityList<ProductSummary>)Products).Source = op.Entities; 
        } 
    } 

    6)在视图模型的构造器中添加如下代码:

    public ProductListViewModel() 
    { 
        Products = new EntityList<ProductSummary>(_context.ProductSummaries); 
        
        var collectionViewLoader = new DomainCollectionViewLoader<ProductSummary>( 
                    LoadProductSummaryList, OnLoadProductSummaryListCompleted); 
     
        ProductCollectionView =  
            new DomainCollectionView<ProductSummary>(collectionViewLoader, Products); 
     
        ProductCollectionView.Refresh(); 
    } 

    上述代码是创建DomainCollectionView的范式,注意加粗字体的内容以及各个方法传递的参数类型,特别传递的委托参数类型。

    7)可以直接在视图中将ProductCollectionView属性绑定到控件的数据源属性:

    <sdk:DataGrid ItemsSource="{Binding ProductCollectionView}" /> 

    实例二:使用DomainCollectionView实现筛选:与PagedCollectionView对象类似,DomainCollectionView对象也有一个Filter属性,指定该属性一个实现了筛选逻辑的委托就可以实现筛选作业。但是这种筛选只对客户端的项目有效,如果要想在服务器端进行筛选操作,需要在DomainCollectionView的加载器里添加Where查询字句。实现方法如下:

    1)与前述例子相类似,先在视图模型类中添加一个SearchBox属性以绑定到查询文本框中,并且在获得数据后刷新集合视图:

    private string _searchText = ""; 
     
    public string SearchText 
    { 
        get { return _searchText; } 
        set 
        { 
            _searchText = value; 
            ProductCollectionView.Refresh(); 
        } 
    } 

    2)在LoadProductSummary方法里添加Where条件字句以在服务器端筛选数据,注意对私有字段的访问:

    private LoadOperation<ProductSummary> LoadProductSummaryList() 
    { 
        EntityQuery<ProductSummary> query = _context.GetProductSummaryListQuery(); 
        query = query.Where(x => x.Name.Contains(_searchText)); 
        return _context.Load(query); 
    } 
     

    3、绑定文本框到SearchText属性上:

    <TextBox Name="SearchTextBox" Grid.Column="1" Margin="0, 3"  
             Text="{Binding SearchText, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" /> 

    示例三:使用DomainCollectionView进行排序:与PagedCollectionView的配置方法类似,也是需要实例化一个SortDescription对象,然后将该对象添加到DomainCollectionView的SortDescriptions集合属性里:这些操作是在客户端完成的,如果想在服务器端实现排序也是可以的,另见分页部分的介绍;

    1)添加如下引用:using System.ComponentModel;

    2)实现排序功能:

    ProductCollectionView = 
           new DomainCollectionView<ProductSummary>(collectionViewLoader, Products); 
    SortDescription sortBy = new SortDescription("Name", ListSortDirection.Ascending); 
    ProductCollectionView.SortDescriptions.Add(sortBy); 
     

    示例四:使用DomainCollectionView实现分组功能:需要实例化一个PropertyGroupDescription对象,然后将该对象添加到DomainCollectionView的GroupDescriptions集合属性即可:

    1)在视图模型类中添加引用:

    using Sysetm.Windows.Data;

    2)在实例化PagedCollectionView对象后,以下列代码用Model属性进行分组:

    ProductCollectionView = 
           new DomainCollectionView<ProductSummary>(collectionViewLoader, Products); 
    PropertyGroupDescription groupBy = new PropertyGroupDescription("Model"); 
    ProductCollectionView.GroupDescriptions.Add(groupBy); 

    示例五:使用DomainCollectionView实现分页,好处是只向服务器请求所需要的数据,从而大幅度减少了网络流量;实现这一功能需要使用EntityQuery类的扩展方法:SortBy,PageBy以及SortAndPageBy。实现方法与步骤如下:

    1)将DataPger控件的Source属性绑定到DomainCollectionView对象上:

    <sdk:DataPager PageSize="30" 
                   Source="{Binding ProductCollectionView}" /> 

    2)修改LoadProductSummaryList方法,确保在将查询传递到域下下文的Load方法之前,使用SortAndPageBy扩展方法将集合视图的状态调整为query:

    private LoadOperation<ProductSummary> LoadProductSummaryList() 
    { 
        EntityQuery<ProductSummary> query = _context.GetProductSummaryListQuery(); 
        query = query.SortAndPageBy(ProductCollectionView); 
        return _context.Load(query); 
    } 

    3)为了完全实现分页行为,DataPager控件需要知道全部对象的数量。可以通过将查询对象的IncludeTotalCount属性为True来显示地通知服务器计算总记录数:

    private LoadOperation<ProductSummary> LoadProductSummaryList() 
    { 
        EntityQuery<ProductSummary> query = _context.GetProductSummaryListQuery(); 
        query = query.SortAndPageBy(ProductCollectionView); 
        query.IncludeTotalCount = true; 
        return _context.Load(query); 
    } 
     

    4)获得服务器响应的同时,需要通过SetTotalItemCount将总记录数设置到DomainCollectionView对象上:

    private void OnLoadProductSummaryListCompleted(LoadOperation<ProductSummary> op) 
    { 
        if (op.HasError) 
        { 
            // NOTE: You should add some logic for handling errors here 
            op.MarkErrorAsHandled(); 
        } 
        else if (!op.IsCanceled) 
        { 
            ((EntityList<ProductSummary>)Products).Source = op.Entities; 
     
            if (op.TotalEntityCount != -1) 
                ProductCollectionView.SetTotalItemCount(op.TotalEntityCount); 
        } 
    } 

    5)确保在DomainCollectionView对象上设置了至少一个Sort Description,否则不同分页间导航会抛出异常;

    6)将对Refresh方法的调用用如下代码替换:

    using (ProductCollectionView.DeferRefresh()) 
    { 
        ProductCollectionView.PageSize = 30; //设置page size
        ProductCollectionView.MoveToFirstPage(); //设置当前页
    } 
  • 相关阅读:
    如何找按钮数组在布局中的横竖坐标位置?
    java中使用rmi进行远程方法调用
    测试视频集,各种测试用的视频文件
    如何彻底禁用VS 2008的智能感知功能
    JavaScript秘密花园 Type Casting,undefined,eval,setTimeout,Auto Semicolon Insertion
    JavaScript秘密花园 scope, namespace, constructor, equality and comparisons
    造成内存位置访问无效的一个原因
    depends在VS2008消失了
    12月10日晚的月全食照片
    JavaScript秘密花园 Array, Array Constructor, for in loop, typeof, instanceOf
  • 原文地址:https://www.cnblogs.com/qouoww/p/2493865.html
Copyright © 2020-2023  润新知