正如孩子们所说,这是“有问题的”,因为上下文菜单不在可视化树中,所以 RelativeSource 的任何风格都不起作用。
您通常可以绑定到PlacementTarget 的属性,但在这种情况下,您需要PlacementTarget 的视觉祖先,而RelativeSource 不会做其他东西的祖先。
在 WPF 中,当可视化树中存在间隙时,最后一个选项始终是 BindingProxy。这是该类的外观(包括我从中窃取的 StackOverflow 问题的 URL——该类已被复制并粘贴在该站点上的许多问题和答案中):
// https://stackoverflow.com/questions/24452264/bindingproxy-binding-to-the-indexed-property
public class BindingProxy : Freezable
{
#region Overrides of Freezable
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
#endregion
public object Data
{
get { return (object)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
// Using a DependencyProperty as the backing store for Data. This enables animation, styling, binding, etc...
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}
你会像这样使用它。首先将 BindingProxy 创建为资源,在它可以“看到”所需元素的位置:
<Window.Resources>
<local:BoolToVisibiltyHiddenConverter x:Key="BoolToVisibiltyHiddenConverter" />
<!-- {Binding} with no path will be the window's datacontext, the main viewmodel. -->
<local:BindingProxy Data="{Binding}" x:Key="MainViewModelBindingProxy" />
</Window.Resources>
然后将其用于绑定的Source。所需的 DataContext 将是代理对象的 Data 属性,因此请提供相对于 Data 的路径:
<TextBlock.ContextMenu>
<ContextMenu
Visibility="{Binding Data.IsContextMenuVisible,
Converter={StaticResource BoolToVisibiltyHiddenConverter},
Source={StaticResource MainViewModelBindingProxy}}"
>
<MenuItem
Header="Open"
Command="{Binding OpenCommand}"
CommandParameter="{Binding}"
/>
</ContextMenu>
</TextBlock.ContextMenu>
现在你遇到了另一个问题:菜单仍在弹出。它只是碰巧不可见。如果用户右键单击,它会隐形弹出,并在IsContextMenuVisible变为true时突然出现。那不是你想要的。
您可以省略转换器,直接绑定到ContextMenu.IsEnabled:它仍然会弹出,但会变灰。这与常见的 Windows UI 实践一致。
您还可以有一个样式触发器,以便 TextBlock 只有在您希望它有一个 ContextMenu 时才具有它。因为该触发器在 TextBlock 上,所以它在可视树中,我们可以使用常规的 RelativeSource 进行绑定。
<TextBlock Text="{Binding}">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Style.Triggers>
<DataTrigger
Binding="{Binding Data.IsContextMenuVisible,
RelativeSource={RelativeSource AncestorType=ListBox}}"
Value="True">
<Setter Property="ContextMenu">
<Setter.Value>
<ContextMenu >
<MenuItem
Header="Open"
Command="{Binding OpenCommand}"
CommandParameter="{Binding}"
/>
</ContextMenu>
</Setter.Value>
</Setter>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>