68

MenuItemオブジェクト内に含まれるオブジェクトに対して、ElementName を持つバインディングが正しく解決されないことに気付いた人はい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 内に含まれるバインディングを除いて、すべてのバインディングが適切に機能します。実行時に出力ウィンドウにエラーを出力します。

回避策を知っている人はいますか?何が起きてる?

4

6 に答える 6

58

もっと簡単な解決策を見つけました。

UserControl のコード ビハインド:

NameScope.SetNameScope(contextMenu, NameScope.GetNameScope(this));
于 2009-06-30T20:54:54.293 に答える
31

他の人が言ったように、「ContextMenu」はビジュアルツリーに含まれておらず、「ElementName」バインディングは機能しません。受け入れられた回答で提案されているようにコンテキストメニューの「NameScope」を設定することは、コンテキストメニューが「DataTemplate」で定義されていない場合にのみ機能します。{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 is a construct defined in XAML 2009. WPF では、XAML 2009 の機能を使用できますが、WPF マークアップ コンパイルされていない XAML に対してのみ使用できます。マークアップ コンパイルされた XAML および XAML の BAML 形式は、現在、XAML 2009 言語のキーワードと機能をサポートしていません。

それが何を意味するにせよ...私にとってはうまくいきます。

于 2016-08-16T09:47:46.963 に答える
20

別の xaml のみの回避策を次に示します。(これは、 DataContextの内部にあるものを必要としていることも前提としています。たとえば、MVVMを実行しています)

ContextMenuの親要素がDataTemplateにないオプション 1 :

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

これは、OPの質問に役立ちます。DataTemplate内にいる場合、これは機能しません。このような場合、多くの場合、DataContextはコレクション内の多くの 1 つです。バインド先のICommandは、同じ ViewModel 内のコレクションの兄弟プロパティ (ウィンドウのDataContextなど) です。

このような場合、Tagを利用して、コレクションと 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>
于 2011-03-18T19:51:20.353 に答える
6

コンテキスト メニューは、バインドするのが難しいです。それらはコントロールのビジュアル ツリーの外に存在するため、要素名を見つけることができません。

コンテキスト メニューの datacontext を配置ターゲットに設定してみてください。RelativeSource を使用する必要があります。

<ContextMenu 
   DataContext="{Binding PlacementTarget, RelativeSource={RelativeSource Self}}"> ...
于 2009-06-18T18:15:49.150 に答える
4

少し実験した後、1 つの回避策を発見しました。

トップ レベルを作成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
}

これにより、コンテキスト メニューで .xml 内の名前付きアイテムを検索できますWindow。他のオプションはありますか?

于 2009-06-18T16:24:06.660 に答える
1

既に処理しているマウス クリックのイベント ハンドラー内の 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;
    }
于 2011-06-18T12:36:56.643 に答える