ElementName Binding In ToolTips (Borrowing a NameScope)

I had previously seen mention of the limitation that you cannot use ElementName binding within a ToolTip but I never bothered to investigate it since I didn’t have need to use it. Yesterday Josh asked me about it so I decided to look into it further. I’m going to deal with ToolTip here since that was the one he asked me about but I believe the same technique could apply to ContextMenu.

First let’s take a look at the various ways you can define a tooltip that uses ElementName in a binding and see which work.

<Window.Resources>
    <!– Shared tooltip –>
    <ToolTip x:Key=”sharedTT”>
        <TextBlock Text=”{Binding ElementName=txt, 
           Path=Text}” />
    </ToolTip>
</Window.Resources>
<DockPanel>
    <TextBox Text=”This is the tooltip text” 
            x:Name=”txt” DockPanel.Dock=”Top” />
   
    <!– 1 – explicitly provide a tooltip instance where
        the content is bound –>
    <Button Content=”Explicit ToolTip” DockPanel.Dock=”Top” >
        <ToolTipService.ToolTip>
            <ToolTip Content=”{Binding ElementName=txt, 
               Path=Text}” />
        </ToolTipService.ToolTip>
    </Button>
   
    <!– 2 – Set the tooltip to a binding –>
    <Button DockPanel.Dock=”Top” Content=”Binding”
           ToolTipService.ToolTip=”{Binding ElementName=txt, 
           Path=Text}”/>

 

    <!– 3- Set the tooltip to an object that tries to
        bind to an element outside the tooltip –>
    <Button DockPanel.Dock=”Top” Content=”Element ToolTip”>
        <ToolTipService.ToolTip>
            <TextBlock Text=”{Binding ElementName=txt, 
               Path=Text}” />
        </ToolTipService.ToolTip>
    </Button>

 

    <!– 4- Set the tooltip to an element that tries to bind
        to the value of another element within the tooltip –>
    <Button DockPanel.Dock=”Top” >
        <ToolTipService.ToolTip>
            <StackPanel>
                <TextBlock x:Name=”ttText” Text=”Foo” />
                <TextBlock Text=”{Binding ElementName=ttText, 
                   Path=Text}” />
            </StackPanel>
        </ToolTipService.ToolTip>
        Normal ToolTip
    </Button>

 

    <!– 5 – Bind to a ToolTip in Resources –>
    <Button DockPanel.Dock=”Top” 
           Content=”SharedResource ToolTip”
           ToolTipService.ToolTip=”{StaticResource sharedTT}”/>
</DockPanel>

Of all of these the only one that actually works is #2 where the ToolTip property is set to a Binding instance. Suprisingly, at least to me, is that even #4 where we are trying to bind to another element within the tooltip itself does not work.

In order to understand why these aren’t working, we need to understand how ElementName binding works. Basically, when an ElementName is used in a binding, the NameScope of the target object is used to locate the element with the specified name. If that element doesn’t have a NameScope specifically on it, the FrameworkElement.FindScope method continues up the logical tree and falls back to the inheritance context if there is no logical parent. The name scope is the object in which all named objects have been registered. So in this example, there is a NameScope created for the Window itself implicitly. Other objects also provide a namescope to prevent conflicts between names – e.g. ControlTemplate and Style. Actually in those cases, the objects themselves implement INameScope.

The ToolTip however is not part of the logical nor visual tree of the element on which it is being set. Instead, it is just the value of a property and as such it doesn’t have a way to reach the NameScope of the Window in which it was created. The reason that #2 worked is because we just set the value of the ToolTip property to a binding. Since that is a property set directly on the element, that binding has access to the namescope just as you can use in a binding for any other dependency property on the element.

So the issue we have to overcome is how to provide a way for the ToolTip to get to the NameScope of the Window. My solution was to set the NameScope of the ToolTip to a custom INameScope implementation that would get to the NameScope of the element for which the ToolTip was being used. To accomplish this, I defined a new attached property named BindableToolTip. When set, it would set the NameScope property of the tooltip (and create a tooltip if the value wasn’t a tooltip instance such as the case where you are just providing the elements that make up the content of the tooltip) to my custom INameScope implementation that would “borrow” the namescope of the element on which the tooltip was being set. I then set the real ToolTipService.ToolTip property to that tooltip instance.

I did hit one glitch along the way. The BamlRecordReader class which is used to process the compiled baml uses a stack to manage the namescopes it encounters. By the time that the BindableToolTip property change is invoked, the BamlRecordReader has already processed the ToolTip instance (through its PushContext method). So when it gets to the point where it wants to clean up its namescope stack (in its PopContext method), it finds that the tooltip now has a namescope and tries to pop an item off its stack which results in an exception because it tries to pop off more items then it pushed. To get around this, I remove the namescope from the tooltip in the Initialized event of the tooltip.

To use the new functionality, you would just replace any place you are setting the ToolTip property with the BindableToolTip property. After doing so with the sample above, every case now works. I’ve attached the sample project that defines and uses this new attached property.

About these ads

12 Responses to “ElementName Binding In ToolTips (Borrowing a NameScope)”

  1. Enable ElementName Bindings with ElementSpy « Josh Smith on WPF Says:

    [...] 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 [...]

  2. NameScope, my name is Marlon you know…. « C# Disciples Says:

    [...] might want to also implement your own NameScope by implementing the INameScope interface. I saw a very clever solution by doing such a thing, by WPF super hero Andrew [...]

  3. AlexM Says:

    I found your site on technorati and read a few of your other posts. Keep up the good work. I just added your RSS feed to my Google News Reader. Looking forward to reading more from you down the road!

  4. Recent Links Tagged With "contextmenu" - JabberTags Says:

    [...] public links >> contextmenu ElementName Binding In ToolTips (Borrowing a NameScope) Saved by janetmck on Sat 27-9-2008 EZ Wipe 1.0 Saved by crsalazar on Fri 26-9-2008 keep on [...]

  5. Echilon Says:

    Excellent post. I was starting to think I’d just have to hard code the property.

  6. Drive Backup Software Says:

    There in fact obviously a lot to know about this. I think you made some extremely points in Features also. Keep working, fine job!

  7. Preeti Says:

    The doc doesn’t open. Can you please verify the download link.

  8. Andrew Smith Says:

    Preeti, did you change the extension from .doc to .zip? WordPress doesn’t allow zip file attachments so I have a tooltip indicating that the extension must be changed.

  9. Joy Says:

    Hi,
    I have issues doing the following:
    So I am defining a style for the Listbox item and binding the listbox using itemsource and assing the style to itemtemplatestyle.

    Now, I wan to show a tooltip for the listbox item, with a property bound to the listboxitem.

    its doesnt seem to work and in this case i cannot use element name as binding has been used.

  10. Saurabh Sharma Says:

    I have a List box which is bound to a collection of different type in a inheritance chain. I have written implicit data templates for each type. I want to bind one control in a list box item to another control in another list box item , please guide me.

  11. How can I give a group of controls a single tooltip, and have it appear smoothly? | SeekPHP.com Says:

    [...] this is something that I have accomplished by using BindableToolTips: I simply define the ToolTip in my XAML resources, and then set the same ToolTip object [...]

  12. me va be Says:

    me va be…

    [...]ElementName Binding In ToolTips (Borrowing a NameScope) « Andrew Smith[...]…

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


Follow

Get every new post delivered to your Inbox.

%d bloggers like this: