Binding to INotifyCollectionChanged event

S

Some Bloke

I am trying to bind a WPF dependency property to an aggregate of a
collection. I've implemented a wrapper around a Dictionary that
implements INotifyCollectionChanged to fire the event when items are
added or removed.

Debugging through it I notice that there is nothing listening to the
event after I bind to it which I expected the SetBinding to do as I
don't know what delegate to attach to the event. It's the binding that
needs updating after all.

I've tried a test with just an ObservableCollection to see if I was
missing something but that doesn't work either.

How do I wire up the event to the binding or do whatever it takes to
get my test to pass?

Here's my test code:

[TestClass()]
public class ObservableDictionaryTest : DependencyObject
{
public class MaxDoubleConverter : IValueConverter
{
public object Convert(object value, Type targetType, object
parameter, System.Globalization.CultureInfo culture)
{
if (value is IEnumerable<double>)
{
return ((IEnumerable<double>)value).Max(); // using
Linq
}

throw new ArgumentException("Cannot covert " +
value.GetType() + " to IEnumerable<double>", "value");
}


public object ConvertBack(object value, Type targetType,
object parameter, System.Globalization.CultureInfo culture)
{
throw new NotImplementedException();
}
}

public double Max
{
get { return (double)GetValue(MaxProperty); }
}

public static readonly DependencyProperty MaxProperty =
DependencyProperty.Register("Max", typeof(double),
typeof(ObservableDictionaryTest), new PropertyMetadata(0.0));

[TestMethod()]
public void TestBindingToObservableCollection()
{
ObservableCollection<double> values = new
ObservableCollection<double>();
values.Add(1.0);
values.Add(2.0);
values.Add(3.0);

Binding b = new Binding();
b.Converter = new MaxDoubleConverter();
b.Source = values;
b.Mode = BindingMode.OneWay;
BindingOperations.SetBinding(this, MaxProperty, b);

Assert.AreEqual(3.0, Max); // this works

values.Add(4.0);
Assert.AreEqual(4.0, Max); // this doesn't

values.Remove(4.0);
Assert.AreEqual(3.0, Max);
}
}
 
S

Some Bloke

What about using ObservableCollection<T>?

--
Pete
=========================================
I use Enterprise Core Objects (Domain driven design)http://www.capableobjects.com/
=========================================

That's what the test I've posted does or do you mean use it in another
way? It's only my test class that is called ObservableDictionaryTest
(maybe I should have renamed it if that caused confusion).

I'd expect the Binding.Source setter to check if the source is
IEnumerable and if it implements INotifyCollectionChanged then listen
to the event and update the bound value when it fires.

I'm obviously not understanding how binding to an ObservableCollection
works.
 
C

Chris Jobson

I am trying to bind a WPF dependency property to an aggregate of a
collection. I've implemented a wrapper around a Dictionary that
implements INotifyCollectionChanged to fire the event when items are
added or removed.

Debugging through it I notice that there is nothing listening to the
event after I bind to it which I expected the SetBinding to do as I
don't know what delegate to attach to the event. It's the binding that
needs updating after all.

I've tried a test with just an ObservableCollection to see if I was
missing something but that doesn't work either.

How do I wire up the event to the binding or do whatever it takes to
get my test to pass?

