Laden...

Window + Usercontrol + MVVM = ???

Erstellt von GinoBambino vor 11 Jahren Letzter Beitrag vor 11 Jahren 5.941 Views
G
GinoBambino Themenstarter:in
30 Beiträge seit 2012
vor 11 Jahren
Window + Usercontrol + MVVM = ???

Hi.

Folgendes Szenario:

Ich habe folgende Klassen:

  • 1 MainWindow + 1 MainWindowViewModel
  • 1 UserControl + 1 UserControlViewModel

Das MainWindow ist in der Lage, spezifische Dateien zu erzeugen (beispielsweise eine Textdatei). Dabei ist der Dateiname bzw. -pfad eine Eigenschaft, die im UserControlViewModel definiert ist und die per Data Binding an das UserControl angeknüpft ist.

MainWindow "kennt" zwar das UserControl, aber dennoch möchte ich unbedingt vermeiden, die Eigenschaft direkt auszulesen (MainWindow.UserControl.Filename).

Wie löse ich das Problem nach MVVM-Kriterien? Wäre evtl. ein Command (RelayCommand) möglich, das an ein Control von UserControl gebunden ist und beim Aufruf eine Prozedur in MainWindow startet?

3.430 Beiträge seit 2007
vor 11 Jahren

Hallo GinoBambino,

also normalerweise ist das so dass die ViewModels die Views gar nicht kennen.
D.h. du lädst einfach dynamisch zum VM den passenden View.

In deinem Fall wäre es doch so dass das UserControlVM ein Element in deinem MainVM ist, oder?
Denn dann hast du ja eh eine Instanz vom UserControlVM und kannst dann im MainVM darauf zugreifen.

Falls diese sich gar nicht kennen bietet sich evtl. das Mediator Pattern un

Grüße
Michael

G
GinoBambino Themenstarter:in
30 Beiträge seit 2012
vor 11 Jahren

Hallo michlG,

bei mir kennen die ViewModels ebenfalls nicht die Views. Und ich möchte die Software so entwerfen, dass auch die Views nicht explizit auf spezifische ViewModels zugreifen.

Wenn das UserControlVM Teil des MainWindowVM sein soll, wo wird dann die View instanziiert? Ich habe bisher das UserControl im MainWindow instanziiert, also auf View-Ebene (dem Konstruktor übergebe ich dann eine neue Instanz des UserControlVM).

Das UserControl wird dabei einfach in ein Grid abgelegt (es handelt sich um eine Sidebar, welche verschiedene Inhalte aufnehmen kann). Das UserControlVM ist somit im MainWindowVM bisher nicht bekannt.

Angenommen, es wäre bekannt: Die DataContext-Property des MainWindowVM verweist auf das MainWindowVM. Die DataContext-Property des UserControls verweist auf das UserControl VM. Wie kann das UserControl nun Commands auf der Ebene des MainWindowVM auslösen (schließlich kennt es das übergeordnete ViewModel überhaupt nicht)? Müsste ich dazu zwingend von RoutedCommands Gebrauch machen?

Update: Ich habe in einem msdn-Projekt ein Beispiel gefunden, wie man einer ViewModel-Instanz eine View zuweisen kann. Ich bin bisher stets davon ausgegangen, dass nur der umgekehrte Weg möglich ist (einer View mittels der DataContext-Property ein ViewModel zuweisen):

  <!-- 
  This template applies an AllCustomersView to an instance 
  of the AllCustomersViewModel class shown in the main window.
  -->
  <DataTemplate DataType="{x:Type vm:AllCustomersViewModel}">
    <vw:AllCustomersView />
  </DataTemplate>

Kann es sein, dass die einzelnen ViewModels Members vom MainWindowViewModel sind und daher wie gewöhnliche Properties an die MainWindowView gebunden werden?

3.430 Beiträge seit 2007
vor 11 Jahren

Hallo,

also am einfachsten ist wenn du einfach den View über ein DataTemplate laden lässt.
Somit arbeitest du immer nur mit den einzelnen VMs und es wird automatisch der richtige View angezeigt.
Dabei kennen sich die einzelnen Views untereinander auch nicht (was normalerweise auch nicht nötig ist).

Siehe dazu: Linking View to ViewModel using DataTemplate for a WPF Application using the MVVM pattern

Oder habe ich dein Problem nicht richtig verstanden?

Grüße
Michael

G
GinoBambino Themenstarter:in
30 Beiträge seit 2012
vor 11 Jahren

