Tuesday, December 2, 2008

WPF Binding to ObservableCollection and a Converter



So, I had nearly the exact same problem described here:
http://social.msdn.microsoft.com/Forums/en-US/wpf/thread/944c8058-8b9a-4833-b0fc-32c47d0900d5/

In a nutshell, when you bind an observable collection to a textbox using a converter (for example, to combine all the elements into a comma-separated string), and you add/remove items from the observable collection, the text box's text doesen't change (the converter doesn't fire).

Why? Well, the reference to the observable collection didn't actually change, so the binding doesn't get re-evaluated (even though the number of members do change).

Very frustrating... So, because I'm doing dyanamically loaded xaml from a database table (via System.Windows.Markup.XamlReader.Load), I need a very generic way to handle this...

So, my solution is to iterate over all the controls on the page (using
System.Windows.LogicalTreeHelper.GetChildren) and invoke the UpdateTarget method on the dependency property's BindingExpression (where appropriate, of course).


So, going into this snippet of code:




source: is the control that hosts the observable collection that had an item added/removed.


_allControls: a List<> of all FrameworkElements on page (found via recurisve function calling
System.Windows.LogicalTreeHelper.GetChildren(dependencyobj)).




--- snip---

foreach (var c in _allControls)
{

System.Windows.LocalValueEnumerator lve;

lve = c.GetLocalValueEnumerator();
while (lve.MoveNext())
{
System.Windows.LocalValueEntry e = lve.Current;
if(System.Windows.Data.BindingOperations.IsDataBound(c, e.Property))
{
System.Windows.Data.BindingExpression be;
be = (System.Windows.Data.BindingExpression)e.Value;

if(be.DataItem == source)
{
be.UpdateTarget();
}
}
}
}


So, does anyone have a better solution than this?

1 comment:

Anonymous said...

Just stumbled over the same issue - needed to create a comma-separated list based on a collection and needed a moment to realize that I published the solution a few days ago myself :-)

Check out the Lambda Dependencies library here:
Lambda Dependencies

Using the library, it's literally a no-brainer:
1: Create a dependency on the collection
2: Update the textblock in the dependency node's event listener:

Create the dependency:

dependency = DependencyBuilder.CreateDependency(() => SomeProperty.SomeCollection, OnCollectionChanged);


Event listener:

private void OnCollectionChanged(object sender, IDependencyChangeInfo e)
{
//refesh text box
txtCsv.GetBindingExpression(TextBox.TextProperty).UpdateTarget();
}

done :)