• SilverLight之路(七)


    接上回,数据表格搞定了,不过当数据量超大时,比如我这个项目,客户数将近100W,那么选择这种分页会死人的,传统作法,服务器端分页吧。

    我们还是使用DataPager分布控件,想一下之前的实现,原理就是它与DataGrid绑定同一数据源,分页的操作其实也是在数据源上进行的,这里是PagedCollectionView。而如果我们要实现服务器端分页,它们就不能绑在一起啦,这里是从网上找到的一个实现方法,实现类似如下

     

    先看服务器WCF端,加入客户实体类(前面有说过步骤,不再累述),然后定义操作契约

     

    public List<DAL.WFT_Batch_CustomerClassification> GetCustomerListPager(CustomerClassificationFilter filter, out int totalCount)
    {
    using (var entities = new DAL.WFT_101104Entities())
    {
    int rowsCount = 0;
    var query
    = entities.WFT_Batch_CustomerClassification;

    if (filter.PageIndex <= 0)
    rowsCount
    = query.Count();
    totalCount
    = rowsCount;
    query
    = query.OrderBy(t => t.GUID).Skip(filter.PageIndex * filter.PageSize).Take(filter.PageSize);

    return query.ToList();
    }
    }

    数据契约 

    [DataContract]
    public class CustomerClassificationFilter
    {
    [DataMember]
    public int PageIndex { set; get; }
    [DataMember]
    public int PageSize { set; get; }
    }

    这里同意提供该服务器端分页实现方法的网友的说法,建议把查询条件也进行实体类包装。目前我们的查询条件并未包含具体业务逻辑条件,只包括了分页大小与当前页索引。这里有一个问题,排序的时候没有指定排序字段,且每次都是固定排序,这个问题我们稍候再来解决,现在我们还是把重点放到分页上。

     

    SL端实现,首先给DataPager增加一个扩展方法

     

    public static class DataPageExtension
    {
    /// <summary>
    /// BindSource扩展方法
    /// </summary>
    /// <param name="dataPager">分页控件</param>
    /// <param name="totalCount">记录总数</param>
    /// <param name="pageSize">分页大小</param>
    public static void BindSource(this DataPager dataPager, int totalCount, int pageSize)
    {
    List
    <int> list = new List<int>(totalCount);
    for (int i = 0; i < totalCount; i++) list.Add(i);
    PagedCollectionView pcv
    = new PagedCollectionView(list);
    pcv.PageSize
    = pageSize;
    dataPager.Source
    = pcv;
    }
    }

    这里本想给pcv指定一个记录总数,似乎没有,所以只能用一个循环来模拟数据源了。

    数据绑定时

     

    private void BindGrid(int pageIndex)
    {
    CustomerClassificationFilter ccf
    = new CustomerClassificationFilter();
    ccf.PageIndex
    = pageIndex;
    ccf.PageSize
    = this.dpCustomerList.PageSize;
    client.GetCustomerListPagerAsync(ccf);
    }

    事件的回调可以放到初始化时去做,否则会多次注册事件,嗯,也可以叫做注册了多个事件响应方法。

     

    client.GetCustomerListPagerCompleted += new EventHandler<WcfService.GetCustomerListPagerCompletedEventArgs>(
    (s, ex)
    =>
    {
    PagedCollectionView pcv
    = new PagedCollectionView(ex.Result);
    this.dgCustomerList.ItemsSource = pcv;

    if (this.dpCustomerList.PageIndex <= 0)
    {
    this.dpCustomerList.PageIndexChanged -= dpCustomerList_PageIndexChanged;
    this.dpCustomerList.BindSource(ex.totalCount, this.dpCustomerList.PageSize);
    this.dpCustomerList.PageIndexChanged += dpCustomerList_PageIndexChanged;
    }
    });

    注意分页事件的注册,如果不这样做,会进行两次绑定,因为在分页时也要进行数据查询的,而绑定时会自动触发分页事件,这种情况在做下拉列表默认选择项时也会存在,也可以考虑用这种办法。

    这里还有一个要注意的地方,注意我们的WCF操作契约的实现,里面使用了一个Out关键字,大家都知道这个是什么意思吧。如果我们这里这样定义了,在wcf服务的代理类生成时,会把它作为result的参数传回到客户端的,如:ex.totalCount,并不需要我们在调用时指定,这与传统的使用方法有区别,所以要注意一下。调用时只传一个参数就可以了,如:client.GetCustomerListPagerAsync(ccf);

    分页事件

     

    void dpCustomerList_PageIndexChanged(object sender, EventArgs e)
    {
    BindGrid(
    this.dpCustomerList.PageIndex);
    }

    上面就是全部的实现代码,它的原理是使DataPager绑定一个虚拟的数据源,只是使用它的分页功能,具体的说就是页面大小、当前页索引与分页事件。还有一点,为了性能的考虑,原作者做了页索引为零时的判断,这样就只有在未获得结果之前进行结果总数的计算,这一点很重要,特别是数据量大的时候。

     

    现在我们再来看看排序的功能,因为进行了服务器端分页,那么排序功能当然也要在服务器端来实现了,否则就只有在当前页(因为是服务器端分页,所以结果就只有当前页数据)进行了。

     

    原理很好理解,把排序字段也传到服务器端就好办了,我们先在条件参数中把排序字段加上

    [DataContract]
    public class CustomerClassificationFilter
    {
    [DataMember]
    public SortDescription Sorted { set; get; }
    [DataMember]
    public int PageIndex { set; get; }
    [DataMember]
    public int PageSize { set; get; }
    }

    在查询时加入排序的逻辑

     

    public List<DAL.WFT_Batch_CustomerClassification> GetCustomerListPager(CustomerClassificationFilter filter, out int totalCount)
    {
    using (var entities = new DAL.WFT_101104Entities())
    {
    int rowsCount = 0;
    var query
    = entities.WFT_Batch_CustomerClassification;

    if (filter.PageIndex <= 0)
    rowsCount
    = query.Count();
    totalCount
    = rowsCount;
    if (filter.Sorted != null)
    query
    = SilverlightClassLibrary.DBHelper.DataSorting<DAL.WFT_Batch_CustomerClassification>(query, filter.Sorted).Skip(filter.PageIndex * filter.PageSize).Take(filter.PageSize);
    else
    query
    = query.OrderBy(t => t.GUID).Skip(filter.PageIndex * filter.PageSize).Take(filter.PageSize);
    return query.ToList();
    }
    }

    DataSorting方法我稍候介绍。

    在SL端放一变量来存储上一次的排序字段,默认可以指定一个,如

     

    //上一次的排序列
    private SortDescription oldsort = new SortDescription("GUID", ListSortDirection.Ascending);

    在调用时把它传到服务器端

     

    private void BindGrid(int pageIndex)
    {
    CustomerClassificationFilter ccf
    = new CustomerClassificationFilter();
    ccf.Sorted
    = oldsort;
    ccf.PageIndex
    = pageIndex;
    ccf.PageSize
    = this.dpCustomerList.PageSize;
    client.GetCustomerListPagerAsync(ccf);
    }

    下面我们要做的就是如何取得这个字段了,那么,如何得到这个值呢?其实排序也与分页类似,其实也是在数据源上进行的,我们的数据源就是PagedCollectionView(注意这个与DataPager的Source可不是同一个,这个是真实的数据源)。那么我们通过它的SortDescriptions属性就可以得到了,而默认它是支持多列排序的(按shift实现),因此我们得到的是一个集合,而恰好它实现了INotifyCollectionChanged接口,我们这里就是要利用这个接口的CollectionChanged事件,代码如下:

     

    System.Collections.Specialized.INotifyCollectionChanged scn = pcv.SortDescriptions;
    scn.CollectionChanged
    += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(
    (scnS, scnE)
    =>
    {
    if (scnE.Action == NotifyCollectionChangedAction.Remove && scnE.NewStartingIndex == -1)
    {
    return;
    }
    if (scnE.Action == NotifyCollectionChangedAction.Reset)
    return;
    pcv.SortDescriptions.Clear();
    oldsort
    = (SortDescription)scnE.NewItems[0];
    BindGrid(
    this.dpCustomerList.PageIndex);
    });

     

    这个方法也是花费了我好久才在网上找到的,现在再感叹一下吧!!!!不过这里只实现了单列排序,可以通过DataGrid的属性来控制是否使用多列排序功能,如果想实现多列排序的话,就要再进行扩展了,目前我没有用到这个功能。

     

    回过头再看看服务器端动态排序的实现,这个与sl与wcf无关了,纯属linq的应用了,按我们对Linq的理解,我们会写出类似这样的DataSorting方法的实现

     

    switch (filter.Sorted.PropertyName)
    {
    case "姓名":
    if (filter.Sorted.Direction == ListSortDirection.Ascending)
    query
    = query.OrderBy(t=>t.客户姓名);
    else
    query
    = query.OrderByDescending(t=>t.客户姓名);
    break;
    case "年龄":
    if (filter.Sorted.Direction == ListSortDirection.Ascending)
    query
    = query.OrderBy(t => t.年龄);
    else
    query
    = query.OrderByDescending(t => t.年龄);
    break;
    }

    这肯定不行啊,继续百度吧,于是我找到了这个解决办法。

     

    Linq初体验——Order By 通过属性名动态排序

    http://www.cnblogs.com/xxfss2/archive/2010/12/13/1905023.html

     

    稍作修改,实现主要代码如

     

    public static IQueryable<T> DataSorting<T>(IQueryable<T> source, SortDescription sort)
    {
    string sortExpression = sort.PropertyName;
    string sortingDir = string.Empty;
    if (sort.Direction == ListSortDirection.Ascending)
    sortingDir
    = "OrderBy";
    else if (sort.Direction == ListSortDirection.Descending)
    sortingDir
    = "OrderByDescending";
    ParameterExpression param
    = System.Linq.Expressions.Expression.Parameter(typeof(T), sortExpression);
    PropertyInfo pi
    = typeof(T).GetProperty(sortExpression);
    Type[] types
    = new Type[2];
    types[
    0] = typeof(T);
    types[
    1] = pi.PropertyType;
    System.Linq.Expressions.Expression expr
    = System.Linq.Expressions.Expression.Call(typeof(Queryable), sortingDir, types, source.Expression, System.Linq.Expressions.Expression.Lambda(System.Linq.Expressions.Expression.Property(param, sortExpression), param));
    IQueryable
    <T> query = source.AsQueryable().Provider.CreateQuery<T>(expr);
    return query;
    }

     

    至此,这个列表功能主体就基本完成了。

     

  • 相关阅读:
    黄金矿工(LeetCode Medium难度)1129题 题解(DFS)
    String,StringBuffer,StringBuilder区别(笔记)
    ArrayList 与LinkedList 的区别及分别的优缺点
    每日温度(LeetCode Medium难度算法题)题解
    openCV从入门到放弃
    visualStudio 的一些常用使用操作总结
    angularjs和ajax的结合使用 (三)
    来手撸一个小小小小小"3D引擎"
    WPF的TextBox水印效果详解
    WPF使用总结
  • 原文地址:https://www.cnblogs.com/meteortent/p/2080439.html
Copyright © 2020-2023  润新知