来自 ToolTip 或 ContextMenu 的 RelativeSource 绑定

Posted

技术标签:

【中文标题】来自 ToolTip 或 ContextMenu 的 RelativeSource 绑定【英文标题】:RelativeSource binding from a ToolTip or ContextMenu 【发布时间】:2011-04-09 18:42:23 【问题描述】:

我在这里做错了什么?:

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

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

请帮帮我。

【问题讨论】:

【参考方案1】:

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

更新 可悲的是,链接消失了,我再也找不到引用的文章了。 据我记得,引用的博客已经展示了如何绑定到另一个 VisualTree 的 DataContext,这在从 ToolTip、ContextMenu 或 Popup 绑定时通常是必需的。

一个很好的方法是在 PlacementTarget 的 Tag-property 中提供所需的实例(例如 ViewModel)。以下示例执行此操作以访问 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>

我还没有测试过它,我上次这样做了很长时间。如果对您不起作用,请发表评论。

更新 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 属性。然后,您可以传递此上下文 向下浏览您的菜单项。

我承认这不是世界上最优雅的解决方案。 但是,它胜过在后面的代码中设置东西。如果有人有 更好的方法,我很想听听。

【讨论】:

@Bill 我没有找到链接,但我已尝试解释如何解决问题。 比以前让我无法使用上下文菜单和工具提示的“namescope hack”解决方案非常有用且简单得多。这非常简单,而且效果很好。 @ChrisValentine 随意打乱我的编辑;我只是想确保这里记录了原始解决方案。【参考方案2】:

我认为应该这样做:

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

【讨论】:

这不是因为工具提示中没有主 VisualTree。看我的回答。 是的,抱歉,实际上甚至没有注意到它在工具提示中。【参考方案3】:

因为ContextMenu 不在可视树中,绑定将不起作用。 一个简单的解决方案是使用代理模式,您可以创建一个从DependencyObject 继承的包装类,并有一个DependencyProperty 将保留您的DataContextWindow,然后您可以在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代码中使用 @TuanTran “仅在同一个窗口 xaml 代码上”到底是什么意思? 您不能将&lt;Window.Resources&gt;...&lt;/Window.Resources&gt; 部分拆分为单独的资源文件【参考方案4】:

如下: PlacementTarget 是拥有 ContextMenu 的控件(例如:DataGrid)。不需要“标签”属性。

IsEnabled 绑定到 DataGrid 的“myProperty”值。

我对此进行了测试,它可以工作。绑定有类似的问题。

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

【讨论】:

以上是关于来自 ToolTip 或 ContextMenu 的 RelativeSource 绑定的主要内容,如果未能解决你的问题,请参考以下文章

ListView中的ContextMenu,长按出来后如何知道来自哪一行?

长按后 EditText 不显示默认的 ContextMenu

WPF:如何设置或禁用 TextBox 的默认 ContextMenu

将 ContextMenu 的 MenuItem 可见性绑定到 ListView 选择

HighCharts 框架我想在Tooltip默认显示的基础上添加一个来自后台的一个说明字段

获取或设置当前窗口contextmenu事件的事件处理函数