2

I have a TextBlock in my UI bound to the .Count() extension of a property. I would like to add LINQ filtering to the property, but when I do it breaks the binding.

This works and updates the UI without any problems:

public IEnumerable<Worker> WorkersTest
{
    get
    {
        return DataManager.Data.Workers; 
    }
}

After adding LINQ filtering, WorkersTest does not update the TextBlock, though a MessageBox confirms that the property is being modified. Upon initialization of the data, however, the value displays correctly:

public IEnumerable<Worker> WorkersTest
{
    get
    {
        return DataManager.Data.Workers.Where(w => w.Gender == Gender.Male); 
    }
}

Is there any way to keep the LINQ filtering without breaking the binding?

Update: One thing that I need to clarify is that DataManager.Data.Workers is an ObservableCollection.

From what I can tell, any LINQ operation breaks the binding with the exception of AsEnumerable(), which doesn't have any effect.

Also, in response to Andres' answer, Count() isn't the problem. If I bind this property to a DataGrid, I get the same results. With LINQ, it doesn't update. Without LINQ, it does. Is there any sort of workaround for this, preferably one that doesn't involve ICollectionView or anything of that nature?

Update: Does anybody know if LINQ extensions such as Where return new objects when used? I haven't been able to find any documentation of this online. If this is the case, however, that may be the problem.

Update: I inserted a breakpoint and found some interesting information that may be useful:

Without LINQ:

enter image description here

With LINQ:

enter image description here

enter image description here

Perhaps this can shed some light on what's really going on and hopefully get me closer to the solution.

Would I be correct in assuming that CollectionChanged and PropertyChanged being null is the source of the problem? If so, what can I do to fix that?

Jason D
  • 2,634
  • 6
  • 33
  • 67
  • Is your binding mode set? Mode=TwoWay. – Guilherme H. J. Jul 19 '13 at 20:57
  • That throws an InvalidOperationException. The Count property is read-only. – Jason D Jul 19 '13 at 21:35
  • Linq query will be lazy, it does nothing until it is evaluated i.e you call ToList(). Also Binding to a query like that is not very good. Bind to a property that raises PropertyChanged event in INotifyPropertyChanged: http://msdn.microsoft.com/en-us/library/system.componentmodel.inotifypropertychanged.aspx this will tell the UI it needs to update. – Mark Homer Jul 20 '13 at 17:27
  • The problem is that the setter for the collection is never called. – Jason D Jul 20 '13 at 18:05

2 Answers2

1

Your problem stems from not understanding observable collections. DataManager.Data.Workers is ObservableCollection or something similar. It means, it will raise events when items are added/removed. The UI can then listen to those events and update appropriately.

LINQ doesn't understand anything about observable collections. It just reads and transforms data into new collection, one time, one way. So using LINQ for your filtering is not going to work. Or at least not without some kind of hack.

There are definitely few ways, like Filtering an ObservableCollection? or Sorts and filters on ObservableCollection. But nothing so easy as just throwing a simple LINQ on it. Or maybe Reactive Extensions could help you here. But I never used it so I don't know how easy would it be to setup a filtering observable collection.

Community
  • 1
  • 1
Euphoric
  • 12,645
  • 1
  • 30
  • 44
  • I'm not a fan of CollectionViews since they have to be manually refreshed. I found an extension called OLinq today that allows LINQ to be bindable. I'm trying that out so we'll see how that goes. If there was a way to automatically refresh a CollectionView that would be perfect... unfortunately I haven't found a way to. – Jason D Jul 20 '13 at 19:38
0

Try adding .ToList() at the end of your Where clause.

return DataManager.Data.Workers.Where(w => w.Gender == Gender.Male).ToList(); 

Binding to methods isn't allowed so what is probably happening is you are binding to the Count property instead of method. List contains the Count property whereas IEnumerable does not.

  • The original probably works because `DataManager.Data.Workers` is a List. – McGarnagle Jul 19 '13 at 21:24
  • Sorry, I should have clarified that `DataManager.Data.Workers` is an `ObservableCollection`. I tried changing it to a list and adding `ToList()`, though, and it still doesn't work. – Jason D Jul 19 '13 at 21:40
  • Actually, adding `ToList()` breaks the binding on the first example (without LINQ). – Jason D Jul 19 '13 at 22:05
  • Also, when the data is initialized, it displays the correct value. It just doesn't update. – Jason D Jul 19 '13 at 22:22