来自 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
将保留您的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>
发生了什么?ProxyClass
的Data
属性将绑定到Window
的DataContext
,然后它具有您的所有命令和ViewModel
的属性在ProxyClass
资源中。
这种方法的另一个好处是可移植性和在多个视图和项目中重用。
【讨论】:
这种方式只能在同一个窗口的xaml代码中使用 @TuanTran “仅在同一个窗口 xaml 代码上”到底是什么意思? 您不能将<Window.Resources>...</Window.Resources>
部分拆分为单独的资源文件【参考方案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 选择