web-dev-qa-db-de.com

MVVM in WPF - Wie kann ich ViewModel über Änderungen in Model informieren ... oder soll ich?

Ich gehe einige MVVM-Artikel durch, hauptsächlich das und das .

Meine spezifische Frage ist: Wie kommuniziere ich Modelländerungen vom Modell zum ViewModel? 

In Joshs Artikel sehe ich nicht, dass er das tut. Das ViewModel fragt das Model immer nach Eigenschaften. In Rachels Beispiel hat sie die Implementierung des Modells INotifyPropertyChanged und ruft Ereignisse aus dem Modell hervor, die jedoch von der Ansicht selbst verwendet werden können (siehe ihren Artikel/Code für weitere Einzelheiten, warum sie dies tut). 

Nirgendwo sehe ich Beispiele, bei denen das Modell das ViewModel über Änderungen an Modelleigenschaften benachrichtigt. Das hat mir Sorgen gemacht, dass es vielleicht aus irgendeinem Grund nicht gemacht wird. Gibt es ein Muster, um das ViewModel über Änderungen im Modell zu warnen? Es scheint notwendig zu sein, da (1) es denkbar ist, dass es mehr als 1 ViewModel für jedes Modell gibt, und (2) selbst wenn nur ein ViewModel vorhanden ist, einige Aktionen des Modells möglicherweise dazu führen, dass andere Eigenschaften geändert werden. 

Ich vermute, dass es Antworten/Kommentare zu dem Formular geben kann "Warum möchten Sie das tun?" Kommentare, hier ist eine Beschreibung meines Programms. Ich bin neu bei MVVM, daher ist mein gesamtes Design möglicherweise fehlerhaft. Ich werde es kurz beschreiben.

Ich programmiere etwas, das interessanter ist (zumindest für mich!) Als die Klassen "Kunde" oder "Produkt". Ich programmiere BlackJack. 

Ich habe eine View, die keinen Code hinter sich hat und nur auf die Bindung an Eigenschaften und Befehle im ViewModel angewiesen ist (siehe Josh Smiths Artikel). 

Zum Besseren oder Schlechten nahm ich an, dass das Modell nicht nur Klassen wie PlayingCard, Deck enthalten sollte, sondern auch die BlackJackGame-Klasse, die den Status des gesamten Spiels beibehält, und weiß, wenn der Spieler pleite geht, der Händler ziehen muss Karten und die aktuelle Punktzahl des Spielers und Händlers (weniger als 21, 21, Büste usw.). 

Von BlackJackGame aus stelle ich Methoden wie "DrawCard" offen, und mir ist aufgefallen, dass beim Ziehen einer Karte Eigenschaften wie CardScore und IsBust aktualisiert werden sollten und diese neuen Werte dem ViewModel mitgeteilt werden. Vielleicht ist das falsches Denken? 

Man könnte die Einstellung annehmen, dass das ViewModel die DrawCard()-Methode genannt hat, so dass er wissen sollte, ob er nach einer aktualisierten Bewertung fragen und herausfinden möchte, ob er kaputt ist oder nicht. Meinungen 

In meinem ViewModel habe ich die Logik, ein aktuelles Bild einer Spielkarte (basierend auf Farbe, Rang) abzurufen und für die Ansicht verfügbar zu machen. Das Modell sollte sich nicht damit befassen (vielleicht würde ViewModel nur Zahlen verwenden, anstatt Kartenbilder abzuspielen). Natürlich sagen mir manche vielleicht, dass das Model nicht einmal das Konzept eines BlackJack-Spiels haben sollte und das im ViewModel behandelt werden sollte.

96
Dave

Wenn Sie möchten, dass Ihre Modelle die ViewModels über Änderungen benachrichtigen, sollten sie INotifyPropertyChanged implementieren und die ViewModels sollten den Erhalt von PropertyChange-Benachrichtigungen abonnieren.

Ihr Code könnte ungefähr so ​​aussehen:

// Attach EventHandler
PlayerModel.PropertyChanged += PlayerModel_PropertyChanged;

...

// When property gets changed in the Model, raise the PropertyChanged 
// event of the ViewModel copy of the property
PlayerModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
    if (e.PropertyName == "SomeProperty")
        RaisePropertyChanged("ViewModelCopyOfSomeProperty");
}

