Fun with CSLA Databinding, paging and sorting along with IReportTotalRowCount

Fun with CSLA Databinding, paging and sorting along with IReportTotalRowCount

Old forum URL: forums.lhotka.net/forums/t/2686.aspx


mongo posted on Thursday, April 12, 2007

In a web app, I needed paging and sorting to be done on a number of pages.  I wanted something generic and looked at SortedBindingList and FilteredBindingList.  After reading a couple of Rocky's posts and looking at the FilteredBindingList, it became apparent that using FilteredBindingList focuses on individual items rather than the state of the list.  Therefore, I went a little different route.

I implemented a class that derives from IList<T> and also implements IReportTotalRowCount where T is your business object  This little class first sorts the list handed to it and then applies the filtering based on the passed in SelectObjectArgs. 

*** I believe that different grids will pass back differing information for the Sort Expression.  The grid I am using (telerik) is clean, but I've seen others (Intersoft) that like to get cute if I remember correctly.  Just a note of caution.  Also, you could make this more generic to recognize when paging is not being requested by looking at SelectObjectArgs and operate accordingly.

The best part is the wiring of the SelectObjects event off the Csla Datasource control. Assuming I have a list of Customer objects (ReadOnly, BusinessObject, etc.) and a function that retrieves the Customer objects for the page (from db, session, viewstate, etc), the code looks like this:

CSLADataSource_SelectObject( object sender, Csla.Web.SelectObjectArgs e)
{
   e.BusinessObject = new SortedAndPagedBindingList< Customer >(GetCustomerCollection(), e);
}

Now for the code:


/// <summary>
/// SortedAndPagedBindingList:  This class is responsible for taking a List of 'TBase' and returning
/// a subset of items that are sorted and paged according to the Csla Datasource control. This control also
/// implements IReportTotalRowCount which is required to support paging within Csla.
/// </summary>
/// <typeparam name="TBase">The collection of Csla objects being sorted and paged</typeparam>
public class SortedAndPagedBindingList<TBase> : List<TBase>, Csla.Core.IReportTotalRowCount
{
    /// <summary>
    /// _totalRowCount:  This retains the count of the initial list (as opposed to the trimmed down
    /// result set. Used for paging purposes.
    /// </summary>
    private int _totalRowCount = -1;

    /// <summary>
    /// Constructor:  This constructor builds out object list according to the Csla request.
    /// </summary>
    /// <param name="collection">The source list of all items to be considered.</param>
    /// <param name="args">The paging and sorting parameters used against the incoming list</param>
    public SortedAndPagedBindingList(IList<TBase> collection, Csla.Web.SelectObjectArgs args)
        : base(args.MaximumRows)
    {
        // first, grab the total row count for reporting through the IReportTotalRowCount
        _totalRowCount = collection.Count;

        // next, we'll sort the list if requested.

        Csla.SortedBindingList<TBase> sortedList = new Csla.SortedBindingList<TBase>(collection);

        if (args.SortExpression.Length > 0)
            sortedList.ApplySort(args.SortExpression, args.SortDirection);

        // finally, we'll add the page of items requested by SelectObjectArgs
        if (args.StartRowIndex > sortedList.Count - 1)
            return;

        int rowIndex = args.StartRowIndex;

        while ((rowIndex - args.StartRowIndex) < args.MaximumRows && rowIndex < sortedList.Count)
            this.Add(sortedList[rowIndex++]);

    }

 

    #region IReportTotalRowCount Members

    /// <summary>
    /// TotalRowCount - this property returns the total row count for the initial list that passed to the
    /// constructor.
    /// </summary>
    int Csla.Core.IReportTotalRowCount.TotalRowCount
    {
        get { return _totalRowCount; }
    }

    #endregion


}

mdurrin replied on Tuesday, July 24, 2007

Thanks, Mongo.  That worked great.  This was a real headache before I found your code.

Marc

miloy001 replied on Sunday, July 29, 2007

Thanks, but why e.StartRowIndex and e.MaxRows always = 1 in my test? Any thought?

I have to modified like this to make it works:

public class SortedAndPagedBindingList<TBase> : List<TBase>, Csla.Core.IReportTotalRowCount
{
    private int _totalRowCount = -1;

    public SortedAndPagedBindingList(IList<TBase> collection,  Csla.Web.SelectObjectArgs args)
        : base(collection.count)
    {
        _totalRowCount = collection.Count;

        Csla.SortedBindingList<TBase> sortedList = new Csla.SortedBindingList<TBase>(collection);

        if (args.SortExpression.Length > 0)
            sortedList.ApplySort(args.SortProperty, args.SortDirection);

               int i;

        for (i=0;i<collection.count;i++)
            this.Add(sortedListIdea [I]);

    }

 

shaheem replied on Sunday, August 05, 2007

Hi,

I became a member jst to say thankx...i'v just work with csla.net for a few months...so im fairly new and your code helped me heapz :-)

Shaheem.

malachai24 replied on Thursday, August 30, 2007

Mongo,

Thanks for the code. I have implemented it with a slight change (to fix an issue I ran into).

I modified it from

