Gute Beispiele für MVVM-Vorlagen

141

Ich arbeite derzeit mit der Microsoft MVVM-Vorlage und finde das Fehlen detaillierter Beispiele frustrierend. Das mitgelieferte ContactBook-Beispiel zeigt nur sehr wenig Befehlshandhabung. Das einzige andere Beispiel, das ich gefunden habe, stammt aus einem Artikel des MSDN-Magazins, in dem die Konzepte ähnlich sind, jedoch einen etwas anderen Ansatz verwenden und dennoch keine Komplexität aufweisen. Gibt es anständige MVVM-Beispiele, die zumindest grundlegende CRUD-Operationen und das Umschalten von Dialogen / Inhalten zeigen?


Alle Vorschläge waren wirklich nützlich und ich werde anfangen, eine Liste guter Ressourcen zusammenzustellen

Frameworks / Vorlagen

Nützliche Artikel

Screencasts

Zusätzliche Bibliotheken

jwarzech
quelle
Ich bin froh, dass diese Ressourcen geholfen haben. Ich bin derzeit in meiner zweiten MVVM-Produktionsanwendung und werde weiterhin Inhalte hinzufügen, die für diejenigen hilfreich sind, die anfangen, wenn ich darauf stoße.
Jwarzech

Antworten:

59

Leider gibt es keine großartige MVVM-Beispiel-App, die alles kann, und es gibt viele verschiedene Ansätze, um Dinge zu tun. Zunächst möchten Sie sich vielleicht mit einem der App-Frameworks vertraut machen (Prism ist eine gute Wahl), da es Ihnen praktische Tools wie Abhängigkeitsinjektion, Befehle, Ereignisaggregation usw. bietet, mit denen Sie auf einfache Weise verschiedene Muster ausprobieren können, die zu Ihnen passen .

Die Prismenfreigabe:
http://www.codeplex.com/CompositeWPF

Es enthält eine ziemlich anständige Beispiel-App (den Aktienhändler) sowie viele kleinere Beispiele und Anleitungen. Zumindest ist es eine gute Demonstration einiger gängiger Untermuster, mit denen MVVM tatsächlich funktioniert. Ich glaube, sie haben Beispiele für CRUD und Dialoge.

Prisma ist nicht unbedingt für jedes Projekt geeignet, aber es ist eine gute Sache, sich damit vertraut zu machen.

CRUD: Dieser Teil ist ziemlich einfach. WPF-Zweiwege-Bindungen machen es wirklich einfach, die meisten Daten zu bearbeiten. Der eigentliche Trick besteht darin, ein Modell bereitzustellen, mit dem die Benutzeroberfläche einfach eingerichtet werden kann. Zumindest möchten Sie sicherstellen, dass Ihr ViewModel (oder Geschäftsobjekt) implementiert wird INotifyPropertyChanged, um die Bindung zu unterstützen, und Sie können Eigenschaften direkt an UI-Steuerelemente binden, aber Sie möchten sie möglicherweise auch IDataErrorInfozur Validierung implementieren . Wenn Sie eine ORM-Lösung verwenden, ist das Einrichten von CRUD in der Regel ein Kinderspiel.

Dieser Artikel beschreibt einfache Rohoperationen: http://dotnetslackers.com/articles/wpf/WPFDataBindingWithLINQ.aspx

Es basiert auf LinqToSql, aber das ist für das Beispiel irrelevant - alles, was wichtig ist, ist, dass Ihre Geschäftsobjekte implementiert werden INotifyPropertyChanged(welche Klassen von LinqToSql generiert werden). MVVM ist nicht der Punkt dieses Beispiels, aber ich denke nicht, dass es in diesem Fall wichtig ist.

Dieser Artikel demonstriert die Datenvalidierung
http://blogs.msdn.com/wpfsdk/archive/2007/10/02/data-validation-in-3-5.aspx

Wiederum generieren die meisten ORM-Lösungen Klassen, die bereits implementiert sind IDataErrorInfound normalerweise einen Mechanismus bieten, der das Hinzufügen benutzerdefinierter Validierungsregeln erleichtert.

Meistens können Sie ein von einem ORM erstelltes Objekt (Modell) in ein ViewModel einbinden, das es und Befehle zum Speichern / Löschen enthält - und Sie können die Benutzeroberfläche direkt an die Eigenschaften des Modells binden.

Die Ansicht würde ungefähr so ​​aussehen (ViewModel hat eine Eigenschaft Item, die das Modell enthält, wie eine im ORM erstellte Klasse):