Dies ist jedoch normalerweise nur dann erforderlich, wenn mehr als ein Objekt Änderungen an den Daten des Modells vornimmt, was normalerweise nicht der Fall ist.

Wenn Sie jemals einen Fall haben, in dem Sie nicht wirklich einen Verweis auf Ihre Model-Eigenschaft haben, um das PropertyChanged-Ereignis daran anzuhängen, können Sie ein Messaging-System verwenden, z. B. EventAggregator von Prism oder Messenger von MVVM Light.

Ich habe in meinem Blog einen kurzen Überblick über Messagingsysteme . Um es jedoch zusammenzufassen, kann jedes Objekt eine Nachricht übertragen und jedes Objekt kann das Abhören bestimmter Nachrichten abonnieren. Sie können also eine PlayerScoreHasChangedMessage von einem Objekt aus übertragen, und ein anderes Objekt kann abonnieren, um auf diese Nachrichtentypen zu warten und ihre PlayerScore-Eigenschaft zu aktualisieren, wenn sie eine hört.

Aber ich denke nicht, dass dies für das beschriebene System erforderlich ist.

In einer idealen MVVM-Welt besteht Ihre Anwendung aus Ihren ViewModels, und Ihre Modelle sind nur die Blöcke, die zum Erstellen Ihrer Anwendung verwendet werden. Sie enthalten normalerweise nur Daten und würden daher keine Methoden wie DrawCard() (die sich in einem ViewModel befinden würden) enthalten.

Sie hätten also wahrscheinlich einfache Modelldatenobjekte wie diese:

class CardModel
{
    int Score;
    SuitEnum Suit;
    CardEnum CardValue;
}

class PlayerModel 
{
    ObservableCollection<Card> FaceUpCards;
    ObservableCollection<Card> FaceDownCards;
    int CurrentScore;

    bool IsBust
    {
        get
        {
            return Score > 21;
        }
    }
}

und Sie hätten gerne ein ViewModel-Objekt

public class GameViewModel
{
    ObservableCollection<CardModel> Deck;
    PlayerModel Dealer;
    PlayerModel Player;

    ICommand DrawCardCommand;

    void DrawCard(Player currentPlayer)
    {
        var nextCard = Deck.First();
        currentPlayer.FaceUpCards.Add(nextCard);

        if (currentPlayer.IsBust)
            // Process next player turn

        Deck.Remove(nextCard);
    }
}

(Obige Objekte sollten alle INotifyPropertyChanged implementieren, aber ich habe es der Einfachheit halber weggelassen.)

55
Rachel

Kurze Antwort: Es hängt von den Besonderheiten ab.

In Ihrem Beispiel werden die Modelle "eigenständig" aktualisiert, und diese Änderungen müssen sich natürlich auf die Ansichten übertragen. Da die Ansichten nur direkt auf die Ansichtsmodelle zugreifen können, muss das Modell diese Änderungen an das entsprechende Ansichtsmodell übermitteln. Der etablierte Mechanismus dafür ist natürlich INotifyPropertyChanged, was bedeutet, dass Sie einen Workflow wie folgt erhalten:

  1. Das Viewmodel wird erstellt und umschließt das Modell
  2. Das Viewmodel abonniert das PropertyChanged-Ereignis des Modells
  3. Das Viewmodel ist als DataContext der Ansicht festgelegt, die Eigenschaften sind gebunden usw
  4. View löst eine Aktion für Viewmodel aus
  5. Viewmodel ruft die Methode für das Modell auf
  6. Das Modell aktualisiert sich selbst
  7. Das Viewmodel behandelt die PropertyChanged des Modells und gibt als Reaktion eine eigene PropertyChanged aus
  8. View spiegelt die Änderungen in den Bindungen wider und schließt die Rückkopplungsschleife

Auf der anderen Seite, wenn Ihre Modelle wenig (oder keine) Geschäftslogik enthielten oder wenn Sie aus einem anderen Grund (z. B. Transaktionsfähigkeit) beschlossen haben, dass jedes Ansichtsmodell das umschlossene Modell "besitzen" lässt, werden alle Änderungen am Modell durchlaufen das Sichtmodell also eine solche Anordnung wäre nicht notwendig.

Ich beschreibe ein solches Design in einer anderen MVVM-Frage hier .

21
Jon

