2009-07-07 4 views
18

Wie kann ich das Abbrechen der Bearbeitung eines Objekts mit MVVM implementieren?Wie wird eine Bearbeitung eines Objekts mit MVVM abgebrochen?

Zum Beispiel: Ich habe eine Kundenliste. Ich wähle einen Kunden und klicke auf den Button "Bearbeiten", ein Dialogfenster (DataContext wird an CustomerViewModel gebunden) öffnet sich und ich beginne mit der Bearbeitung der Kundenfelder. Und dann beschließe ich, die Bearbeitung abzubrechen, aber die Felder des Kunden wurden bereits geändert. Wie kann ich einen Kunden in den vorherigen Status in MVVM zurückversetzen?

Antwort

11

Überprüfen Sie die IEditableObject Schnittstelle. Ihre Customer Klasse sollte das implementieren, und Ihre Befehle können BeginEdit/CancelEdit/EndEdit entsprechend ausführen.

+0

IEditableObject erzeugt eine Menge Overhead für Ihre Objekte. Insbesondere wenn Ihre Model-Objekte eine Klasse und kein Struct sind, müssten Sie Ihre Modellobjekte neu schreiben, um sie zu unterstützen. – Agies

+6

@Agies: Warum der Downvote? Ob IEditableObject "viel Overhead" ist oder nicht, hängt ganz von Ihrer Infrastruktur ab oder davon, wie Sie es implementieren möchten. Es ist nur eine Schnittstelle, die WPF versteht. Wie Sie es umsetzen, liegt an Ihnen. –

+0

+1, ja, ich möchte es mit IEditableObject implementieren, aber ich habe eine ViewModelBase , die eine Eigenschaft Modell des Typs TModel verfügbar macht, und ich binde die Ansicht direkt auf die Eigenschaften des exponierten Modells. Nun, wie könnte ich noch "Abbrechen bearbeiten" verwenden, auch jetzt, da Sie mein Szenario kennen, sagen Sie, dass mein TModel eine Address-Entität ist. Im Ansichtsmodus bindet es nur an die FullAddress-Zeile und verwendet die AddressDataTemplate (eine Verknüpfung zu GMaps), aber wenn der Benutzer auf die Edit-Schaltfläche in der AddressView klickt, sollte ein ChildWindow (SL oder ein beliebiges Fenster in WPF geöffnet werden)) wird fortgesetzt ... – Shimmy

3

In this article, Raul nur das Objekt von der DB neu zu laden. Ich schätze, es ist weniger problematisch als die Lösung, die Kent vorschlägt.

internal void Cancel(CustomerWorkspaceViewModel cvm) 
    { 
     Mainardi.Model.ObjectMapping.Individual dc = cvm.DataContext 
           as Mainardi.Model.ObjectMapping.Individual; 

     int index = 0; 

     if (dc.ContactID > 0 && dc.CustomerID > 0) 
     { 
      index = _customerCollectionViewModel.List.IndexOf(dc); 
      _customerCollectionViewModel.List[index] = 
            _customerBAL.GetCustomerById(dc.CustomerID); 
     } 

     Collection.Remove(cvm); 
    } 
+0

Ich denke, das Nachladen von der DB ist eine Möglichkeit, die Verpflichtungen des IEditableObject von Herrn Boogaarts Vorschlag zu erfüllen, nicht notwendigerweise eine Alternative zu demselben. – Guge

0

Sie könnte auch in Ihrem Ansichtsmodell der staatlichen Modell kopieren, um interne Felder, und diese dann aussetzen und sie dann nur festgelegt auf dem Modell, wenn tatsächlich der Benutzer die Änderung verpflichtet. Das Problem könnte sein, dass die On-the-Fly-Validierung problematischer ist, wenn die Validierung auf der aktualisierten Entität beruht. Wenn dies eine Voraussetzung ist, können Sie einen Klon des Modells erstellen, mit dem der Klon arbeiten und ihn dann zusammenführen soll die tatsächliche Entität, wenn sie gespeichert wird.

4

