从的ElementName绑定菜单项中的ContextMenu从的ElementName绑定菜单项中

2019-05-13 12:17发布

有其他人注意到,与绑定的ElementName没有为正确解析MenuItem所包含内的物体ContextMenu对象? 看看这个例子:

<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>

除了包含的文本菜单中的绑定所有绑定的工作的伟大。 它们在运行时错误打印到输出窗口。

任何变通的任何一个知道? 这里发生了什么?

Answer 1:

我发现了一个更简单的解决方案。

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

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


Answer 2:

正如其他人所说的“文本菜单”不包含在视觉树和一个“的ElementName”结合将无法正常工作。 设置上下文菜单中的“名称范围”被接受的答案的建议只有在上下文菜单中是不是在“DataTemplate中”定义工作。 我已经通过使用解决了这个标记-扩展:{基准X} ,其类似于“的ElementName”结合,但不同地解决了结合,绕过可视树。 我认为这是远远高于使用“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:Reference是在2009年XAML定义在WPF的构建,你可以使用XAML 2009年的功能,但仅限于XAML不是WPF标记编译。 标记编译XAML和XAML的BAML形式当前不支持XAML 2009年语言的关键字和功能。

这意味着什么?对我的作品,虽然。



Answer 3:

这里还有一个只有XAML的解决方法。 (这还假定你想要的是DataContext的内部,例如,你MVVMing它)

选项之一,其中所述文本菜单的父元素不是一个DataTemplate:

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

这将工作OP的问题。 如果你是一个DataTemplate内这将无法正常工作。 在这些情况下,DataContext的往往是很多的一个集合中,和你的ICommand希望绑定到被收集的同一视图模型内的同级属性(在窗口的DataContext的 ,说的)。

在这些情况下,您可以采取的优势, 标签可暂时保持其既包含了收集和你的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>


Answer 4:

上下文菜单是棘手的绑定反对。 他们存在的控件的可视化树之外,因此他们无法找到你的元素名称。

试试你的上下文菜单的设置的DataContext把它放在目标。 你必须使用的RelativeSource。

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


Answer 5:

试验了一下后,我发现了一个解决办法:

使顶层Window / UserControl执行INameScope并设置NameScopeContextMenu的最高级控制。

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
}

这使上下文菜单中找到的内部命名项目Window 。 任何其他的选择吗?



Answer 6:

我不知道为什么诉诸魔术只是为了避免代码的事件处理程序为鼠标内部的一个行单击您已经办理:

    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;
    }


文章来源: ElementName Binding from MenuItem in ContextMenu