        if (args.SortExpression.Length > 0)
            sortedList.ApplySort(args.SortExpression, args.SortDirection);

to

        if (args.SortProperty.Length > 0)
            sortedList.ApplySort(args.SortProperty, args.SortDirection);

since ApplySort takes the SortProperty as a first argument and not the Expression. It seems like the correct thing to do, do you agree?

I utilized this in conjunction with telerik's RadGrid control, and ran into an issue sorting in ascending order on a date. I probed further, and looked at SelectObjectArgs.cs. I don't know if it is specific to this control (the telerik RadGrid control), but when I was attempting to sort ascending, the _sortProperty would be set to "PropertyName ASC" which is not a valid property on my object (it should of been "PropertyName"). I changed it from the following:

    public SelectObjectArgs(System.Web.UI.DataSourceSelectArguments args)
    {

      _startRowIndex = args.StartRowIndex;
      _maximumRows = args.MaximumRows;
      _retrieveTotalRowCount = args.RetrieveTotalRowCount;

      _sortExpression = args.SortExpression;
      if (!(string.IsNullOrEmpty(_sortBLOCKED EXPRESSION)
      {
        if (_sortExpression.Length >= 5 &&
          _sortExpression.Substring(_sortExpression.Length - 5) == " DESC")
        {
          _sortProperty = _sortExpression.Substring(0, _sortExpression.Length - 5);
          _sortDirection = ListSortDirection.Descending;

        }
        else
        {
          _sortProperty = args.SortExpression;
          _sortDirection = ListSortDirection.Ascending;
        }
      }
    }

to

    public SelectObjectArgs(System.Web.UI.DataSourceSelectArguments args)
    {

      _startRowIndex = args.StartRowIndex;
      _maximumRows = args.MaximumRows;
      _retrieveTotalRowCount = args.RetrieveTotalRowCount;

      _sortExpression = args.SortExpression;
      if (!(string.IsNullOrEmpty(_sortBLOCKED EXPRESSION)
      {
        if (_sortExpression.Length >= 5 &&
          _sortExpression.Substring(_sortExpression.Length - 5) == " DESC")
        {
          _sortProperty = _sortExpression.Substring(0, _sortExpression.Length - 5);
          _sortDirection = ListSortDirection.Descending;

        }
        else if (_sortExpression.Length >= 4 &&
            _sortExpression.Substring(_sortExpression.Length - 4) == " ASC")
        {
            _sortProperty = _sortExpression.Substring(0, _sortExpression.Length - 4);
            _sortDirection = ListSortDirection.Ascending;
        }
        else
        {
          _sortProperty = args.SortExpression;
          _sortDirection = ListSortDirection.Ascending;
        }
      }
    }

 

After making these two changes, presto. Dates now sorted in both ascending and descending order correctly. I don't know if the telerik control is to blame for passing in the addtional " ASC" for the sort expression or if other controls do the same. I figured I would post it here as a potential bug in CSLA in case someone else runs into it. I'll test it with a different Grid (probably the Microsoft Gridview) and see if the sort expression includes the " ASC" or not. I'll post my results.

 

Mike

malachai24 replied on Thursday, August 30, 2007

Following up, it appears like the Microsoft GridView does in fact only pass in "PropertyName" instead of "PropertyName ASC" like the telerik RadGrid does for the SortExpression.

Regardless, would the change in SelectObjectArgs I proposed in the previous post make sense?

As for the change I made to Mongo's code, I needed to also add a check for null as well.

        if (args.SortProperty != null && args.SortProperty.Length > 0)
            sortedList.ApplySort(args.SortProperty, args.SortDirection);

Are there any potential issues with either of these changes?

Thanks,

Mike

RockfordLhotka replied on Saturday, September 01, 2007

Those seem like good changes, I'll add this to the wish list.

icereval replied on Monday, November 17, 2008

One more thing incase paging is not used and you still want to use the same binding list.

            if (args.MaximumRows > 0)
                while ((rowIndex - args.StartRowIndex) < args.MaximumRows && rowIndex < sortedList.Count)
                    this.Add(sortedList[rowIndex++]);
            else
                while (rowIndex < sortedList.Count)
                    this.Add(sortedList[rowIndex++]);

swegele replied on Saturday, June 13, 2009

Mongo et al,

I want to implement your in-memory method.  But you don't show how you use what you made on the page...and I am too thick to figure it out.  Can you show me how you implement this on a page?

Are you using the CSLA datasource or not?

 

Thanks

Sean

swegele replied on Saturday, June 13, 2009

Oops I missed the part at the top where you do your select list...sorry

Copyright (c) Marimer LLC