元素名称从ContextMenu中的MenuItem绑定

有没有人注意到ElementName绑定不能正确parsingContextMenu对象中包含的MenuItem对象? 看看这个例子:

 <Window x:Class="EmptyWPF.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1" Height="300" Width="300" x:Name="window"> <Grid x:Name="grid" Background="Wheat"> <Grid.ContextMenu> <ContextMenu x:Name="menu"> <MenuItem x:Name="menuItem" Header="Window" Tag="{Binding ElementName=window}" Click="MenuItem_Click"/> <MenuItem Header="Grid" Tag="{Binding ElementName=grid}" Click="MenuItem_Click"/> <MenuItem Header="Menu" Tag="{Binding ElementName=menu}" Click="MenuItem_Click"/> <MenuItem Header="Menu Item" Tag="{Binding ElementName=menuItem}" Click="MenuItem_Click"/> </ContextMenu> </Grid.ContextMenu> <Button Content="Menu" HorizontalAlignment="Center" VerticalAlignment="Center" Click="MenuItem_Click" Tag="{Binding ElementName=menu}"/> <Menu HorizontalAlignment="Center" VerticalAlignment="Bottom"> <MenuItem x:Name="anotherMenuItem" Header="Window" Tag="{Binding ElementName=window}" Click="MenuItem_Click"/> <MenuItem Header="Grid" Tag="{Binding ElementName=grid}" Click="MenuItem_Click"/> <MenuItem Header="Menu" Tag="{Binding ElementName=menu}" Click="MenuItem_Click"/> <MenuItem Header="Menu Item" Tag="{Binding ElementName=anotherMenuItem}" Click="MenuItem_Click"/> </Menu> </Grid> </Window> 

除了ContextMenu中包含的绑定之外,所有的绑定工作都很好。 他们在运行期间向输出窗口输出一个错误。

任何人都知道任何工作? 这里发生了什么?

我find了一个更简单的解决scheme。

在用户控件的后面的代码中:

 NameScope.SetNameScope(contextMenu, NameScope.GetNameScope(this)); 

这是另一个仅用于xaml的解决方法。 (这也假设你想要什么在DataContext内 ,例如,你MVVMing它)

选项一, ContextMenu的父元素不在DataTemplate中

 Command="{Binding PlacementTarget.DataContext.MyCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}" 

这将适用于OP的问题。 如果你在一个DataTemplate中,这将不起作用。 在这些情况下, DataContext通常是集合中的许多元素之一,并且您希望绑定到的ICommand是同一个ViewModel(窗口的DataContext)中集合的同级属性。

在这些情况下,您可以利用Tag来暂时保存包含集合AND your ICommand的父DataContext

 class ViewModel { public ObservableCollection<Derp> Derps { get;set;} public ICommand DeleteDerp {get; set;} } 

和在xaml

 <!-- ItemsSource binds to Derps in the DataContext --> <StackPanel Tag="{Binding DataContext, ElementName=root}"> <StackPanel.ContextMenu> <ContextMenu> <MenuItem Header="Derp" Command="{Binding PlacementTarget.Tag.DeleteDerp, RelativeSource={RelativeSource AncestorType=ContextMenu}}" CommandParameter="{Binding PlacementTarget.DataContext, RelativeSource={RelativeSource AncestorType=ContextMenu}}"> </MenuItem> 

正如其他人所说,“视觉菜单”不包含在视觉树中,“元素名称”绑定将不起作用。 如果接受的答案build议设置上下文菜单的“NameScope”,则只有在“DataTemplate”中没有定义上下文菜单时才起作用。 我已经通过使用类似于'ElementName'绑定的{x:Reference}标记扩展来解决这个问题,但绕过了可视化树,以不同的方式解决了绑定问题。 我认为这比使用“PlacementTarget”更具可读性。 这里是一个例子:

 <Image Source="{Binding Image}"> <Image.ContextMenu> <ContextMenu> <MenuItem Header="Delete" Command="{Binding Source={x:Reference Name=Root}, Path=DataContext.RemoveImage}" CommandParameter="{Binding}" /> </ContextMenu> </Image.ContextMenu> </Image> 

根据MSDN文档

x:引用是在XAML 2009中定义的结构。在WPF中,可以使用XAML 2009function,但仅适用于不是WPF标记编译的XAML。 标记编译的XAML和XAML的BAMLforms目前不支持XAML 2009语言关键字和function。

不pipe那意味着什么……虽然对我有用。

上下文菜单很难绑定。 它们存在于您的控件的可视化树之外,因此无法find您的元素名称。

尝试将上下文菜单的datacontext设置为其放置目标。 你必须使用RelativeSource。

 <ContextMenu DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}"> ... 

经过一番尝试,我发现了一个工作:

使顶层Window / UserControl实现INameScope ,并将ContextMenu NameScope设置为顶层控件。

 public class Window1 : Window, INameScope { public Window1() { InitializeComponent(); NameScope.SetNameScope(contextMenu, this); } // Event handlers and etc... // Implement INameScope similar to this: #region INameScope Members Dictionary<string, object> items = new Dictionary<string, object>(); object INameScope.FindName(string name) { return items[name]; } void INameScope.RegisterName(string name, object scopedElement) { items.Add(name, scopedElement); } void INameScope.UnregisterName(string name) { items.Remove(name); } #endregion } 

这允许上下文菜单findWindow内的命名项目。 任何其他选项?

我不知道为什么诉诸魔术技巧,只是为了避免事件处理程序中的一行代码,你已经处理的鼠标点击:

  private void MenuItem_Click(object sender, System.Windows.RoutedEventArgs e) { // this would be your tag - whatever control can be put as string intot he tag UIElement elm = Window.GetWindow(sender as MenuItem).FindName("whatever control") as UIElement; }