Deine Entscheidungen: 

  • Implementieren Sie INotifyPropertyChanged
  • Veranstaltungen
  • POCO mit Proxy-Manipulator

Für mich ist INotifyPropertyChanged ein grundlegender Bestandteil von .Net. d. h. in System.dll. Die Implementierung in Ihrem "Modell" ähnelt der Implementierung einer Ereignisstruktur. 

Wenn Sie ein reines POCO wünschen, müssen Sie Ihre Objekte effektiv über Proxys/Services bearbeiten. Anschließend wird Ihr ViewModel durch Abhören des Proxys über Änderungen benachrichtigt. 

Ich persönlich implementiere einfach INotifyPropertyChanged und verwende dann FODY , um die Drecksarbeit für mich zu erledigen. Es sieht aus und fühlt sich POCO an. 

Ein Beispiel (mit FODY zum IL Weave der PropertyChanged-Erhöhungen): 

public class NearlyPOCO: INotifyPropertyChanged
{
     public string ValueA {get;set;}
     public string ValueB {get;set;}

     public event PropertyChangedEventHandler PropertyChanged;
}

dann können Sie ViewModel PropertyChanged auf etwaige Änderungen anhören. oder eigenschaftsspezifische Änderungen. 

Das Schöne an der INotifyPropertyChanged-Route ist die Verkettung mit einer Extended ObservableCollection . Sie legen also Ihre nahen Poco-Objekte in eine Sammlung und hören sich die Sammlung an ... wenn sich irgendwo etwas ändert, erfahren Sie davon.

Um ehrlich zu sein, könnte dies an der Diskussion "Warum wurde INotifyPropertyChanged nicht automatisch vom Compiler gehandhabt werden?" Teilnehmen, die sich auf Folgendes bezieht: Jedes Objekt in c # sollte die Möglichkeit haben, zu benachrichtigen, ob ein Teil davon geändert wurde; implementieren Sie INotifyPropertyChanged standardmäßig. Dies ist jedoch nicht der Fall, und die beste Route, die den geringsten Aufwand erfordert, ist IL Weaving (speziell FODY ). 

3
Meirion Hughes

Ziemlich alter Thread, aber nach langem Suchen fand ich meine eigene Lösung: Ein PropertyChangedProxy

Mit dieser Klasse können Sie sich problemlos bei NotifyPropertyChanged einer anderen Person registrieren und geeignete Maßnahmen ergreifen, wenn diese für die registrierte Eigenschaft ausgelöst wird.

Hier ein Beispiel, wie dies aussehen könnte, wenn Sie eine Modelleigenschaft "Status" haben, die sich selbst ändern kann, und das ViewModel automatisch dazu aufgefordert wird, seine eigene PropertyChanged-Eigenschaft "Status" auszulösen, sodass die Ansicht ebenfalls benachrichtigt wird: )

public class MyModel : INotifyPropertyChanged
{
    private string _status;
    public string Status
    {
        get { return _status; }
        set { _status = value; OnPropertyChanged(); }
    }

    // Default INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

public class MyViewModel : INotifyPropertyChanged
{
    public string Status
    {
        get { return _model.Status; }
    }

    private PropertyChangedProxy<MyModel, string> _statusPropertyChangedProxy;
    private MyModel _model;
    public MyViewModel(MyModel model)
    {
        _model = model;
        _statusPropertyChangedProxy = new PropertyChangedProxy<MyModel, string>(
            _model, myModel => myModel.Status, s => OnPropertyChanged("Status")
        );
    }

    // Default INotifyPropertyChanged
    public event PropertyChangedEventHandler PropertyChanged;
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        var handler = PropertyChanged;
        if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
    }
}

und hier ist die Klasse selbst:

/// <summary>
/// Proxy class to easily take actions when a specific property in the "source" changed
/// </summary>
/// Last updated: 20.01.2015
/// <typeparam name="TSource">Type of the source</typeparam>
/// <typeparam name="TPropType">Type of the property</typeparam>
public class PropertyChangedProxy<TSource, TPropType> where TSource : INotifyPropertyChanged
{
    private readonly Func<TSource, TPropType> _getValueFunc;
    private readonly TSource _source;
    private readonly Action<TPropType> _onPropertyChanged;
    private readonly string _modelPropertyname;

