LinqBindingList + sorting

LinqBindingList + sorting

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


lukky posted on Friday, February 27, 2009

Hi,

I have a LabelList class derived from BLB. It holds a collection of Label objects, themselves derived from BB. Finally, I use the LabelList in the Item class, which is derived from BusinessBase as well.

In the Item class, I want to add a property that would return a filtered + sorted list of Label objects, so I go like this:

        public List<Label> BoxLabels
        {
            get
            {
                var l = from lbl in Labels //the Labels property is of type LabelList
                        where lbl.PrintOn == "B"
                        orderby lbl.LabelDescr descending
                        select lbl;
                return l.ToList();
            }
        }

The goal is binding this List<Label> to a WinForms ListBox so that I display a sorted and filtered list of Labels.

The issue is that the "orderby" clause doesn't seem to apply, as it doesn't matter if I use descending or ascending. The result is always in the same order as they appear in the original LabelList.

If I do the following instead, then I get the proper result:

        public List<Label> BoxLabels
        {
            get
            {
                var l = from lbl in Labels
                        where lbl.PrintOn == "B"
                        select lbl;
                List<Label> tmp = l.ToList();
                tmp.Sort((x,y)=> y.LabelDescr.CompareTo(x.LabelDescr));
                return tmp;
            }
        }

So it would seem that LinqBindingList doesn't really support sorting. Or am I again completely off track ?

I'm only an egg. I must wait for fullness Wink [;)]

JoeFallon1 replied on Friday, February 27, 2009

I have been working with Aaron Erickson to fix this issue. In some new code that he is testing, regular LINQ queries should sort and filter. Must not be checked in yet.

I am trying to get it to do the same with the MS Dynamic Query Library so you can pass String parameters at run time to Dynamic linq queries. Still working on it.

Joe

 

lukky replied on Saturday, February 28, 2009

Thanks Joe,

Now I feel better knowing it was not in my code Wink [;)]


Oob replied on Friday, March 13, 2009

No it is not your code. Does anyone know if this is fixed in 3.6.2?

If you add AsEnumerable() to your link query, it will not create a LinqBindingList and you will get a list back that is sorted. So to "fix" your code, the work around would be...

                var l = from lbl in Labels.AsEnumerable()
                        where lbl.PrintOn == "B"
                        select lbl;
                List<Label> tmp = l.ToList();

JoeFallon1 replied on Friday, March 13, 2009

Oob:
Does anyone know if this is fixed in 3.6.2?


It was fixed this week. Aaron is checking in changes. I made a few suggestions which I hope Rocky will incorporate. It is possible that it will make it into 3.6.2.


The Csla.LinqBindingList can now sort and filter using standard LINQ as well as the Microsoft Dynamic Query Extensions for LINQ. So you can pass string parameters for things like sortColumn and sortDirection. You can also use string filters and multi-column sorting.


If you define a variable like mSortedList As LinqBindingList(Of T) (where T is your BO type in your collection) then you can use LINQ or Dynamic Query to fetch the list.


Aaron's code stores your original collection in an internal _list variable and then builds an index cross reference that satisifies your query criteria. So the LBL may *look* unsorted if you just examine its internal list. That is because it is.


