Frage

Hat jemand anderes bemerkt, dass Bindungen mit Element lösen nicht korrekt für MenuItem Objekte, die innerhalb ContextMenu Objekte enthalten sind? Schauen Sie sich dieses Beispiel:

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

Alle diese Bindungen lassen sich gut mit Ausnahme der Bindungen innerhalb der ContextMenu enthalten. Sie drucken einen Fehler an das Ausgabefenster während der Laufzeit.

wissen Jeder von irgendwelchen Arbeitsarounds? Was ist denn hier los?

War es hilfreich?

Lösung

fand ich eine viel einfachere Lösung.

In der Code-behind für die Usercontrol:

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

Andere Tipps

Wie bereits von anderen gesagt, die ‚ContextMenu‘ ist nicht in der visuellen Struktur enthalten sind und ein ‚Elementname‘ Bindung wird nicht funktionieren. Einstellen des ‚Namescope‘ über das Kontextmenüs, wie durch die akzeptierte Antwort vorgeschlagen funktioniert nur, wenn das Kontextmenü nicht in einer ‚Datatemplate‘ definiert ist. Ich habe dieses Problem gelöst, indem Sie die {x: Reference} Markup-Erweiterung das ist ähnlich den ‚Elementname‘ binden, jedoch löst die Bindung anders, die visuelle Struktur zu umgehen. Ich halte dies für weit mehr lesbar sein als die Verwendung von ‚Placement‘. Hier ein Beispiel:

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

Nach der MSDN-Dokumentation

  

x: Reference ein Konstrukt in XAML 2009. In WPF definiert ist, können Sie   XAML 2009 Funktionen, aber nur für XAML, die nicht WPF-Markup-kompiliert ist.   Markup-kompilierten XAML und die BAML Form von XAML derzeit nicht   unterstützen die XAML 2009 Sprache Schlüsselwörter und Funktionen.

was immer das heißt ... Arbeiten für mich, though.

Hier ist eine andere XAML-einzige Abhilfe. (Dies ist auch vorausgesetzt, dass Sie wollen, was in der Datacontext , zum Beispiel, du bist MVVMing it)

Option eins, wenn das Mutterelement der ContextMenu ist nicht in a Datatemplate :

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

Dies würde für OP Frage arbeiten. Das wird nicht funktionieren, wenn Sie innerhalb eines sind Datatemplate . In diesen Fällen ist die Datacontext ist oft einer von vielen in einer Sammlung, und die ICommand Sie binden möchten, ist ein Geschwister Eigenschaft der Auflistung innerhalb des gleichen Ansichtsmodell (das Datacontext der Fenster, sagen).

In diesen Fällen können Sie die Vorteile der Tag , um vorübergehend das übergeordnete halten Datacontext , die sowohl die Sammlung und Ihre ICommand enthält:

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

und in der 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>

Kontextmenüs sind schwierig zu binden, gegen. Sie existieren außerhalb der visuellen Struktur Ihrer Kontrolle, daher können sie nicht Ihre Elementnamen finden.

Versuchen Sie, die Datacontext des Kontextmenüs auf sein Platzierungsziel zu setzen. Sie haben Relative zu verwenden.

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

Nach ein wenig experimentieren, entdeckte ich eine Arbeit um:

Machen Top-Level-Window / UserControl INameScope implementieren und NameScope von ContextMenu auf die oberste Ebene Regelung eingestellt.

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
}

Auf diese Weise kann das Kontextmenü benannten Elemente innerhalb des Window zu finden. Alle anderen Optionen?

Ich bin mir nicht sicher, warum Resort Zaubertricks nur eine Zeile Code in der Eventhandler für die Maus zu vermeiden, klicken Sie bereits umgehen:

    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;
    }
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top