RelativeSource binding from a ToolTip or ContextMenu

iLemming picture iLemming · Sep 8, 2010 · Viewed 20.8k times · Source

What am I doing wrong here?:

 <GridViewColumn>
    <GridViewColumn.CellTemplate>
       <DataTemplate>
          <Button>
            <Button.ToolTip>
              <TextBlock Text="{Binding Path=Title, RelativeSource={RelativeSource AncestorType=Window}}" />

That's just a simplified example, that doesn't work anyway :) Actually I need to get a value from another property that is in scope of the Window's DataContext.

Help me pls.

Answer

HCL picture HCL · Sep 8, 2010

This is tricky because ToolTip is not part of the VisualTree. Here you see a cool solution for the same problem with ContextMenus. The same way you can go for the ToolTip.

UPDATE
Sadly the link is gone and I have not found the referenced article anymore.
As far as I remember, the referenced blog has shown how to bind to a DataContext of another VisualTree, which is often necessay when binding from a ToolTip, a ContextMenu or a Popup.

A nice way to do this, is to provide the desired instance (e.g. ViewModel) within the Tag-property of the PlacementTarget. The following example does this for accessing a Command-instance of a ViewModel:

<Button Tag="{Binding DataContext,RelativeSource={RelativeSource Mode=Self}}">
  <Button.ContextMenu>
    <ContextMenu>
       <MenuItem Command="{Binding PlacementTarget.Tag.DesiredCommand,RelativeSource={RelativeSource Mode=FindAncestor,AncestorType=ContextMenu}}" .../>
    <ContextMenu>
  </Button.ContextMenu>
</Button>

I have not tested it and its a long time I did this the last time. Please make a comment if it does not work for you.

UPDATE 2

As the original link that this answer was written about is gone, I hit archive.org and found the original blog entry. Here it is, verbatim from the blog:

Because a ContextMenu in WPF does not exist within the visual tree of your page/window/control per se, data binding can be a little tricky. I have searched high and low across the web for this, and the most common answer seems to be “just do it in the code behind”. WRONG! I didn’t come in to the wonderful world of XAML to be going back to doing things in the code behind.

Here is my example to that will allow you to bind to a string that exists as a property of your window.

public partial class Window1 : Window
{
    public Window1()
    {
        MyString = "Here is my string";
    }

    public string MyString
    {
        get;
        set;

    }
}


<Button Content="Test Button" 
     Tag="{Binding RelativeSource={RelativeSource AncestorType={x:Type Window}}}">
  <Button.ContextMenu>
    <ContextMenu DataContext="{Binding Path=PlacementTarget.Tag, 
          RelativeSource={RelativeSource Self}}" >
      <MenuItem Header="{Binding MyString}"/>
    </ContextMenu>
  </Button.ContextMenu>   
</Button>

The important part is the Tag on the button(although you could just as easily set the DataContext of the button). This stores a reference to the parent window. The ContextMenu is capable of accessing this through it’s PlacementTarget property. You can then pass this context down through your menu items.

I’ll admit this is not the most elegant solution in the world. However, it beats setting stuff in the code behind. If anyone has an even better way to do this I’d love to hear it.