You can set the DataSource of a grid directly to the mSortedList. When databinding or an Enumerator (For Each loop) executes against the LBL it asks the default Item property (indexer in C#) for a BO and it looks it up in the cross reference list and hands you back the value that met your crtieria. It is pretty cool.


If you do reflection against a LBL be sure to cast it to IList first so that it uses the same indexer to retrieve an index value correctly.


Joe


 


 


 


 

Woggly replied on Thursday, April 02, 2009

Now I am completely confused...

I am trying to sort a set of BOs too. With the fix from Oob my set gets sorted. But a LinqBindingList is no longer returned from the sorting.
Now when I am editing one of the objects will the set get notified? I am not sure about this all. In the book it is said, that the LinqBindingList takes care of notifing the base set of changes, so the whole csla functions will work. But when I bind my Grid not to this LinqBindingList?

Is this issue fixed in 3.6.2? Did not upgrade yet...

JoeFallon1 replied on Thursday, April 02, 2009

When you run LINQ against an Editable Child Collection the query itself is some variation of IQueryable.

You must cast the result of the query to LinqBindingList in order to treat it as one.

Joe

Woggly replied on Thursday, April 02, 2009

I just don't get it Sad [:(]

if (direction.ToString() == "Descending")

{

var sortedList = (LinqBindingList<UIBuchung>) from p in set.AsEnumerable() orderby (p.Betrag) descending select p;//set.OrderBy("Valuta");

this._buchungGrid.ItemsSource = sortedList;

}

else

{

var sortedList = (LinqBindingList<UIBuchung>) from p in set.AsEnumerable() orderby (p.Betrag) select p;

this._buchungGrid.ItemsSource = sortedList;

}

 

But when casting to LinqBindingList I get an error:
The Object of type "System.Linq.OrderedEnumerable`2[MeinGeld.DatabaseAccess.UIBuchung,System.Nullable`1[System.Decimal]]" could not be converted into type of "Csla.LinqBindingList`1[MeinGeld.DatabaseAccess.UIBuchung]".

What am I doing wrong?

Luzius replied on Thursday, April 02, 2009

Hi Woggly,

the LinqBindingList is a Csla class and the result of the Linq query will only be of type LinqBindingList if the original collection was derived from a Csla Collection class and not if it's a mere IEnumerable.

That's at least as far as I understand it.

Luzius

Woggly replied on Friday, April 03, 2009

Luzius:

Hi Woggly,

the LinqBindingList is a Csla class and the result of the Linq query will only be of type LinqBindingList if the original collection was derived from a Csla Collection class and not if it's a mere IEnumerable.

That's at least as far as I understand it.

Luzius

 

Yes I know. But the problem is that the List will not get sorted when I remove .AsEnumerable()!
And when I write .AsEnumerable it is no LinqBindingList. And as I understand then the linking to the original source is lost and changes will not be noticed (so is dirty and commands connected to issavable will not work).

Luzius replied on Friday, April 03, 2009

I have the following Property in my BO:

private static readonly PropertyInfo<UrgencyEditCollection> urgenciesProperty = RegisterProperty<UrgencyEditCollection>(c => c.Urgencies);

public LinqBindingList<UrgencyEdit> Urgencies
{
   get
   {
      
IOrderedQueryable<UrgencyEdit> query =
         
from urgency in GetProperty(urgenciesProperty)
         orderby urgency.StartDateParsableField.Date , (int) urgency.UrgencyType.Code descending
         select urgency;

         return (LinqBindingList<UrgencyEdit>) query;
   }
}

UrgencyEditCollection is a direct decendant of BusinessListBase

UrgencyEdit is a decendant of BusinessBase.

The sorting works without problems.

 I have some unsolved problems with databinding it in Wpf. To resort, I currently have to call a Rebind on the CslaDataProvider it's not automatically done when a property changes.

Hope this helps

JoeFallon1 replied on Friday, April 03, 2009

Luzius:

I have the following Property in my BO:


private static readonly PropertyInfoUrgencyEditCollection> urgenciesProperty = RegisterPropertyUrgencyEditCollection>(c => c.Urgencies);


public LinqBindingListUrgencyEdit> Urgencies
{
   get
   {
      
IOrderedQueryableUrgencyEdit> query =
         
from urgency in GetProperty(urgenciesProperty)
         orderby urgency.StartDateParsableField.Date , (int) urgency.UrgencyType.Code descending
         select urgency;

         return (LinqBindingListUrgencyEdit>) query;
   }
}


UrgencyEditCollection is a direct decendant of BusinessListBase


UrgencyEdit is a decendant of BusinessBase.


The sorting works without problems.


 I have some unsolved problems with databinding it in Wpf. To resort, I currently have to call a Rebind on the CslaDataProvider it's not automatically done when a property changes.


Hope this helps



This code looks fine. So does your explanation of it.

JoeFallon1 replied on Friday, April 03, 2009

Woggly:

I just don't get it Sad [:(]


if (direction.ToString() == "Descending")


{


var sortedList = (LinqBindingListUIBuchung>) from p in set.AsEnumerable() orderby (p.Betrag) descending select p;//set.OrderBy("Valuta");


this._buchungGrid.ItemsSource = sortedList;


}


else


{


var sortedList = (LinqBindingListUIBuchung>) from p in set.AsEnumerable() orderby (p.Betrag) select p;


this._buchungGrid.ItemsSource = sortedList;


}


 


But when casting to LinqBindingList I get an error:
The Object of type "System.Linq.OrderedEnumerable`2[MeinGeld.DatabaseAccess.UIBuchung,System.Nullable`1[System.Decimal]]" could not be converted into type of "Csla.LinqBindingList`1[MeinGeld.DatabaseAccess.UIBuchung]".


What am I doing wrong?



This code does not look fine at all.

You write from p in set.AsEnumerable() ...
What kind of BO is "set"?

Is it an Editable Child Collection?
Is it a ReadOnlyCollection?
Or something else?

Joe


JoeFallon1 replied on Friday, April 03, 2009

For a ReadOnlyCollection I use code like this:

'At the top of the page:
Private mSortedList As System.Linq.IQueryable

'in a method to set grid datasource:
mSortedList = mROList.AsQueryable.OrderBy(mSortColumn & " " & mSortDirection)

dg.DataSource = mSortedList.ToList


'In Item_DataBound:
Dim item As ROList.Info = CType(mSortedList(mIndex), ROList.Info)

The key idea is:
For Read Only Collections, you do NOT get back a LinqBindingList.

HTH
Joe

Woggly replied on Monday, April 06, 2009

Hi Joe.

My set is a decendant of BusinessListBase.

But I think I solved my problem with the help of Luzius' posted code.
Now I have got a property SortedList of type IOrderedEnumerable in my ViewModel to which the DataGrid is bound to. When performing a sort the SortedList gets updated and the list is sorted like I wanted.
Now I only have to add the dynamic Linq and everything will be fine!

Thank you!

Edit:

I tried to get this working wih dynamic Linq but it seems that this is not working with my IOrderedEnumerable...

In my ViewModel I have:

private IOrderedEnumerable<UIBuchung> _sortedSet;
public IOrderedEnumerable<UIBuchung> SortedSet
{
   
get { return _sortedSet; }
   
set
   
{
      
this._sortedSet = value;
      OnPropertyChanged(
"SortedSet");
   }

}

The DataGrid is bound to this List. When a user clicks into the header of the Grid I am calling my custom Sorting Method:

private void PerformCustomSort(DataGridColumn column)
{
   
ListSortDirection direction = (column.SortDirection != ListSortDirection.Ascending)    
   
ListSortDirection.Ascending : ListSortDirection.Descending;
   column.SortDirection = direction;
   
BuchungViewModel model = (BuchungViewModel)this.DataContext;
   
UIBuchungSet set = model.DatabaseEntitySetModel; // DatabaseEntitySetModel = BusinessListBase descendant

   if (set != null)
   {
      
if (direction.ToString() == "Descending")
      {
       
var sortedList = from p in set.AsEnumerable() orderby (p.Betrag) descending select p;
         
model.SortedSet = sortedList;
         column.SortDirection =
ListSortDirection.Descending;
      
}
      
else
      
{
         
var sortedList = (IOrderedEnumerable<UIBuchung>) set.OrderBy("Betrag");
         
model.SortedSet = sortedList;
         column.SortDirection =
ListSortDirection.Ascending;
      
}
   

}

I really don't know if this is the right way to do this! All I want to achieve is, that the Grid gets sorted when the user wants it. But When I am trying the easiest way like:
_grid.ItemsSource = from p in set orderby(p.Betrag) select p; my list is not getting sorted!
And another problem with my implementation... When I am doing it the way described above I have a problem with the connection between sortedList and original list.
I am visualizing a read state in the grid. this is bound to a property IsRead in the BO from the set. When a user selects a row this property is set to true and I am saving the DatabaseEntitySetModel. But the Sorted List (of course??) gets not updated. i would have to do another sorting after saving.

My question is. Which is the best way to achieve this? I see I am somehow stuck in my implementation. Would be great if you can give my some more tips!
Thank you.

Copyright (c) Marimer LLC