是否还有其他人注意到带有ElementName的Bindings无法正确解析 ContextMenu 对象中包含的 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中包含的绑定外,所有绑定都很有效。它们在运行期间向输出窗口输出错误。

任何人都知道有任何工作吗?这是怎么回事?

有帮助吗?

解决方案

我找到了一个更简单的解决方案。

在UserControl背后的代码中:

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

其他提示

正如其他人所说,'ContextMenu'不包含在可视树中,'ElementName'绑定不起作用。如果在“DataTemplate”中未定义上下文菜单,则仅按接受的答案建议设置上下文菜单的“NameScope”。我已使用 {x:Reference}标记扩展解决了这个问题这类似于'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是XAML 2009中定义的构造。在WPF中,您可以使用   XAML 2009的功能,但仅适用于非WPF标记编译的XAML。   目前没有标记编译的XAML和BAML形式的XAML   支持XAML 2009语言关键字和功能。

无论那意味着什么......不过对我有用。

这是另一个仅限xaml的解决方法。 (这也假设您想要 DataContext 中的内容,例如,您 MVVMing 它)

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

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

这适用于OP的问题。如果您位于 DataTemplate 内,则无法使用此功能。在这些情况下, DataContext 通常是集合中的众多元素之一,而您希望绑定的 ICommand 是同一ViewModel中集合的兄弟属性(窗口的 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>

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

尝试将上下文菜单的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
}

这允许上下文菜单在 Window 中查找命名项。还有其他选择吗?

我不确定为什么要使用魔术技巧来避免在事件处理程序中使用一行代码进行鼠标点击:

    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;
    }
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top