如何使用MVVM取消对对象的编辑?

时间:2009-07-07 08:32:27

标签: wpf mvvm

如何使用MVVM实现取消编辑对象。

例如:我有一个客户列表。我选择一个客户,单击“编辑”按钮,打开一个对话框窗口(DataContext绑定到CustomerViewModel),然后我开始编辑客户的字段。然后我决定取消编辑,但客户的字段已经更改,那么如何将客户返回到MVVM中的先前状态?

7 个答案:

答案 0 :(得分:12)

查看IEditableObject界面。您的Customer类应该实现这一点,并且您的命令可以根据需要执行BeginEdit / CancelEdit / EndEdit

答案 1 :(得分:3)

this article中,Raul只是从数据库重新加载对象。我想这比肯特提出的解决方案要少。

    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);
    }

答案 2 :(得分:3)

一种非常简单的方法,如果您的对象已经可序列化,例如您使用的是WCF。您可以将原始对象序列化为内部字段。如果您的对象不可序列化,那么只需使用AutoMapper使用一行代码创建对象的副本。

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

当您处理CancelCommand时,只需反向调用AutoMapper。由于您的属性已经有更改通知,因此一切正常。如果您需要并且想要编写额外的代码,可以将这些技术与IEditableObject结合使用。

答案 3 :(得分:2)

您可以使用UpdateSourceTrigger = Explicit绑定。 Here您可以找到有关如何实施此信息的更多信息。

答案 4 :(得分:1)

我也有这个问题。我用“Memento Pattern Design”解决了这个问题。使用此模式,您可以轻松保存原始对象的副本,并且在selectedIndexChange(控件的)或“取消”按钮中,您可以轻松恢复对象的先前版本。

How is the Memento Pattern implemented in C#4?

提供了使用此模式的示例

代码示例:

如果我们有一个具有UserName Password和NombrePersona属性的用户,我们需要添加方法CreateMemento和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;
    }

然后,我们需要一个Memento类,它将包含我们对象的“副本”,如下所示:

 /// <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; }
    }
}

我们需要一个能够生成并包含memento对象的类:

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

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

}

然后为了实现这个模式,我们必须创建一个Caretaker

的实例
Caretaker creadorMemento = new Caretaker();

在选择新用户进行编辑时创建我们的memento对象,例如在SelectedUser初始化后的selectedIndexChange中,我使用事件RaisPropertyChanged的方法,如下所示:

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);
        }
    }

对此进行说明,当selectedIndexChanged更改值时,我们检查UserSelectedour memento object是否为空意味着我们在编辑模式下的实际项目已更改,那么我们必须还原我们的对象使用方法SetMemento。 如果我们的UserSelected属性发生更改且不为null,我们将“创建我们的Memento对象”,我们将在取消编辑时使用该对象。

为了完成,我们在取消版本所需的每个方法中都使用了SetMemento方法,当编辑提交时,就像在SaveCommand中一样,我们可以设置null我们的memento对象,如{{1} }。

答案 5 :(得分:0)

您还可以在ViewModel中将模型的状态复制到内部字段,然后在用户实际提交更改时公开它们,然后仅在模型上设置它们。

问题可能是,如果验证依赖于正在更新的实体,那么即时验证将更加麻烦 - 如果这是一个要求,您可以创建要处理的模型的克隆,然后将克隆与保存时的实际实体。

答案 6 :(得分:0)

基于Камен Великов's answer

您可以通过定义

将绑定标记为手动更新

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

在您的视图中(XAML)。然后,您必须通过调用

在ViewModel中从UI编写更改

yourTextBox.GetBindingExpression(TextBox.TextProperty).UpdateSource();
单击“保存”时。

请注意,如果更新了从其他任何内容触发的绑定源,它们仍会直接显示在UI中。