上下文菜单有问题,不显示项目
Trouble With ContextMenu, not displaying items
目标:
右键单击 ListBox 并获得绑定的上下文菜单。
public class MyViewModel
{
public List<string> ContextMenuItems{ get; set; }
public ItemObject MyObject{ get; set; }
}
public class ItemObject{
public List<OtherObjects> SomeCollection{ get; set; }
}
现在我的 ListBox 绑定到 "SomeCollection",但是我的 ContextMenu 应该访问列表框绑定之外的绑定。我试过了,但根本无法正常工作,我的上下文菜单总是空的。知道为什么吗?这是在 UserControl 中,而不是 Window,并不相关。我只是指出为什么我的 AncestorType 指向 UserControl
<ListBox ItemsSource="{Binding SomeCollection}">
<ListBox.ContextMenu >
<ContextMenu DataContext="{Binding DataContext, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" ItemsSource="{Binding ContextMenuItems}">
<ContextMenu.ItemTemplate>
<DataTemplate>
<MenuItem Header="{Binding}" Command="{Binding MyCommand}"/>
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
众所周知,在 WPF 中绑定上下文菜单非常棘手。这样做的原因是它们存在于其余组件所做的可视化树之外。 (如果你仔细想想,这是有道理的,因为它们有自己的 pop-up window)。
因为它们位于不同的可视化树中,所以有用的东西(例如能够按名称绑定)会导致绑定错误,正如您所发现的那样。 "Cannot find source" 表示无法在可视化树中找到命名元素。
绕过它的一种方法是使用上下文菜单本身的 "PlacementTarget" 属性。这是指父控件,您可以在其中访问数据上下文,如下所示:
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}" ItemsSource="{Binding ContextMenuItems}">
我还发现如果我正在做任何非常复杂的事情并且需要在大上下文菜单中引用多个内容,我喜欢将我想要访问的内容有效地存储在代理静态资源中。
创建自定义代理 class:
public class BindingProxy : Freezable
{
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
public object Data
{
get { return (object)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}
在您的资源中,每当您难以访问正确的 DataContext 时创建一个代理对象:
<UserControl.Resources>
<local:BindingProxy x:Key="Proxy" Data="{Binding}" />
</UserControl.Resources>
然后在您的 xaml 中的任何地方您都可以很容易地访问它:
<ContextMenu ItemsSource="{Binding Data.ContextMenuItems, Source={StaticResource Proxy}}">
目标: 右键单击 ListBox 并获得绑定的上下文菜单。
public class MyViewModel
{
public List<string> ContextMenuItems{ get; set; }
public ItemObject MyObject{ get; set; }
}
public class ItemObject{
public List<OtherObjects> SomeCollection{ get; set; }
}
现在我的 ListBox 绑定到 "SomeCollection",但是我的 ContextMenu 应该访问列表框绑定之外的绑定。我试过了,但根本无法正常工作,我的上下文菜单总是空的。知道为什么吗?这是在 UserControl 中,而不是 Window,并不相关。我只是指出为什么我的 AncestorType 指向 UserControl
<ListBox ItemsSource="{Binding SomeCollection}">
<ListBox.ContextMenu >
<ContextMenu DataContext="{Binding DataContext, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" ItemsSource="{Binding ContextMenuItems}">
<ContextMenu.ItemTemplate>
<DataTemplate>
<MenuItem Header="{Binding}" Command="{Binding MyCommand}"/>
</DataTemplate>
</ContextMenu.ItemTemplate>
</ContextMenu>
</ListBox.ContextMenu>
</ListBox>
众所周知,在 WPF 中绑定上下文菜单非常棘手。这样做的原因是它们存在于其余组件所做的可视化树之外。 (如果你仔细想想,这是有道理的,因为它们有自己的 pop-up window)。
因为它们位于不同的可视化树中,所以有用的东西(例如能够按名称绑定)会导致绑定错误,正如您所发现的那样。 "Cannot find source" 表示无法在可视化树中找到命名元素。
绕过它的一种方法是使用上下文菜单本身的 "PlacementTarget" 属性。这是指父控件,您可以在其中访问数据上下文,如下所示:
<ContextMenu DataContext="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource Self}}" ItemsSource="{Binding ContextMenuItems}">
我还发现如果我正在做任何非常复杂的事情并且需要在大上下文菜单中引用多个内容,我喜欢将我想要访问的内容有效地存储在代理静态资源中。
创建自定义代理 class:
public class BindingProxy : Freezable
{
protected override Freezable CreateInstanceCore()
{
return new BindingProxy();
}
public object Data
{
get { return (object)GetValue(DataProperty); }
set { SetValue(DataProperty, value); }
}
public static readonly DependencyProperty DataProperty =
DependencyProperty.Register("Data", typeof(object), typeof(BindingProxy), new UIPropertyMetadata(null));
}
在您的资源中,每当您难以访问正确的 DataContext 时创建一个代理对象:
<UserControl.Resources>
<local:BindingProxy x:Key="Proxy" Data="{Binding}" />
</UserControl.Resources>
然后在您的 xaml 中的任何地方您都可以很容易地访问它:
<ContextMenu ItemsSource="{Binding Data.ContextMenuItems, Source={StaticResource Proxy}}">