2009-06-15 13 views
30

Ich schaute über dieses Web und Google und die Lösungen funktionierte nicht für mich.Verwenden des Eltern DataContext (WPF - Dynamic Menu Command Binding)

Ich habe einen Befehl auf dem ViewModel eines UserControl. Nun, die Benutzersteuerung hat ein ItemsControl, das an eine ObservableCollection gebunden ist. Innerhalb der DataTemplate der ItemsControl.ItemTemplate habe ich eine Schaltfläche und ich möchte den Befehl verwenden. Ich kann den Befehl nicht binden, da in der DataTemplate der Datenkontext nicht das ViewModel, sondern ein Objekt der ObservableCollection ist.

Die Frage ist: Wie kann ich die Schaltfläche an den Befehl binden, wenn ein Elternteil Datenkontext verloren?

Ich denke, dass dies eine einfache Lösung haben muss, weil ich denke, dass dies ein häufiges Problem ist.

dieses sceneario Stellen Sie sich vor:

Sie ein ListBox-Element mit einer ObservableCollection als Itemssource haben, so dass Sie für jedes Element in der Auflistung ein Datatemplate in der List-Box verwenden. Nun, Sie möchten das ausgewählte Element löschen und Sie fügen eine Schaltfläche in jeder Zeile für diesen Job ein. Wie machst du das?

In MVP, kann ich dies tun in dem Click-Ereignis der Schaltfläche:

Button but = e.Source as Button; 

if (but != null) 
     Presenter.ActualNote = but.DataContext as Note; 

Kurz. Sie senden den Datenkontext der Zeile (das ausgewählte Element) an den Präsentator.

Aber wie kann ich es auf die mvvm-Art tun? Weil ich einen Befehl verwenden muss, aber ich kann den Befehl der Schaltfläche nicht zuweisen, da die Schaltfläche nichts über das ViewModel weiß (wo der Befehl vorhanden ist).

Wie Sie sehen können, muss die Schaltfläche innerhalb der Datenplatte vorhanden sein, dann ist der Datenkontext nicht mehr das ViewModel .... Es gibt einen Grund, warum ich auf den DataContext der Eltern zugreifen muss, um auf den Befehl zuzugreifen.

Ich hoffe, dass Sie mein Problem besser verstehen.

Vielen Dank.

Antwort

5

Wenn Sie eine dreckige, MVVM-brechende Lösung wünschen, legen Sie das Tag = "{Binding}" auf der Schaltfläche fest und bearbeiten Sie das Click-Ereignis. Rufen Sie im Ereignishandler den Befehl in Ihrem ViewModel auf.

+0

: P. Ich bin auf der Suche nach einer guten Lösung, ohne mvvm: P zu brechen, aber danke, es ist eine Lösung: P –

+2

Ich bin ein SL-Entwickler, also breaking mvvm ist Par für den Kurs :) – geofftnz

+0

Warum SL = MVVM verwendet? – user112889

89

Verwenden Sie die Bindung unten Befehl Ihre Schaltfläche:

{Binding DataContext.CommandName, 
     RelativeSource={RelativeSource FindAncestor, 
         AncestorType={x:Type MyUserControl}}} 

Diese sagen, es wird Ihr Usercontrol zu finden und seine Datacontext verwenden.

+0

Das ist die Lösung, die ich gefunden, aber es ist nicht für mich arbeiten.Wenn ein Put: Command = "{Binding DataContext.CommandName, RelativeSource = {RelativeSource FindAncestor, AncestorType = {x: Type UserControl}}}" Es besagt, dass AncestorType für RelativeSource im FindAncestor-Modus angegeben werden sollte. Wo ist das Problem? Danke für die Antwort. –

+1

Ich habe das gerade in einer Probe versucht und es hat für mich funktioniert. Es klingt wie ein Syntaxfehler. Können Sie den XAML Ihrer Schaltfläche kopieren und einfügen? –

+0

Oh, es gibt keinen Fehler, VS markiert es, aber es kompiliert. Ich versuche es mit etwas Einfachem zu wissen. Ich habe ein Tag-Element in die Benutzersteuerung eingefügt und möchte es in der Kopfzeile eines Menüeintrags ausgeben: . Aber, Visual Studio sagen: Kann nicht finden Quelle für die Bindung mit Bezug 'RelativeSource FindAncestor, AncestorType =' System.Windows.Controls.UserControl ', AncestorLevel =' 1 ''. BindingExpression: Pfad = Tag; Datenelement = null; Zielelement ist 'MenuItem' (Name = ''); Zieleigenschaft ist 'Kopfzeile' (geben Sie 'Objekt' ein –

3

RelativeSource funktioniert, aber ich denke nicht, dass es richtig ist, wenn sich die Steuerelemente gegenseitig beeinflussen. Es ist seltsam, dass der in einer Elementansicht platzierte Button etwas mit einer äußeren Datenquelle und nicht mit dem gebundenen Element ausführt. Möglicherweise müssen Sie das Design des Programmcodes überprüfen.

+0

vorhanden ist, wird Ich mag Ihre Antwort, aber ich brauche das für diese Lösung. Ich habe ein Itemscontrol mit einem Gitter im Inneren, jeder Artikel ist eine Notiz. Nun, ich habe ein Kontextmenü im Raster, das einige Optionen für die "ausgewählte" Note enthält. Jede Option ist ein Befehl, aber der Datenkontext des Menüs ist der eigentliche Hinweis und nicht das Viewmodel. Ich kann nicht das Menü draußen, weil ich das Menü für jeden Artikel brauche. –

+0

Hm, ja, denken Sie nicht, dass es eine ideale Lösung ist, aber Sie können Ihr ModelView-Objekt als statische Resorce in ein Window.Resources-Element platzieren, eventuell mit ObjectDataProvider, und dann durch eine statische Erweiterung innerhalb einer beliebigen Datenbindung von einem beliebigen Teil aus darauf verweisen des Fensters. –

+0

Ich stimme zu - es ist immer wie ein Hack gefühlt, wenn ich diese Lösung verwendet habe - aber ein Hack, der funktioniert. ;) –

3

Ok, was ist dann mit der Modifizierung Ihrer Datenelementklasse, sodass eine Eigenschaft auf die gesamte Modellansicht verweist?

Wenn Ihr Itemssource vom Typ ObservableCollection<DataItem> dann DataItem Typen wie folgt ändern:

public class DataItem 
{ 
    public BusinessObject Value { get; set; } 

    private ModelView modelView; 

    public ModelView ModelView 
    { 
     get 
     { 
      return modelView; 
     } 
    } 

    public DataItem(ModelView modelView) 
    { 
     this.modelView = modelView; 
    } 
} 
+0

Kannst du das nochmal erklären? Mit meinem Englisch kann ich nicht verstehen, was Sie mir erklären wollen. Es tut uns leid. –

+0

Eine ausführlichere Erklärung hinzugefügt. –

+0

Es ist ein kleiner Hack, aber es sollte funktionieren. –