<StackPanel>
   <StackPanel DataContext=Item>
      <TextBox Text="{Binding FirstName, Mode=TwoWay, ValidatesOnDataErrors=True}" />
      <TextBox Text="{Binding LastName, Mode=TwoWay, ValidatesOnDataErrors=True}" />
   </StackPanel>
   <Button Command="{Binding SaveCommand}" />
   <Button Command="{Binding CancelCommand}" />
</StackPanel>

Dialoge: Dialoge und MVVM sind etwas knifflig. Ich bevorzuge es, eine Variante des Mediator-Ansatzes mit Dialogen zu verwenden. Weitere Informationen hierzu finden Sie in dieser StackOverflow-Frage:
Beispiel für einen WPF MVVM-Dialog

Mein üblicher Ansatz, der nicht ganz klassisch MVVM ist, kann wie folgt zusammengefasst werden:

Eine Basisklasse für ein Dialogfeld ViewModel, das Befehle zum Festschreiben und Abbrechen von Aktionen bereitstellt, ein Ereignis, mit dem die Ansicht darüber informiert wird, dass ein Dialogfeld zum Schließen bereit ist, und alles, was Sie sonst noch in all Ihren Dialogfeldern benötigen.

Eine allgemeine Ansicht für Ihren Dialog - Dies kann ein Fenster oder ein benutzerdefiniertes "modales" Overlay-Typ-Steuerelement sein. Im Kern handelt es sich um einen Content Presenter, in den wir das Ansichtsmodell kopieren, und der die Verkabelung zum Schließen des Fensters übernimmt. Beispielsweise können Sie bei Änderungen des Datenkontexts überprüfen, ob das neue ViewModel von Ihrer Basisklasse geerbt wurde und ob dies der Fall ist. Abonnieren Sie das entsprechende Abschlussereignis (der Handler weist das Dialogergebnis zu). Wenn Sie eine alternative universelle Schließfunktion bereitstellen (z. B. die Schaltfläche X), sollten Sie sicherstellen, dass Sie den entsprechenden Befehl zum Schließen auch im ViewModel ausführen.

Wenn Sie Datenvorlagen für Ihre ViewModels bereitstellen müssen, können diese sehr einfach sein, insbesondere da Sie wahrscheinlich eine Ansicht für jeden Dialog haben, der in einem separaten Steuerelement gekapselt ist. Die Standarddatenvorlage für ein ViewModel würde dann ungefähr so ​​aussehen:

<DataTemplate DataType="{x:Type vmodels:AddressEditViewModel}">
   <views:AddressEditView DataContext="{Binding}" />
</DataTemplate>

Die Dialogansicht muss Zugriff auf diese haben, da sie sonst nicht weiß, wie das ViewModel angezeigt werden soll. Abgesehen von der Benutzeroberfläche des freigegebenen Dialogfelds sind die Inhalte im Wesentlichen folgende:

<ContentControl Content="{Binding}" />

Die implizite Datenvorlage ordnet die Ansicht dem Modell zu, aber wer startet sie?

Dies ist der nicht so mvvm Teil. Eine Möglichkeit besteht darin, ein globales Ereignis zu verwenden. Ich denke, es ist besser, ein Ereignisaggregatortyp-Setup zu verwenden, das durch Abhängigkeitsinjektion bereitgestellt wird. Auf diese Weise ist das Ereignis für einen Container global und nicht für die gesamte App. Prism verwendet das Unity-Framework für die Containersemantik und die Abhängigkeitsinjektion, und insgesamt gefällt mir Unity ziemlich gut.

Normalerweise ist es sinnvoll, dass das Stammfenster dieses Ereignis abonniert. Es kann den Dialog öffnen und seinen Datenkontext auf das ViewModel festlegen, das mit einem ausgelösten Ereignis übergeben wird.

Wenn Sie dies auf diese Weise einrichten, können ViewModels die Anwendung auffordern, ein Dialogfeld zu öffnen und dort auf Benutzeraktionen zu reagieren, ohne etwas über die Benutzeroberfläche zu wissen, sodass die MVVM-Funktion größtenteils vollständig bleibt.

Es gibt jedoch Situationen, in denen die Benutzeroberfläche die Dialoge öffnen muss, was die Dinge etwas schwieriger machen kann. Überlegen Sie beispielsweise, ob die Dialogposition von der Position der Schaltfläche abhängt, mit der sie geöffnet wird. In diesem Fall benötigen Sie einige UI-spezifische Informationen, wenn Sie ein geöffnetes Dialogfeld anfordern möchten. Im Allgemeinen erstelle ich eine separate Klasse, die ein ViewModel und einige relevante UI-Informationen enthält. Leider scheint dort eine gewisse Kopplung unvermeidlich.