I think the problem is that only bindings which expect to bind to a
collection (like ListBox.ItemsSource) check for and subscribe to
INotifyCollectionChanged (though I don't know exactly how they do it).

What you could do is make your collection a property of your class, bind to
the property, implement INotifyPropertyChanged, then subscribe to the
collection's INotifyCollectionChanged yourself and raise a PropertyChanged
event. This seems rather clumsy and I'm sure there ought to be a better
way - maybe someone more knowledgeable will point it out.

Chris Jobson
 
C

Chris Jobson

I am trying to bind a WPF dependency property to an aggregate of a
collection. I've implemented a wrapper around a Dictionary that
implements INotifyCollectionChanged to fire the event when items are
added or removed.

Debugging through it I notice that there is nothing listening to the
event after I bind to it which I expected the SetBinding to do as I
don't know what delegate to attach to the event. It's the binding that
needs updating after all.

I've tried a test with just an ObservableCollection to see if I was
missing something but that doesn't work either.

How do I wire up the event to the binding or do whatever it takes to
get my test to pass?

I think the problem is that only bindings which expect to bind to a
collection (like ListBox.ItemsSource) check for and subscribe to
INotifyCollectionChanged (though I don't know exactly how they do it).

What you could do is make your collection a property of your class, bind to
the property, implement INotifyPropertyChanged, then subscribe to the
collection's INotifyCollectionChanged yourself and raise a PropertyChanged
event. This seems rather clumsy and I'm sure there ought to be a better
way - maybe someone more knowledgeable will point it out.

Chris Jobson
 
S

Some Bloke

I think the problem is that only bindings which expect to bind  to a
collection (like ListBox.ItemsSource) check for and subscribe to
INotifyCollectionChanged (though I don't know exactly how they do it).

What you could do is make your collection a property of your class, bind to
the property, implement INotifyPropertyChanged, then subscribe to the
collection's INotifyCollectionChanged yourself and raise a PropertyChanged
event. This seems rather clumsy and I'm sure there ought to be a better
way - maybe someone more knowledgeable will point it out.

Chris Jobson

Thanks.

One other idea I had was that it was the DependencyProperty that
needed refreshing so I tried subscribing to the event and calling
InvalidateProperty(MaxProperty), but that didn't work either, it still
came out as 3.0 somehow and the Convert was never called again so the
3.0 isn't being invalidated.

I'll try your idea since the collection will be a property of a
UserControl once I get it working.
 
S

Some Bloke

After looking into this a bit more I found
http://www.drwpf.com/Blog/Default.aspx?tabid=36&EntryID=18 that told
me that it is a CollectionView that does the subscribing to
CollectionChanged in ListBox and the like.

Naturally I tried wrapping my collection in a CollectionView and
setting that as the source but now the Max property doesn't get
evaluated at all (my converter is never called) so thebinding is
treating the view differently to any other plain IEnumerable, like it
thinks it doesn't need to do anything if it already has a
CollectionView.

This seems like one of those times when I'm digging deeper than I need
to and all this clever Binding and CollectionView stuff does what I
need if only I knew how to use it properly.
 
C

Chris Jobson

After looking into this a bit more I found
http://www.drwpf.com/Blog/Default.aspx?tabid=36&EntryID=18 that told
me that it is a CollectionView that does the subscribing to
CollectionChanged in ListBox and the like.
Thanks for that link. I'd read it some time ago but had forgotten the bit
about CollectionView.
Naturally I tried wrapping my collection in a CollectionView and
setting that as the source but now the Max property doesn't get
evaluated at all (my converter is never called) so thebinding is
treating the view differently to any other plain IEnumerable, like it
thinks it doesn't need to do anything if it already has a
CollectionView.
I *think* (but could well be wrong - I find it all a bit confusing!) it's
still the same problem - binding Max to a CollectionView is more or less the
same as binding it to an ObservableCollection, and in both cases a change to
the collection won't re-evaluate Max because it isn't interested in
INotifyCollectionChanged. AIUI the special feature of CollectionView is that
it if your property is of type CollectionView then you can bind a collection
to it and the property will re-evaluate when the collection changes.

(By the way, I'm surprised your idea of InvalidateProperty(MaxProperty)
didn't work. I've never used InvalidateProperty myself, but from the
description in MSDN it sounds ideal.)

I've just come across what might be another solution to your problem -
BindableLINQ (http://www.codeplex.com/bindablelinq/). In particular this
post (http://www.paulstovell.com/blog/bindable-linq-bindable-aggregates)
seems to describe something very similar to your requirement. It's only a
beta, and I've no experience of it myself, but it might be worth
investigating.

Chris
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top