从ContextMenu中的MenuItem绑定ElementName

时间:2009-06-18 16:00:37

标签: wpf data-binding contextmenu elementname

是否有人注意到带有ElementName的Bindings无法正确解析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>

除了ContextMenu中包含的绑定外,所有绑定都很有效。它们在运行期间向输出窗口输出错误。

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

5 个答案:

答案 0 :(得分:52)

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

在UserControl背后的代码中:

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

答案 1 :(得分:19)

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

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

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

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

在这些情况下,您可以利用Tag暂时保留父 DataContext ,其中包含集合和您的ICommand:

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>

答案 2 :(得分:5)

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

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

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

答案 3 :(得分:4)

经过一番实验,我发现了一个解决方法:

使顶级Window / UserControl实施INameScope并将NameScope ContextMenu设置为顶级控件。

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内找到命名项。还有其他选择吗?

答案 4 :(得分:1)

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

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