    /// <summary>
    /// Constructor for a property changed proxy
    /// </summary>
    /// <param name="source">The source object to listen for property changes</param>
    /// <param name="selectorExpression">Expression to the property of the source</param>
    /// <param name="onPropertyChanged">Action to take when a property changed was fired</param>
    public PropertyChangedProxy(TSource source, Expression<Func<TSource, TPropType>> selectorExpression, Action<TPropType> onPropertyChanged)
    {
        _source = source;
        _onPropertyChanged = onPropertyChanged;
        // Property "getter" to get the value
        _getValueFunc = selectorExpression.Compile();
        // Name of the property
        var body = (MemberExpression)selectorExpression.Body;
        _modelPropertyname = body.Member.Name;
        // Changed event
        _source.PropertyChanged += SourcePropertyChanged;
    }

    private void SourcePropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == _modelPropertyname)
        {
            _onPropertyChanged(_getValueFunc(_source));
        }
    }
}
3
Roemer

Ich fand diesen Artikel hilfreich: http://social.msdn.Microsoft.com/Forums/vstudio/en-US/3eb70678-c216-414f-a4a5-e1e3e557bb95/mvvm-businesslogic-is-part-of -the-? forum = wpf

Meine Zusammenfassung:

Die Idee der MVVM-Organisation besteht darin, Ansichten und Modelle einfacher wiederzuverwenden und auch entkoppelte Tests zu ermöglichen. Ihr Ansichtsmodell ist ein Modell, das die Ansichtsentitäten darstellt, Ihr Modell repräsentiert die Geschäftsentitäten.

Was wäre, wenn Sie später ein Pokerspiel machen wollten? Ein Großteil der Benutzeroberfläche sollte wiederverwendbar sein. Wenn Ihre Spielelogik in Ihr Ansichtsmodell eingebunden ist, ist es sehr schwierig, diese Elemente wiederzuverwenden, ohne das Ansichtsmodell neu programmieren zu müssen. Was ist, wenn Sie Ihre Benutzeroberfläche ändern möchten? Wenn Ihre Spielelogik mit Ihrer Ansichtsmodell-Logik gekoppelt ist, müssen Sie erneut prüfen, ob Ihr Spiel noch funktioniert. Was ist, wenn Sie einen Desktop und eine Web-App erstellen möchten? Wenn Ihr Ansichtsmodell die Spielelogik enthält, wird es kompliziert, diese beiden Anwendungen nebeneinander zu verwalten, da die Anwendungslogik unweigerlich mit der Geschäftslogik im Ansichtsmodell verbunden ist.

Datenänderungsbenachrichtigungen und Datenvalidierung finden in jeder Schicht (der Ansicht, dem Ansichtsmodell und dem Modell) statt.

Das Modell enthält Ihre Datendarstellungen (Entitäten) und Geschäftslogik, die für diese Entitäten spezifisch sind. Ein Kartenspiel ist eine logische "Sache" mit inhärenten Eigenschaften. Bei einem guten Deck können keine doppelten Karten eingelegt werden. Es muss eine Möglichkeit offen gelegt werden, um die obersten Karten zu erhalten. Es muss wissen, dass nicht mehr Karten ausgegeben werden, als übrig sind. Solche Verhaltensweisen sind Teil des Modells, da sie einem Kartenstapel innewohnen. Es wird auch Dealer-Modelle, Spielermodelle, Handmodelle usw. geben. Diese Modelle können und werden interagieren.

Das Ansichtsmodell würde aus der Präsentations- und Anwendungslogik bestehen. Die gesamte Arbeit, die mit der Anzeige des Spiels verbunden ist, ist von der Logik des Spiels getrennt. Dies kann die Anzeige von Händen als Bilder, Kartenanforderungen für das Händlermodell, Benutzeranzeigeeinstellungen usw. umfassen.

Die Hintergründe des Artikels:

Im Grunde erkläre ich das gerne so, dass es Ihr Geschäft ist Logik und Entitäten bilden das Modell. Dies ist, was Ihre spezifische Anwendung wird verwendet, kann aber von vielen Anwendungen gemeinsam genutzt werden.

Die Ansicht ist die Präsentationsschicht - alles, was sich tatsächlich auf .__ bezieht. direkte Schnittstelle zum Benutzer.

Das ViewModel ist im Grunde der "Kleber", der für Ihr .__ spezifisch ist. Anwendung, die die beiden miteinander verbindet.