Eine super einfache Möglichkeit, wenn Ihr Objekt bereits serialisierbar ist, etwa wenn Sie WCF verwenden. Sie können Ihr ursprüngliches Objekt in ein internes Feld serialisieren. Wenn Ihr Objekt nicht serialisierbar ist, verwenden Sie einfach AutoMapper, um eine Kopie Ihres Objekts mit einer Codezeile zu erstellen.

Order backup = Mapper.Map<Order, Order>(order); 

Wenn Sie Ihren CancelCommand bearbeiten, rufen Sie AutoMapper einfach umgekehrt auf. Da Ihre Eigenschaften bereits eine Änderungsbenachrichtigung haben, funktioniert alles. Es ist möglich, diese Techniken mit IEditableObject zu kombinieren, wenn Sie den zusätzlichen Code schreiben müssen.

+0

Der Nachteil von AutoMapper ist, dass nur der Status erfasst wird, der durch die Eigenschaften reflektiert wird. So wie andere Lösungen, die annehmen, dass jeder Status für jede editierbare Klasse über Lese-/Schreibeigenschaften verfügbar gemacht wird, sollte dies für Szenarios funktionieren, die DTOs oder andere ähnliche anämische Objekte verwenden, aber in dem Fall, wo es sich um Domänenobjekte handelt keine Eigenschaften sein. Binäre Serialisierung und NetDataContractSerialization haben dieses Problem nicht, da sie mit Feldern arbeiten. Übrigens ist es schwierig, wenn Events und Delegierte ins Spiel kommen. – jpierson

0

Sie können Bindung mit UpdateSourceTrigger = Explicit verwenden. Here finden Sie weitere Informationen, wie dies implementiert werden kann.

+2

Gute Antwort sollte spezifischer sein, am besten, wenn es Codebeispiel enthält. –

+1

Obwohl diese Antwort fehlt Details, denke ich, es ist die sauberste Lösung. UpdateSourceTrigger = Explicit wird genau für das gemacht, was terkri versucht. Die anderen Lösungen, obwohl sie funktionieren können, sind wirklich nur Hacks. – user2780436

+0

Wenn Ihre Validierung darauf basiert, Quelländerungen zu verfolgen (zB über 'IDataErrorInfo'), erhalten Sie erst dann eine Validierung, wenn Sie die Quelle aktualisieren (wenn überhaupt, ich bin mir nicht sicher), an welcher Stelle Sie noch sein möchten Sie können alle Änderungen rückgängig machen, aber Ihr Modell wurde bereits aktualisiert. – Dan

0

Basierend auf Камен Великов's answer:

Sie Ihre Bindungen markieren können, wie durch die Definition

<TextBox Name="yourTextBox" Text="{BindingPath=YourBinding, UpdateSourceTrigger=Explicit}" />

Ihrer Ansicht (XAML) manuell aktualisiert werden. Dann müssen Sie die Änderungen von Ihrer Benutzeroberfläche in ViewModel schreiben, indem Sie


aufrufen, wenn auf Speichern geklickt wird.

Bitte beachten Sie, wenn die verbindliche Quelle aktualisiert wird, die von etwas anderem ausgelöst wurde, werden sie immer noch direkt in der Benutzeroberfläche angezeigt.

1

Ich hatte dieses Problem auch. Ich löste es mit "The Memento Pattern Design". Mit diesem Muster könnten Sie einfach eine Kopie Ihres Originalobjekts speichern und in selectedIndexChange (eines Steuerelements) oder in der Abbrechen-Schaltfläche können Sie einfach die vorherige Version Ihres Objekts wiederherstellen.

Ein Beispiel für die Verwendung dieses Musters ist bei How is the Memento Pattern implemented in C#4?

Ein Beispiel-Code zur Verfügung:

Wenn wir eine Klasse Benutzer mit Eigenschaften haben Benutzername Passwort und NombrePersona wir hinzufügen müssen Methoden CreateMemento und SetMemento:

public class Usuario : INotifyPropertyChanged 
{ 
    #region "Implementación InotifyPropertyChanged" 

