从ToolTip或ContextMenu进行的RelativeSource绑定

37

我在这里做错了什么?

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

这只是一个简化的例子,实际上并不起作用 :) 实际上我需要从窗口数据上下文中作用域内的另一个属性获取值。

请帮帮我。

4个回答

84

由于 ToolTip 不是 VisualTree 的一部分,所以这有点棘手。这里有一个很酷的解决方案,用于解决上下文菜单的同样问题。你可以用同样的方法来解决 ToolTip 的问题。

更新
遗憾的是,该链接已经失效,我再也找不到那篇文章了。
据我记得,该博客展示了如何绑定到另一个VisualTree的DataContext,这在从ToolTip、ContextMenu或Popup进行绑定时经常需要。

一个不错的方法是,在PlacementTarget的Tag属性中提供所需的实例(例如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上搜寻并找到了原博客文章。以下是完全引用该博客的内容:

因为 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
@Bill,我已经找不到链接了,但我已经尝试解释了如何解决问题的方法。 - HCL
非常有帮助,比之前让我不想使用上下文菜单和工具提示的“命名空间黑客”解决方案简单得多。这个解决方案非常简单,而且完美地运行。 - Gusdor
@ChrisValentine 请随意修改我的编辑;我只是想确保原始解决方案被记录在这里。 - Lynn Crumbling

6
根据以下说明:
PlacementTarget 是拥有 ContextMenu 的控件(例如:DataGrid)。不需要“tag”属性。
IsEnabled 绑定到 DataGrid 的 “myProperty” 值。
我已经测试过了,这很有效。之前在绑定方面遇到了类似的问题。
<ContextMenu
DataContext="{Binding Path=PlacementTarget, RelativeSource={RelativeSource Self}}"
IsEnabled="{Binding myProperty}"  
>

-1

由于ContextMenu不在可视树中,绑定将无法工作。 一个简单的解决方案是使用代理模式,您可以创建一个继承自DependencyObject的包装类,并具有一个DependencyProperty,它将保留您的WindowDataContext,然后您可以在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>
发生了什么?
ProxyClassData属性将绑定到WindowDataContext,然后它将拥有ViewModel中所有命令和属性的ProxyClass资源。
这种方法的另一个好处是可移植性和在多个视图和项目中重复使用。

这种方法只能在同一个窗口的XAML代码中使用。 - Hunter Tran
@TuanTran,您的“only on the same window xaml code”是什么意思? - Amir Oveisi
您不能将<Window.Resources>...</Window.Resources>部分拆分到单独的资源文件中。 - Hunter Tran

-3
我认为应该像这样完成:
{Binding Path=Title, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"

2
这个能够工作并不是因为主VisualTree在ToolTip中不可用。请参考我的回答。 - HCL
抱歉,我实际上甚至没有注意到它是在工具提示中。 - mdm20

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接