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

Wpf 如何使用MVVM取消对对象的编辑?,wpf,mvvm,Wpf,Mvvm,如何使用MVVM实现取消编辑对象 例如:我有一份客户名单。我选择一个客户,然后单击“编辑”按钮,一个对话框窗口(DataContext绑定到CustomerServiceWModel)打开,我开始编辑客户字段。然后我决定取消编辑,但客户的字段已经更改,因此如何将客户恢复到MVVM中以前的状态?查看界面。您的Customer类应该实现这一点,您的命令可以根据需要执行BeginEdit/CancelEdit/EndEdit。在中,Raul只需从数据库重新加载对象。我想这比肯特提出的解决方案麻烦少

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

例如:我有一份客户名单。我选择一个客户,然后单击“编辑”按钮,一个对话框窗口(DataContext绑定到CustomerServiceWModel)打开,我开始编辑客户字段。然后我决定取消编辑,但客户的字段已经更改,因此如何将客户恢复到MVVM中以前的状态?

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

在中,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);
    }

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


问题可能是,如果验证依赖于正在更新的实体,则动态验证将更加麻烦-如果这是一项要求,则您可以创建模型的克隆以进行工作,然后在保存克隆时将其与实际实体合并。

如果您的对象已可序列化,则一种超级简单的方法,例如,如果您正在使用WCF。可以将原始对象序列化到内部字段中。如果对象不可序列化,则只需使用一行代码创建对象的副本

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

当您处理cancel命令时,只需反向调用AutoMapper。由于您的属性已经有更改通知,所以一切都正常工作。如果您需要并希望编写额外的代码,您可以将这些技术与IEditableObject结合使用。

您可以将绑定与
UpdateSourceTrigger=Explicit
结合使用。您可以找到如何实现此功能的更多信息。

基于:

您可以通过定义

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

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


请注意,如果有其他触发的绑定源更新,它们仍然直接显示在UI中。

我也有这个问题。我用“纪念图案设计”解决了这个问题。使用此模式,您可以轻松保存原始对象的副本,并且在
选择的索引更改
(控件)或“取消”按钮中,您可以轻松恢复对象的早期版本

有关此模式的使用示例,请访问

代码示例:

如果类用户的属性为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; }
    }
}
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);
        }
    }
并在选择新用户进行编辑时创建我们的memento对象,例如在SelectedUser初始化后的
selectedIndexChange
中,我使用event
RaisPropertyChanged
的方法如下:

 /// <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; }
    }
}
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
更改值时,我们检查
UserSelected
我们的memento对象是否不为空,这意味着我们在编辑模式下的实际项目已更改,然后我们必须使用
SetMemento
方法恢复我们的对象。 如果我们的
UserSelected
属性更改且不为空,我们将“创建我们的Memento对象”,在取消编辑时使用该对象


对于finish,我们在需要取消版本的每个方法中都使用了
SetMemento
方法,当编辑像在SaveCommand中一样提交时,我们可以像这样设置null我们的memento对象
this.creadorMemento=null

我认为从DB重新加载是履行Boogaarts先生建议的IEditableObject义务的一种方式,不一定是同一个对象的替代品。IEditableObject为对象创建了大量开销,特别是如果模型对象是类而不是结构,则必须重写模型对象才能支持它。@Agies:为什么要否决?IEditableObject是否是“大量开销”完全取决于您的基础架构或您想要如何实现它。它只是WPF理解的一个接口。如何实现它取决于您。+1,是的,我想用IEditableObject实现它,但我有一个ViewModelBase,它公开了一个类型为TModel的属性模型,我将视图直接绑定到公开模型的属性。现在我怎么还能使用“Cancel Edit”,同样,既然您知道我的场景,就说我的TModel是一个地址实体。在查看模式下,它只是绑定到FullAddress行并使用AddressDataTemplate(创建到GMaps的链接),但我希望当用户单击AddressView上的Edit按钮时,它应该打开一个ChildWindow(SL或WPF中的任何窗口)继续……继续:这个子窗口应该显示编辑地址字段的文本框,它应该有一个保存和取消按钮,这样我可以取消编辑,现在基本上是两个问题,1)OPs问题;如何将实体的更改字段取消为原始字段2)如何在编辑开始时调用编辑窗口以从AddressViewModel弹出?请参考一个好的链接。AutoMapper的缺点是它只捕获通过属性反映的状态。因此,与其他假设每个可编辑类的每一个状态都通过读/写属性公开的解决方案一样,这对于使用DTO或其他类似贫乏对象的场景应该可以很好地工作,但在存在丰富域对象的情况下,可能根本就没有属性。二进制序列化和NetDataCont