    internal void RaisePropertyChanged(string prop) 
    { 
     if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(prop)); } 
    } 
    public event PropertyChangedEventHandler PropertyChanged; 

    #endregion 

    private String _UserName = "Capture su UserName"; 

    public String UserName 
    { 
     get { return _UserName; } 
     set { _UserName = value; RaisePropertyChanged("UserName"); } 
    } 

    private String _Password = "Capture su contraseña"; 

    public String Password 
    { 
     get { return _Password; } 
     set { _Password = value; RaisePropertyChanged("Password"); } 
    } 

    private String _NombrePersona = "Capture su nombre"; 

    public String NombrePersona 
    { 
     get { return _NombrePersona; } 
     set { _NombrePersona = value; RaisePropertyChanged("NombrePersona"); } 
    } 

    // Creates memento 
    public Memento CreateMemento() 
    { 
     return (new Memento(this)); 
    } 

    // Restores original state 
    public void SetMemento(Memento memento) 
    { 
     this.UserName memento.State.UserName ; 
     this.Password = memento.State.Password ; 
     this.NombrePersona = memento.State.NombrePersona; 
    } 

Dann brauchen wir eine Klasse Memento, die die „Kopie“ unseres Objekt wie folgt enthalten:

/// <summary> 
/// The 'Memento' class 
/// </summary> 
public class Memento 
{ 
    //private Usuario _UsuarioMemento; 
    private Usuario UsuarioMemento { get; set; } 

    // Constructor 
    public Memento(Usuario state) 
    { 
     this.UsuarioMemento = new Usuario(); 

     this.State.UserName = state.UserName ; 
     this.State.Password = state.Password ; 
     this.State.NombrePersona = state.NombrePersona ; 
    } 

    // Gets or sets state 
    public Usuario State 
    { 
     get { return UsuarioMemento; } 
    } 
} 

Und wir brauchen eine Klasse, die generiert und enthält unser Erinnerungsobjekt:

/// <summary> 
/// The 'Caretaker' class 
/// </summary> 
class Caretaker 
{ 
    private Memento _memento; 

    // Gets or sets memento 
    public Memento Memento 
    { 
     set { _memento = value; } 
     get { return _memento; } 
    } 

} 

Dann für Umsetzung dieser Muster wir haben eine Instanz Caretaker Klasse

Caretaker creadorMemento = new Caretaker(); 

erstellen und erstellen unser Memento-Objekt, wenn ein neuer Benutzer zum Bearbeiten ausgewählt wurde, zum Beispiel in selectedIndexChange nachdem der SelectedUser initialisiert wurde, verwende ich die Methode für das Ereignis RaisPropertyChanged wie folgt:

internal void RaisePropertyChanged(string prop) 
    { 
     if (PropertyChanged != null) { PropertyChanged(this, new PropertyChangedEventArgs(prop)); } 
     if (prop == "RowIndexSelected") // This is my property assigned to SelectedIndex property of my DataGrid 
     { 
      if ((this.UserSelected != null) && (creadorMemento .Memento != null)) 
      { 
       this.UserSelected.SetMemento(creadorMemento .Memento); 
      } 
     } 
     if (prop == "UserSelected") // Property UserSelected changed and if not is null we create the Memento Object 
     { 
      if (this.UserSelected != null) 
       creadorMemento .Memento = new Memento(this.UserSelected); 
     } 
    } 

Eine Erklärung für dieses, wenn selectedIndexChanged Wert, den wir ändern überprüfen, ob UserSelected und our memento object nicht null sind, bedeutet, dass unsere tatsächlichen Artikel im Bearbeitungsmodus dann geändert wir unser Objekt mit der Methode SetMemento wiederherstellen müssen. Und wenn sich unsere UserSelected Eigenschaft ändert und nicht null ist, erstellen wir unser Memento-Objekt, das wir verwenden werden, wenn die Bearbeitung abgebrochen wurde.

Für das Ende haben wir die SetMemento Methode in jeder Methode, die wir brauchen, um die Ausgabe abzubrechen, und wenn die Bearbeitung wie im SaveCommand commited hat, können wir null unser Memento-Objekt wie dieses this.creadorMemento = null setzen.

Verwandte Themen