来自ToolTip或ContextMenu的RelativeSource绑定

时间:2010-09-08 14:18:57

标签: wpf xaml binding relativesource

我在这里做错了什么?:

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

这只是一个简单的例子,无论如何都不起作用:) 实际上我需要从Window的DataContext范围内的另一个属性中获取一个值。

请帮帮我。

4 个答案:

答案 0 :(得分:69)

这很棘手,因为ToolTip不是VisualTree的一部分。 Here您看到了与ContextMenus相同问题的一个很酷的解决方案。您可以使用与工具提示相同的方式。

<强>更新
可悲的是,链接已经消失了,我还没有找到引用的文章。
据我所知,引用的博客已经展示了如何绑定到另一个VisualTree的DataContext,这在从ToolTip,ContextMenu或Popup进行绑定时经常是必需的。

一种很好的方法是在PlacementTarget的Tag-property中提供所需的实例(例如ViewModel)。以下示例用于访问ViewModel的Command实例:

<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>

我没有测试过,很长一段时间我都是这样做了。如果它不适合你,请发表评论。

更新2

由于这个答案的原始链接已经消失,我点击了archive.org和found the original blog entry。这是博客中的逐字:

  

因为WPF中的ContextMenu不存在于可视树中   你的页面/窗口/控制本身,数据绑定可能有点棘手。   为了这个,我在网上搜索了高低   常见的答案似乎是“只是在代码背后做”。错误!一世   没有进入XAML的精彩世界回归   在后面的代码中做事。

     

这是我的例子,它允许你绑定到一个字符串   作为窗口的属性存在。

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>
     

重要的部分是按钮上的标签(尽管你也可以   轻松设置按钮的DataContext)。这存储了对的引用   父窗口。 ContextMenu能够访问它   通过它的PlacementTarget属性。然后,您可以传递此上下文   浏览菜单项。

     

我承认这不是世界上最优雅的解决方案。   但是,它在背后的代码中胜过设置。如果有人有   更好的方式,我很乐意听到它。

答案 1 :(得分:3)

以下:
PlacementTarget是拥有ContextMenu的控件(例如:DataGrid)。不需要&#34;标签&#34;属性。

IsEnabled绑定到DataGrid&#34; myProperty&#34;值。

我测试了这个并且它有效。与绑定有类似的问题。

<ContextMenu
DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource Self}}"
IsEnabled="{Binding myProperty}"  
>

答案 2 :(得分:0)

由于ContextMenu不在可视树中,因此绑定不起作用。 一个简单的解决方案是使用代理模式,您可以创建一个继承自DependencyObject的包装类,并且DependencyProperty将保留DataContext Window,然后您可以拥有XAML中的代理资源,最后通过代理对象将MenuItem命令绑定到所需的命令。
示例代理:

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

}

如何在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>

发生了什么事?
Data ProxyClass的{​​{1}}属性将绑定到DataContext的{​​{1}},然后它Window中包含ViewModel的所有comamnds和属性资源。
这种方法的另一个好处是可移植性和在多个视图和项目中重用。

答案 3 :(得分:-2)

我认为应该这样做:

{Binding Path=Title, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"