Ich habe hier ein schönes Diagramm, das zeigt, wie sie miteinander verbunden sind: 

http://reedcopsey.com/2010/01/06/better-user-and-developer-erfahrungen-von-windows-forms-to-wpf-mit-mvvm-part-7-mvvm/

In Ihrem Fall - lassen Sie uns einige Details besprechen ...

Validierung: Dies kommt in der Regel in zwei Formen. Die Validierung bezog sich auf Benutzereingaben würden in ViewModel (hauptsächlich) und View .__ erfolgen. (dh: "Numerisch" TextBox, das die Eingabe von Text verhindert, wird .__ für Sie in der Ansicht usw. behandelt). Daher wird die Überprüfung der Eingabe von Bei dem Benutzer handelt es sich normalerweise um ein Problem VM. Davon abgesehen gibt es oft eine zweite "Schicht" der Validierung - dies ist die Validierung der Daten verwendet wird, entspricht den Geschäftsregeln. Dies ist oft Teil von Modell selbst - Wenn Sie Daten in Ihr Modell verschieben, kann dies __. Validierungsfehler. Die VM muss dann diese Informationen neu zuordnen zurück zur Ansicht.

Vorgänge "hinter den Kulissen ohne Sicht, z. B. Schreiben in eine Datenbank, Senden von E-Mail-Nachrichten usw.": Dies ist wirklich Teil der "domänenspezifischen Vorgänge" in meinem Diagramm und ist wirklich ein reiner Teil des Modells. Dies ist, was Sie versuchen, über die Anwendung verfügbar zu machen. Das ViewModel fungiert als Brücke, um diese Informationen anzuzeigen, aber das Operationen sind pure-Model.

Operationen für das ViewModel: Das ViewModel benötigt mehr als nur INPC - Außerdem müssen alle Vorgänge ausgeführt werden, die für Ihre Anwendung spezifisch sind (nicht Ihre Geschäftslogik), z. B. das Speichern von Einstellungen und der Benutzerstatus usw. Dies wird App variieren. per app., auch wenn die gleiches "Modell". 

Eine gute Möglichkeit, darüber nachzudenken - Nehmen Sie an, Sie möchten 2 Versionen Ihres Bestellsystem. Der erste ist in WPF und der zweite ist ein Web Schnittstelle.

Die gemeinsame Logik, die sich mit den Bestellungen selbst befasst (Versenden von E-Mails, Eingabe in eine Datenbank usw.), ist das Modell. Ihre Bewerbung lautet dem Benutzer diese Operationen und Daten zur Verfügung stellen, dies aber in 2 .__ tun. Wege.

In der WPF-Anwendung ist die Benutzeroberfläche (mit der der Betrachter mit Interagiert) die "Ansicht". In der Webanwendung ist dies im Wesentlichen die Code, der (zumindest irgendwann) in Javascript + HTML + CSS umgewandelt wird auf dem Client.

Das ViewModel ist der Rest des "Klebers", der zur Anpassung Ihres .__ benötigt wird. Modell (diese Vorgänge beziehen sich auf die Bestellung), damit es funktioniert mit der spezifischen Ansichtstechnologie/Schicht, die Sie verwenden.

2
VoteCoffee

Eine Benachrichtigung basierend auf INotifyPropertyChanged und INotifyCollectionChanged ist genau das, was Sie brauchen. Um Ihr Leben durch das Abonnieren von Eigenschaftsänderungen, die Überprüfung der Gültigkeit der Eigenschaft während der Kompilierung und die Vermeidung von Speicherverlusten zu vereinfachen, würde ich Ihnen raten, PropertyObserver von Josh Smiths MVVM Foundation zu verwenden. Da dieses Projekt Open Source ist, können Sie Ihrem Projekt nur diese Klasse aus Quellen hinzufügen.

Um zu verstehen, wie PropertyObserver verwendet wird, lesen Sie diesen Artikel .

Schauen Sie sich auch Reactive Extensions (Rx) genauer an. Sie können IObserver <T> in Ihrem Modell verfügbar machen und es im Ansichtsmodell abonnieren.

2

Die Jungs haben eine erstaunliche Arbeit geleistet, als sie das beantworteten, aber in solchen Situationen habe ich wirklich das Gefühl, dass das MVVM-Muster ein Schmerz ist. Ich würde also einen Supervising Controller oder einen Passive View-Ansatz verwenden und das Bindungssystem mindestens für Modellobjekte freigeben sind Änderungen selbst erzeugen.