Pseudocode eines Schaltflächenhandlers, der einen Dialog auslöst, der Elementpositionsdaten benötigt:

ButtonClickHandler(sender, args){
    var vm = DataContext as ISomeDialogProvider; // check for null
    var ui_vm = new ViewModelContainer();
    // assign margin, width, or anything else that your custom dialog might require
    ...
    ui_vm.ViewModel = vm.SomeDialogViewModel; // or .GetSomeDialogViewModel()
    // raise the dialog show event
}

Die Dialogansicht wird an Positionsdaten gebunden und das enthaltene ViewModel an das Innere übergeben ContentControl. Das ViewModel selbst weiß immer noch nichts über die Benutzeroberfläche.

Im Allgemeinen verwende ich die DialogResultreturn-Eigenschaft der ShowDialog()Methode nicht und erwarte nicht, dass der Thread blockiert, bis der Dialog geschlossen wird. Ein nicht standardmäßiger modaler Dialog funktioniert nicht immer so, und in einer zusammengesetzten Umgebung möchten Sie oft nicht, dass ein Ereignishandler sowieso so blockiert. Ich ziehe es vor, die ViewModels damit befassen zu lassen - der Ersteller eines ViewModels kann seine relevanten Ereignisse abonnieren, Commit / Cancel-Methoden festlegen usw., sodass Sie sich nicht auf diesen UI-Mechanismus verlassen müssen.

Also anstelle dieses Flusses:

