Enable ElementName Bindings with ElementSpy

There are some situations in WPF where the ElementName property of a Binding is useless.  One common example of this is when you try to perform an ElementName binding on elements in a tooltip.  WPF manages to pump an inherited DataContext into the elements in a tooltip, but it does not add a tooltip to a namescope, so you cannot use ElementName in their bindings.

If you try to use an ElementName binding on a tooltip element, the Output window will display this type of debugging info:

System.Windows.Data Error: 4 : Cannot find source for binding with reference ‘ElementName={0}’. BindingExpression:Path={1}; DataItem=null; target element is ‘{2}’ (Name=”); target property is ‘{3}’ (type ‘{4}’)

There have been some workarounds posted, such as the excellent approach that Andrew Smith posted shortly after we discussed this very issue.  His approach involves a custom implementation of INameScope that traverses the element tree to find a namescope to apply to a tooltip.  There is also the idea of binding to the ToolTip’s PlacementTarget, which still does not give you a functioning ElementName binding, but does allow you to bind to a certain element.  This blog post introduces yet another utility class that leverages the Hillberg Freezable Trick to gain access to an inheritance context from an object added to a resource dictionary.  This is similar to my DataContextSpy, only its purpose is a bit different.  ElementSpy, the new class, provides two ways to enable you to bind to any element in the element tree, from any element that is not able to use ElementName bindings (such as elements in a tooltip).

It is important to note that ElementSpy relies on the use of reflection, so it cannot be used in a partial trust application, such as a standard XBAP.

The ElementSpy class exposes two public properties, Element and NameScopeSource.  Element is a read-only property that returns the element in whose Resources collection the ElementSpy is placed.  You should only put an ElementSpy into one element’s Resources and once it is in a Resources collection, you should not remove it and add it to another element’s Resources.  Here is the Element property.

As mentioned above, the ElementSpy class relies on reflection to get at its InheritanceContext, which is an internal property of Freezable (which is ElementSpy’s base class).   If you have a policy that prohibits the use of reflection to coerce .NET into bending to your wills, you obviously cannot use this class.  When a Freezable, such as ElementSpy, is placed into an element’s Resources collection, the InheritanceContext property is set to that containing element.

The other property is an attached property called NameScopeSource.  That property allows you to assign an element the NameScope that was assigned to the element that contains an ElementSpy.  This property is a tad more confusing than the Element property, but, as we will soon see, it has very strong advantages.  Here is the property definition:

Now let’s see this in use.  The demo app looks like this:

The key point to notice is that the tooltip of the TextBlock displays information taken from properties of the Window.  This is achieved via ElementName bindings.  The beginning of the Window’s XAML looks like this:

It is important to note that we add the ElementSpy to the Window’s Resources, because we want it to “spy on” the Window.  Whatever element’s Resources collection an ElementSpy is added to will be the element returned by the spy’s Element property.  Now let’s see how the upper TextBlock in the demo app uses the Element property to bind its tooltip elements to the Window:

The key is that we reference the ElementSpy as a resource (in this case, a StaticResource) via the binding’s Source property.  Also, the binding’s Path includes the Element property.  ElementSpy exposes that property, which returns whatever element the ElementSpy happens to live in the Resources collection of.  In this example, the Element property returns the demo Window.

Now let’s see how to use the attached NameScopeSource property.  Setting this property once enables multiple elements to use ElementName bindings.  Here is the lower TextBlock in the demo app:

As seen in the XAML above, using the NameScopeSource property enables true use of ElementName bindings.  This works because we are assigning the root TextBlock of the tooltip the same NameScope assigned to the Window.  Since the Window is in that scope and has an x:Name of “window”, the elements in the tooltip are able to find the Window via ElementName binding.

Download the source code here: Element Spy Demo .  Be sure to rename the file extension from .DOC to .ZIP and then decompress the file.

About these ads

3 Responses to Enable ElementName Bindings with ElementSpy

  1. sacha says:

    Nice atricle Josh…I may be using this one soon. Can see a need for it, like in a tooltip actually.

    Thanks

  2. [...] Josh Smith the WPF Rock star and his Element Spy is also a brilliant article that must be read.. Possibly related posts: (automatically generated)Marlon LuckyLucky to Be AliveThat’s How You Know [...]

Follow

Get every new post delivered to your Inbox.

Join 289 other followers

%d bloggers like this: