UserControls und Viewmodels in View-First-MVVM

8

Ich bin gezwungen, View First MVVM in einer WPF-Anwendung zu verwenden, und ich kämpfe darum, wie man es elegant gestalten kann.

Die Wurzel des Problems liegt in verschachteltem UserControls . In einer MVVM-Architektur muss für jedes UserControl das View-Modell seinem DataContext zugewiesen werden. Dies hält die Bindungsausdrücke einfach. Darüber hinaus wird WPF jede über ein DataTemplate generierte Ansicht instanziieren.

Aber wenn ein Kind UserControl Abhängigkeitseigenschaften hat, die der Elternteil an sein eigenes Ansichtsmodell binden muss, dann bedeutet die Tatsache, dass das Kind UserControl sein DataContext auf sein eigenes Ansichtsmodell gesetzt hat, dass es sich um eine 'implizite Pfadbindung' handelt in der übergeordneten XAML-Datei wird in das Viewmodel des untergeordneten Elements anstelle der übergeordneten aufgelöst.

Um dieses Problem zu umgehen, muss jeder Elternteil jedes UserControl in der Anwendung entweder explizit benannte Bindings für alles standardmäßig verwenden (was ausführlich, hässlich und fehlerträchtig ist), oder er muss wissen, ob ein bestimmtes Steuerelement vorhanden ist Sein DataContext wird auf ein eigenes Viewmodel gesetzt oder nicht und verwendet die entsprechende Bindesyntax (was ebenfalls fehlerbehaftet ist und eine wesentliche Verletzung der Grundkapselung darstellt).

Nach Tagen der Forschung habe ich keine einzige halbwegs vernünftige Lösung für dieses Problem gefunden. Der Lösung, die mir am nächsten kommt, ist, das UserControl's viewmodel auf ein inneres Element von UserControl (das oberste Grid oder was auch immer) zu setzen, was immer noch ein Problem beim Binden von Eigenschaften des UserControl selbst zu einem eigenen Viewmodel! ( ElementName binding funktioniert in diesem Fall nicht, da die Bindung vor dem benannten Element deklariert würde, wobei das Viewmodel seinem DataContext zugewiesen ist.)

Ich vermute, dass der Grund, warum nicht viele andere Leute darauf stoßen, dass sie entweder viewmodel zuerst MVVM verwenden, das dieses Problem nicht hat, oder sie verwenden zuerst MVVM in Verbindung mit einer Dependency-Injektion-Implementierung, die dieses Problem verbessert .

Hat jemand dafür bitte eine saubere Lösung?

UPDATE:

Beispielcode wie angefordert.

%Vor% %Vor% %Vor% %Vor% %Vor%

Dies führt zu:

  

System.Windows.Data Error: 40: BindingExpression-Pfadfehler:   'MainWindowVmString1' -Eigenschaft nicht auf 'Objekt' '' UserControl6Vm 'gefunden   (HashCode = 44204140) '. BindingExpression: Pfad = MainWindowVmString1;   DataItem = 'UserControl6Vm' (HashCode = 44204140); Zielelement ist   'UserControl6' (Name = '_ this'); Zieleigenschaft ist 'Text' (Typ   'String')

weil in MainWindow.xaml die Deklaration <local:UserControl6 Text="{Binding MainWindowVmString1}"/> versucht, MainWindowVmString1 in UserControl6Vm aufzulösen.

In UserControl6.xaml kommentiert die Deklaration von DataContext und der erste TextBlock den Code, aber der UserControl benötigt ein DataContext . In MainWIndow1 mit einer ElementName anstelle einer imvictt Pfadbindung funktioniert auch, aber um die ElementName Bindesyntax zu verwenden, müsstest du entweder wissen, dass UserControl sein Viewmodel seinem DataContext ( Einkapselungsfehler) oder alternativ eine Richtlinie zur Verwendung von ElementName bindings überall . Keine von beiden ist ansprechend.

    
Neutrino 21.01.2013, 15:56
quelle

2 Antworten

0

Eine sofortige Lösung besteht darin, ein RelativeSource zu verwenden und nach dem DataContext eines Elternteils UserControl :

zu suchen %Vor%

Sie können die untergeordneten Ansichtsmodelle auch als Eigenschaften des übergeordneten Ansichtsmodells behandeln und sie vom übergeordneten Objekt weiterleiten. Auf diese Weise erkennt das übergeordnete Ansichtsmodell die untergeordneten Elemente, sodass es ihre Eigenschaften aktualisieren kann. Die untergeordneten Ansichtsmodelle können auch eine "Parent" -Eigenschaft aufweisen, die einen Verweis auf das übergeordnete Element enthält, das vom übergeordneten Element selbst bei ihrer Erstellung eingegeben wird, was direkten Zugriff auf das übergeordnete Element gewähren kann.

%Vor%

BEARBEITEN Ein anderer Ansatz und komplexer wäre es, die DataContext des Elternteils für das Kind UserControl unter Verwendung einer angehängten Eigenschaft verfügbar zu machen. Ich habe es noch nicht vollständig implementiert, aber es würde in einer angehängten Eigenschaft bestehen, um das Feature anzufordern (etwas wie "HasAccessToParentDT" ), in dem DependencyPropertyChanged event die Load- und Unload -Ereignisse von ChildUserControl miteinander verbindet. Greifen Sie auf die Eigenschaft Parent zu (verfügbar, wenn das Steuerelement geladen ist) und binden Sie ihr DataContext an eine zweite angehängte Eigenschaft, "ParentDataContext" , die dann in xaml verwendet werden kann.

%Vor%     
Arthur Nunes 21.01.2013 17:01
quelle
0

Die offensichtlichste Lösung ist die Verwendung der RelativeSource. Die Bindung selbst sieht nicht sehr hübsch aus, aber es ist tatsächlich sehr häufig zu sehen. Ich würde es nicht vermeiden - das ist genau das Szenario, warum es da ist.

Ein anderer Ansatz, den Sie verwenden können, ist ein Verweis auf ein Eltern-View-Modell, wenn es logisch ist, es zu haben. So wie ich eine FlightPlan-Ansicht habe, die eine Liste von Navigationspunkten und deren graphischer "Karte" nebeneinander zeigt. Die Liste der Punkte ist eine separate Ansicht mit separatem Ansichtsmodell:

%Vor%

Dann kann die Ansicht wie folgt an diese Eigenschaft gebunden werden:

%Vor%

Allerdings ist das Erstellen von Viewmodels oft fraglich.

    
eMko 01.11.2015 15:18
quelle

Tags und Links