我有一个带有命令的ViewModel' OpenCommand',一个标志' IsConextMenuVisible'和一个可观察的清单'链接'。
public ObservableList<string> Links { get; set; }
public bool IsContextMenuVisible { get; set; }
public ICommand OpenCommand { get; set; }
在XAML中,我想要以下工作。
<ListBox ItemsSource="{Binding Links}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}">
<TextBlock.ContextMenu>
<ContextMenu Visibility="{Binding IsContextMenuVisible, Converter={StaticResource BoolToVisibiltyHiddenConverter}}">
<MenuItem Header="Open" Command="{Binding OpenCommand}" CommandParameter="{Binding}"/>
</ContextMenu>
</Textblock.ContextMenu>
</TextBlock>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
我已经为ContextMenu上的内部绑定尝试了一些绑定表达式,但似乎没有任何效果。类似的东西:
Visibility="{Binding Path=DataContext.IsContextMenuVisible,
Converter={StaticResource BoolToVisibilityCollapsedConverter},
RelativeSource={RelativeSource AncestorType=ListBox}}"
答案 0 :(得分:1)
这是有问题的&#34;正如孩子们所说,因为上下文菜单不在视觉树中,所以没有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创建为一个资源,在一个可以&#34;参见&#34;所需元素:
<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>