Hi.

Ich habe gerade meinen Beitrag ergänzt. Ich bin beim Googlen ebenfalls auf DataTemplates gestoßen, daher tippe ich mal ganz stark darauf, dass du mich schon richtig verstanden hast.

Ich werde das mal ausprobieren, so wie du es beschrieben hast.

G
GinoBambino Themenstarter:in
30 Beiträge seit 2012
vor 11 Jahren

Hi,

danke für die Antwort. Das ist genau das, was ich gesucht habe.

Eine Frage hätte ich noch, die du mir vielleicht beantworten könntest:

Angenommen, ich habe ein MainWindowVM und darin ein UserControlVM. Ist es möglich, dass das UserControl Data Bindings für Eigenschaften definiert, die sich in MainWindowVM befinden? Dass also untergeordnete ViewModels die Properties eines übergeordneten ViewModels verwenden?

3.430 Beiträge seit 2007
vor 11 Jahren

Hallo,

du kannst dazu entweder das Ancestor-Binding verwenden um vom UserControl auf ein Property im Parent zu binden.
Siehe dazu: How do I use WPF bindings with RelativeSource?

Das scheint aber eine etwas eigenartige Struktur zu sein. Denn dein UserControl sollte eigentlich gar nicht wissen wer sein Parent ist, bzw. nicht von Diesem in irgend einer weise abhängig sein.
Damit hast du eine nicht mehr so gut durchschaubare Struktur die schnell zu Fehlern und Problemen führen kann.

Besser ist da wenn du im deinem UserControlVM die nötigen Properties zur Verfügung stellst. Diese kannst du von deinem MainVM aus setzen.
Wenn du an diese ein Binding manchen willst dann musst du ein DependencyProperty (anstatt einem normalen) verwenden.

Grüße
Michael

G
GinoBambino Themenstarter:in
30 Beiträge seit 2012
vor 11 Jahren

Hallo michlG,

okay, vielen Dank. Ich wollte das UserControl nur lose an das Parent binden. Es sollte abgesehen von einigen Eigenschaften nichts von der tatsächlichen Implementierung des Parents wissen. Damit wäre das UserControl in ähnlicher Weise an das Parent gebunden wie eine View an das ViewModel gebunden ist.

Damit wollte ich verhindern, dass ich die gleichen Eigenschaften in Parent und UserControl (doppelt) aufgeführt habe. Außerdem erhielte ich damit die Möglichkeit, im Parent-Element die Logik für Commands zu definieren, welche vom UserControl ausgelöst werden. Soweit mir bekannt, ist das ansonsten nur mit RoutedCommands möglich, oder?

Mein Ziel ist, dass ich im UserControl Logik zur Definition einiger Eigenschaften zur Verfügung stelle (z.B. der Dateiname für eine zu erstellende Datei). Das UserControl soll außerdem einen Button enthalten, der beim Klick ein Command auslösen soll. Das Command soll dann bewirken, dass im Parent-Element einige Methoden ausgeführt werden.

Mit meinem bisher überwiegend "theoretischem WPF-Wissen" wüsste ich das in der Praxis nicht nach gängiger Konvention umzusetzen.

G
GinoBambino Themenstarter:in
30 Beiträge seit 2012
vor 11 Jahren

Ich habe noch mal intensiv über meine Problematik nachgedacht und mich auch gefragt, ob es richtig ist, was du schreibst.

Es ist zwar richtig, dass ein UserControl nicht wissen sollte, was sein Parent ist. Aber in meinem speziellen Fall teilen sich Parent und UserControl gemeinsame Eigenschaften.

Das UserControl ist letztlich nur eine von mehreren "SideBars", die ich im MainWindow je nach ausgewählter Aktion anzeige. Die Eigenschaften, die in meinen UserControls verwendet werden, müssen auch im MainWindow verfügbar sein.

Wenn ich für das UserControl ein eigenes ViewModel verwendet, ergibt sich ein Problem. Ein Command bewirkt, dass eine Methode im UserControl ausgeführt wird, nicht jedoch im MainWindow. RoutedCommands möchte ich eigentlich nicht gerne verwenden.

Gibt es sonst noch eine Möglichkeit, das MainWindow darüber zu informieren, dass ein Command ausgelöst wurde (am besten wäre es, wenn das Command im MainWindow ausgelöst werden könnte)? Bietet sich hier vielleicht das Mediator-Pattern an?