1
Ibrahim Najjar

Ich habe mich schon lange für das direktionale Modell -> Modell anzeigen -> Änderungsfluss von Änderungen befürwortet, wie Sie im Abschnitt Änderungsfluss meines MVVM-Artikels von 2008 sehen können. Dies erfordert INotifyPropertyChanged am Modell implementieren. Soweit ich das beurteilen kann, ist es mittlerweile üblich geworden.

Da Sie Josh Smith erwähnt haben, werfen Sie einen Blick auf seine PropertyChanged-Klasse . Es ist eine Hilfsklasse für das Abonnieren des INotifyPropertyChanged.PropertyChanged-Ereignisses des Modells.

Sie können diesen Ansatz tatsächlich viel weiter verfolgen, da ich kürzlich mit meiner PropertiesUpdater-Klasse erstellt habe. Eigenschaften des Ansichtsmodells werden als komplexe Ausdrücke berechnet, die eine oder mehrere Eigenschaften des Modells enthalten.

1
HappyNomad

Es ist nichts Falsches, INotifyPropertyChanged in Model zu implementieren und in ViewModel anzuhören. Tatsächlich können Sie in XAML sogar das Eigenschaftsrecht von model verwenden: {Binding Model.ModelProperty}

Bei abhängigen/berechneten schreibgeschützten Eigenschaften habe ich bisher noch nichts Besseres und Einfacheres gesehen: https://github.com/StephenCleary/CalculatedProperties . Es ist sehr einfach, aber unglaublich nützlich. Es ist wirklich "Excel-Formeln für MVVM" - funktioniert genauso wie Excel, das Änderungen an Formelzellen ohne zusätzlichen Aufwand von Ihrer Seite weitergibt.

0
KolA

Sie können Ereignisse aus dem Modell auslösen, die das Viewmodel abonnieren müsste.

Ich habe zum Beispiel kürzlich an einem Projekt gearbeitet, für das ich eine Baumansicht erstellen musste (natürlich hatte das Modell einen hierarchischen Charakter). Im Modell hatte ich eine beobachtbare Sammlung namens ChildElements.

Im Ansichtsmodell hatte ich einen Verweis auf das Objekt im Modell gespeichert und das CollectionChanged -Ereignis der Beobachtungssammlung wie folgt abonniert: ModelObject.ChildElements.CollectionChanged += new CollectionChangedEventHandler(insert function reference here)...

Dann wird Ihr Ansichtsmodell automatisch benachrichtigt, sobald sich das Modell ändert. Sie können dasselbe Konzept mit PropertyChanged verfolgen, Sie müssen jedoch Eigenschaftsänderungsereignisse explizit aus Ihrem Modell auslösen, damit dies funktioniert.

0
Mash

Dies scheint mir eine sehr wichtige Frage zu sein - auch wenn es keinen Druck gibt. Ich arbeite an einem Testprojekt, das eine TreeView beinhaltet. Es gibt Menüelemente und solche, die Befehlen zugeordnet sind, beispielsweise Löschen. Derzeit aktualisiere ich sowohl das Modell als auch das Ansichtsmodell aus dem Ansichtsmodell heraus.

Zum Beispiel,

public void DeleteItemExecute ()
{
    DesignObjectViewModel node = this.SelectedNode;    // Action is on selected item
    DocStructureManagement.DeleteNode(node.DesignObject); // Remove from application
    node.Remove();                                // Remove from view model
    Controller.UpdateDocument();                  // Signal document has changed
}

Dies ist einfach, scheint aber einen sehr grundlegenden Fehler zu haben. Ein typischer Komponententest würde den Befehl ausführen und dann das Ergebnis im Ansichtsmodell überprüfen. Dies prüft jedoch nicht, dass das Modellupdate korrekt war, da beide gleichzeitig aktualisiert werden.

Daher ist es vielleicht besser, Techniken wie PropertyObserver zu verwenden, damit das Modellupdate ein Ansichtsmodellupdate auslöst. Der gleiche Unit-Test würde jetzt nur funktionieren, wenn beide Aktionen erfolgreich waren.

Ich weiß, dass dies keine mögliche Antwort ist, aber es scheint es wert zu sein, da draußen vorzugehen.

0
Art