Monday, February 04, 2008

I'm a strange man: I seem to be equally interested in EntityFramework and in WPF. They are such different beasts, and still I take great pleasure in using them both! That's possibly because I view them as enablers of the kind of projects I like to do. Weird.

Anywho, it's been a long time since I blogged about WPF. And even longer since I blogged about unittesting WPF. The simple trick in this post is probably widely used already in the community: I haven't paid any attention ;-)

In this post, I explained how to setup a tracelistener to listen for binding errors. In the months that followed, this proved to be less than convenient! In WPF views, even when everything is setup great, there might be binding errors that you wish to accept. For instance: a view binds to an instance of type Foo, and is later subsituted by an instance of type Bar. Bar has the same properties as Foo, except 1. The binding engine just clears the bound label, and you are fine with it (yes, sure.. it smells a bit, but you get the example).
Using the tracelistener, you have less control over what the process.

It is much better to have total control over the binding objects in a view. With some exceedingly simple methods, you can get to them to query their status:

First, let's start with an enumeration over all the visuals in your view:

public
IEnumerable<Visual> EnumVisual(Visual visual)
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(visual); i++)
 {
   Visual childVisual = (Visual) VisualTreeHelper.GetChild(visual, i);
   yield return childVisual;
 }
}

Then, we use some cleverness by dr. WPF that enumerates all the bindings found on a visual:

private IEnumerable<BindingExpression> EnumerateBindings(DependencyObject target)
{
   if(target is ContentControl && ((ContentControl)target).Content is DependencyObject)
   {
      EnumerateBindings( (DependencyObject) ((ContentControl)target).Content);
   }

LocalValueEnumerator lve = target.GetLocalValueEnumerator();

while (lve.MoveNext())
   {
      LocalValueEntry entry = lve.Current;

      if (BindingOperations.IsDataBound(target, entry.Property))
      {
         Binding binding = (entry.Value as BindingExpression).ParentBinding;

         yield return entry.Value as BindingExpression;
      }
   }
}

It uses the GetLocalValueEnumerator, which is a largely unknown method that gets all the properties on a dependencyobject.
I first check to see if the target is a content control, if it is, I go for its content as well.

Now, let's see all the bindings:

private IEnumerable<BindingExpression> GetFlattenedBindings(Visual root)
{
   foreach (Visual child in EnumVisual(root))
   {
      foreach (BindingExpression childBinding in GetFlattenedBindings(child))
      {
         yield return childBinding;
      }

      foreach (BindingExpression binding in EnumerateBindings(child))
      {
         yield return binding;
      }
   }
}

Use it in your unittest to get all the bindings that are available in the visualtree. Test specific bindings or just fail if one breaks.

foreach(BindingExpression b in GetFlattenedBindings(this))
{
   Debug.WriteLine(b.ParentBinding.Path + "=" + b.Status + " on item:" + b.DataItem.ToString() );
}

Ofcourse, it's cool to change the helper methods to extension methods.

Beware that I have not really tested the code. Copy pasting it, I see a few errors, but you get the drift.

 

Thursday, November 25, 2010 12:43:53 PM (Romance Standard Time, UTC+01:00)
Use it in your unittest to get all the bindings that are available in the visualtree. Test specific bindings or just fail if one breaks.
Thursday, December 02, 2010 3:48:14 AM (Romance Standard Time, UTC+01:00)
This article was Very helpful. Actually, I am fond of reading online punjabi news. Thanks for writing such a complete ..
thank you for sharing.
Comments are closed.