views:

108

answers:

4

Hi,

I have a ListView and ObservableCollection in my wpf application. I want to bind linq expression to ListView:

lv_Results.DataContext = store.Where(x => x.PR > 5).ToList();
tempStore = new M1Store()
                            {
                                SiteName = siteName,
                                Date = date,
                                url = IndexedPage,
                                TCY = YandexTCY.GetYandexTCY(IndexedPage),
                                PR = Pagerank.GetPR(IndexedPage)
                            };
            store.Add(tempStore);

But when i add new elements to store collection, lv_Results doesnt updates. How can i update ListView?

A: 

Add a .ToList() to your code, LINQ evaluates lazy, so it brings results only when needed, .ToList() is a greedy operator and forces an evaluation.

[edit]

Sorry, missunterstood your first version of the question :)

Here's my solution:

ObservableCollection<M1Store> l = store.Where(x => x.PR > 5);
lv_Results.DataContext = l;

Thats simply all, in all following steps, change the Observable collection l:

List<M1Store> otherList = GetFromAnywhere();
otherList.ForEach(e => l.Add(e));

Here the internals of the Observable Collection will Update the listView in the UI.

spookycoder
It doesnt work. I bind store collection when it's empty and adding elements after binding.
Neir0
+1  A: 

Put your LINQ results into an ObservableCollection. WPF and Silverlight databinding require that collections raise change notifications, and ObservableCollection does that for you. Otherwise you'd have to implement it yourself, which is more of a pain and completely unnecessary.

Cylon Cat
A: 

The results from the LINQ expression are being fed into a new List(of T) which does not raise PropertyChanged or CollectionChanged events.

The easiest way to make it work is to retrieve the results you want, then populate an ObservableCollection(of T) with the results you want to display. As that ObservableCollection gets added to, the new items will appear in the ListView.

Eric
+1  A: 

Your problem is that you want the "Where" condition to be evaluated continuously on all items added to the "store" collection. The built-in LINQ "Where" operator is not designed to do that. Rather, when it is enumerated by the ListView it scans your collection exactly once then ignores it from then on.

Check out Continuous LINQ. It is designed to do exactly what you are looking for, and can be used almost as a drop-in replacement for standard LINQ queries.

Limitations of the built-in LINQ implementation

The built-in LINQ extension methods have a fundamental limitation in that the collections they produce don't support INotifyPropertyChanged. So no matter how much the underlying data changes, the client will never receive notification that the collection has changed and hence will never refresh its display of the data.

User jrista points out in the comments that the built-in LINQ methods do actually produce up-to-date data if re-enumerated. While true, this has no practical effect. No WinForms or WPF control contains code to periodically re-enumerate its data source. The reasons for not doing so are, of course, obvious: It would be incredibly wasteful. If you re-enumerate 10 times per second and it takes 10ms to re-enumerate and scan for changes you will use up 10% of your CPU for just one control!

Ray Burns
Actually, your comment about Where is completely incorrect. Where WILL pick up new items added to the store collection each time the IEnumerable created by it is iterated. It is only when calling ToList that the results "framed in time", because they are then iterated and added to another collection. Please look at the internal where iterators via reflector: WhereArrayIterator<T>, WhereEnumerableIterator<T>, WhereListIterator<T>. In the initial state, the following is run: this.enumerator = this.source.GetEnumerator(); That retrieves an enumerator from the source AT THE TIME OF ITERATION.
jrista
@jrista: No, sorry, you are incorrect about this. In Neir0's code, the "time of iteration" you mention is exactly the moment the DataContext is assigned. The ListView immediately enumerates its ItemsSource and does so **exactly once**. Therefore the Where condition scans the collection **exactly once**. It doesn't matter whether or not Neir0 uses ToList(): He will get exactly the same results. (In fact, he did.) It would only update if ListView periodically re-enumerated the collection, which it does not. Therefore my answer **is** correct.
Ray Burns
@jrista: I didn't mean to imply you don't understand how Where works. I'm just trying to point out that you were so focused on the internals of Where that you missed the big picture - that none of the built-in LINQ functionality in NET Framework is continuous, and removing .ToList() in Neir0's code won't fix that. The Continuous LINQ folks developed their system to remedy this. I also developed something very similar several years back (so I too have a very deep understanding of LINQ) but my code is not yet public domain so I pointed Neir0 toward Continuous LINQ instead.
Ray Burns
It sounds like you are talking about a limitation in the interaction of the ListView and LINQ, not a limitation in the way LINQ's enumerators work. I would update your reply to indicate that, since Where (or any other LINQ method) WILL reprocess its source collection every time the enumerator generated by it is iterated.
jrista
Clearly you misunderstood me. I am definitely talking about a limitation of System.Data's implementation of LINQ, not of any particular LINQ client. Specifically, the collections produced by Enumerable.Where and its ilk *do not support INotifyCollectionChanged.* This makes it impossible for ListView *or any other client of the IEnumerable* to ever maintain a perfetly up-to-date view of the data. The very best that can be accomplished is periodic refresh at small (say, 10ms) intervals, but this is exhorbitantly expensive. Both Emerald Data Foundation and Continuous LINQ solve this problem.
Ray Burns