// in code behind
var result = somedialog.ShowDialog();
if (result == ...

Ich benutze:

// in view model
var vm = new SomeDialogViewModel(); // child view model
vm.CommitAction = delegate { this.DoSomething(vm); } // what happens on commit 
vm.CancelAction = delegate { this.DoNothing(vm); } // what happens on cancel/close (optional)
// raise dialog request event on the container

Ich bevorzuge es auf diese Weise, da die meisten meiner Dialoge nicht blockierende pseudomodale Steuerelemente sind und es einfacher erscheint, dies zu tun, als es zu umgehen. Einfach zu testen.

Egor
quelle
Danke für die ausführliche Antwort! Ich habe kürzlich festgestellt, dass mein größtes Problem darin besteht, dass ein MainViewModel mit anderen Ansichtsmodellen kommunizieren muss, um den Ablauf der Anwendung zu bewältigen. Es scheint jedoch, dass MVVM + Mediator der beliebte Ansatz zu sein scheint.
Jwarzech
2
Der Mediator hilft definitiv, das Ereignisaggregatormuster (Prism hat eine gute Implementierung) ist auch sehr hilfreich, wenn eine niedrige Kopplung ein Ziel ist. Außerdem verfügt Ihr Hauptansichtsmodell normalerweise über eigene untergeordnete Ansichtsmodelle und sollte keine Probleme bei der Kommunikation mit ihnen haben. Sie müssen einen Mediator oder / und einen Ereignisaggregator verwenden, wenn Ihre untergeordneten Ansichtsmodelle mit anderen Modulen in Ihrer App interagieren müssen, über die sie nicht unbedingt Bescheid wissen - einschließlich der Benutzeroberfläche (in meinem Dialogbeispiel geht es um diesen speziellen Fall).
Egor
1
Die Richtlinien für die Arbeit mit Dialogen und Fenstern waren sehr hilfreich. Ich habe jedoch einige Probleme: 1. Wie legen Sie den Fenstertitel in der Ansicht fest? 2. Wie gehen Sie mit dem Einstellen des Eigentümerfensters um?
Djskinner
@ Daniel Skinner: Ich gehe davon aus, dass Sie hier über Dialoge sprechen. Korrigieren Sie mich, wenn ich falsch liege. Der Dialogtitel ist nur eine weitere Eigenschaft und Sie können ihn an eine beliebige Eigenschaft binden. Wenn Sie meinem Ansatz mit einer Basisdialogansichtsmodellklasse gefolgt sind (nehmen wir an, sie hat eine Titeleigenschaft), können Sie in Ihrem generischen Dialogfenster die Bindung von Benutzeroberfläche zu Benutzeroberfläche verwenden, um den Titel auf {Binding Path = DataContext.Title, ElementName = zu setzen NameOfContentPresenter}. Das Eigentümerfenster ist ein kleiner Trick - es bedeutet, dass der Mediator, der den Dialog tatsächlich öffnet, über die Root-App-Ansicht Bescheid wissen muss.
Egor
Tatsächlich nehme ich das zurück - unabhängig davon, wie Sie dies irgendwann strukturieren, muss derjenige, der den Dialog tatsächlich öffnet, einen Verweis auf das Fenster / die Ansicht der Root-App haben. Beachten Sie, wo ich sagte: "Normalerweise ist es sinnvoll, dass das Stammfenster dieses Ereignis abonniert. Es kann den Dialog öffnen und seinen Datenkontext auf das Ansichtsmodell festlegen, das mit einem ausgelösten Ereignis übergeben wird." Hier würden Sie den Eigentümer festlegen.
Egor
6

Jason Dolinger hat einen guten Screencast von MVVM gemacht. Wie Egor erwähnt hat, gibt es kein gutes Beispiel. Sie sind alle vorbei. Die meisten sind gute MVVM-Beispiele, aber nicht, wenn Sie in komplexe Probleme geraten. Jeder hat seinen eigenen Weg. Laurent Bugnion hat auch eine gute Möglichkeit, zwischen Ansichtsmodellen zu kommunizieren. http://blog.galasoft.ch/archive/2009/09/27/mvvm-light-toolkit-messenger-v2-beta.aspx Cinch ist ebenfalls ein gutes Beispiel. Paul Stovel hat einen guten Beitrag , der auch mit seinem Magellan-Framework viel erklärt.

nportelli
quelle
3

Hast du dir Caliburn angesehen ? Das ContactManager-Beispiel enthält viele gute Inhalte. Die generischen WPF-Beispiele bieten auch einen guten Überblick über Befehle. Die Dokumentation ist ziemlich gut und die Foren sind aktiv. Empfohlen!

Andy S.
quelle
2

Das Beispielprojekt im Cinch-Framework zeigt grundlegende CRUD- und Navigationswerkzeuge. Es ist ein ziemlich gutes Beispiel für die Verwendung von MVVM und enthält einen mehrteiligen Artikel , in dem die Verwendung und die Motivationen von MVVM erläutert werden.

Reed Copsey
quelle
2

Ich teilte auch Ihre Frustration. Ich schreibe eine Bewerbung und hatte diese 3 Anforderungen:

  • Erweiterbar
  • WPF mit MVVM
  • GPL-kompatible Beispiele

Alles, was ich fand, waren Kleinigkeiten, also fing ich gerade an, es so gut ich konnte zu schreiben. Nachdem ich mich ein wenig damit beschäftigt hatte, wurde mir klar, dass es möglicherweise andere Leute (wie Sie) gibt, die eine Referenzanwendung verwenden könnten. Deshalb habe ich das generische Material in ein WPF / MVVM-Anwendungsframework umgestaltet und es unter der LGPL veröffentlicht. Ich habe es SoapBox Core genannt . Wenn Sie zur Downloadseite gehen, wird eine kleine Demoanwendung mitgeliefert, und der Quellcode für diese Demoanwendung steht auch zum Download zur Verfügung. Ich hoffe, Sie finden das hilfreich. Senden Sie mir auch eine E-Mail an scott {at} soapboxautomation.com, wenn Sie weitere Informationen wünschen.

BEARBEITEN : Außerdem wurde ein CodeProject-Artikel veröffentlicht , in dem die Funktionsweise erläutert wird.

Scott Whitlock
quelle
2

Ich habe ein einfaches MVVM-Beispiel von Grund auf für ein Code-Projekt geschrieben. Hier ist der Link MVVM WPF Schritt für Schritt . Es geht von einer einfachen 3-Schicht-Architektur aus und bietet Ihnen die Möglichkeit, ein Framework wie PRISM zu verwenden.

Geben Sie hier die Bildbeschreibung ein

Shivprasad Koirala
quelle
1

Sogar ich teilte die Frustration, bis ich die Angelegenheit in meine Hände nahm. Ich habe IncEditor gestartet.

IncEditor ( http://inceditor.codeplex.com ) ist ein Editor, der versucht, Entwicklern WPF, MVVM und MEF vorzustellen. Ich habe es gestartet und es geschafft, einige Funktionen wie "Theme" -Unterstützung zu erhalten. Ich bin kein Experte für WPF, MVVM oder MEF, daher kann ich nicht viele Funktionen einbauen. Ich bitte euch aufrichtig, es besser zu machen, damit Verrückte wie ich es besser verstehen können.

Abdulsattar Mohammed
quelle