RelativeSource binding from a ToolTip or ContextMenu
Asked Answered
T

4

37

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.

Tatouay answered 8/9, 2010 at 14:18 Comment(0)
M
85

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.

Mueller answered 8/9, 2010 at 14:23 Comment(3)
@Bill I have not found the link anymore but I have tried to explain the way how the problem can be resolved.Mueller
Massively helpful and much simpler than the 'namescope hack' solutions that put me off using contextmenus and tooltips previously. This is disgustingly simple and works perfectly.Picro
@ChrisValentine Feel free to mess with my edit; I just wanted to make sure the original solution was recorded here.Lightly
D
6

Per below:
PlacementTarget is the control that owns the ContextMenu (ex: DataGrid). No need for a "tag" property.

IsEnabled binds to the DataGrid's "myProperty" value.

I tested this and it works. Was having similar issue with binding.

<ContextMenu
DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource Self}}"
IsEnabled="{Binding myProperty}"  
>
Dense answered 24/6, 2017 at 21:45 Comment(0)
M
-1

Because ContextMenu is not in visual tree, binding will not work. a simple solution is using Proxy Pattern, you can create a wrapper class that inherits from DependencyObject and has a DependencyProperty that will keep a DataContext of your Window, then you can have a resource of the proxy in XAML and finally bind your MenuItem command to your desired command via the proxy object.
Sample Proxy:

Public class ProxyClass : DependencyObject
{
    Public object Data {get; set;}
   public static readonly DependencyProperty DataProperty = DependencyProperty.Register("DataProperty", typeof(object), typeof(ProxyClass), new FrameworkPropertyMetadata(null));

}

How to use in XAML:

<Window DataContext="{Binding MyViewModel}">
...
<Window.Resources>
    <ProxyClass Data={Binding} x:Key="BindingProxy"/>

</Window.Resources>
...  
<MenuItem Command="{Binding Source={StaticResource BindingProxy}, Path=Data.MyDesiredCommand"/>
...
</Window>

What is happening?
Data property of ProxyClass will bind to DataContext of Window, then it has all of your comamnds and properties of your ViewModel inside the ProxyClass resource.
another benefit of this approach is portability and re-using in multiple views and projects.

Mcclintock answered 6/1, 2017 at 12:40 Comment(3)
this approach can be use only on the same window xaml codeGelatinous
@TuanTran what do you mean exactly of "only on the same window xaml code"?Mcclintock
You cannot split the <Window.Resources>...</Window.Resources> part to a separated resource fileGelatinous
T
-3

I think it should be done like this:

{Binding Path=Title, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
Trochelminth answered 8/9, 2010 at 14:24 Comment(2)
This works not because the main VisualTree is not available from the ToolTip. See my answer.Mueller
yah sorry, didn't actually even notice that it was within a tooltip.Trochelminth

© 2022 - 2024 — McMap. All rights reserved.