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