So You Want To Know What You're Bound To?
While the Data Binding infrastructure provided by the Windows Presentation Framework is an extremely powerful and flexible tool, that added power comes with a significant amount of complexity. One issue I ran into recently seems like a simple enough question to answer, and one that would come up often in daily development: 'To what am I bound?'
The simplest answer is, the DataContext for the control. This works for most scenarios because DataContext is a dependency property and will return whatever DataContext it finds up the containment hierarchy. However, there are ways to override the binding source for a particular binding, so this is not always the right answer.
So after a fair amount of digging we find the BindingExpression.DataItem property. This is better because it gives us a way to find the data source for the binding independent of the way the binding is actually configured. However, because of the power of the PropertyPath, we still only know the root item to which we are bound, not the actual instance that immediately contains the property to which we are bound.
So I turned to the web. Unfortunately, I found nothing. After hours of searching MSDN documentation, WPF blogs, and anything I could get my hands on, I still had nothing. But it's clear (because binding works) that internally, the BindingExpression knows what it is bound to.
So I turned to ildasm! Through a combination of run-time watches and ildasm references, I found the structures which contain the resolved property path for the binding expression. Unfortunately, they are all hidden in the MS.Internal.Data namespace deep in the private bowels of the framework.
So I turned to reflection! And now I have the answer. If I had access to all the MS.Internal.Data namespace structures (and private members :) I could simply write this expression to get the information:
SourceValueState[] svs =
((MS.Internal.Data.ClrBindingWorker)be._worker)._pathWorker._arySVS;
return (WeakReference)svs[svs.Length - 1].item;
The _arySVS array contains weak references to each instance variable along the resolved property path for the binding. So all we have to do is use reflection to get at the values and we can determine the immediate data source for any binding!
BTW, Sorry for the formatting, I'm still trying to figure out how to get that to work :)
public static object GetImmediateDataSource(BindingExpression be)
{
FieldInfo workerFieldInfo =
typeof(BindingExpression).GetField("_worker", BindingFlags.Instance | BindingFlags.NonPublic);
object worker = workerFieldInfo.GetValue(be);
FieldInfo pathWorkerFieldInfo =
worker.GetType().GetField("_pathWorker", BindingFlags.Instance | BindingFlags.NonPublic);
object pathWorker = pathWorkerFieldInfo.GetValue(worker);
FieldInfo sourceValueStateFieldInfo =
pathWorker.GetType().GetField("_arySVS", BindingFlags.Instance | BindingFlags.NonPublic);
Array sourceValueState = (Array)sourceValueStateFieldInfo.GetValue(pathWorker);
FieldInfo itemFieldInfo = sourceValueState.GetType().GetElementType().GetField("item");
return ((WeakReference)itemFieldInfo.GetValue(sourceValueState.GetValue(sourceValueState.Length - 1))).Target;
}
And there you have it! A generic method for retrieving the immediate source instance for any particular binding expression. I used this as part of a larger class that handled the differences between the different types of bindings (binding, multi- or priority) so I could generically find the set of objects to which any given control was bound. Couple this with the LocalValueEnumerator to get all the bindings for a control, and you can do some pretty generic wire-up code for things like field audit history display and the like.
I hope this post will help others that have had the same question!
Bryn Rhodes
Comments
Hey Bryn,
According to this: http://msdn2.microsoft.com/en-us/library/bb613588(VS.90).aspx there's going to be some new debugging support for data bindings in WPF 3.5. You can probably try that out by using the 2008 beta 2.
Myself, I've been avoiding the databindings a bit too much. Must be the hacker in me not wanting to give up control. :)
I tried using the WPF validation system. It sucks. It's another area I've heard they are improving in 3.5.
Posted by: Bryan Livingston | August 20, 2007 11:01 AM