我之前做的是制作数据对象的深层副本,然后编写一个通用的比较方法,使用反射器比较两个对象之间是否存在差异。
所以说如果我有一个SaveButton,一个用ViewModel.PropertyA绑定的TextBoxA,则初始PropertyA是=“123”。
当用户在TextBoxA中键入“1234”时,PropertyA set方法将执行compare方法以查找差异。并启用“保存”按钮。
但是当用户将文本“1234”更改回“123”时,“保存”按钮将再次禁用。
1年后,现在我想知道有更好的方法或更简单的方法吗? 即是否有任何框架可以做这种事情?所以我没有深度复制对象的编写代码,自己编写比较方法吗?
我所拥有的实际用户界面并非如此简单,只包含TextBox类型,即用于编辑客户信息的UI,因此具有DateTime,Collection等。这就是为什么我编写深度复制方法来克隆整个对象的原因。
答案 0 :(得分:0)
假设视图模型上的这些属性以某种方式引发PropertyChanged
事件,因为该问题已标记为MVVM
。
这是一种方法。为ViewModel的PropertyChanged
事件编写一个事件处理程序。仅在属性更改时才将原始值保存在私有Dictionary<string, string>
中。这样就可以防止复制整个对象,以防有人进行编辑。如果该属性已存在于字典中,那么您可以轻松确定它是否已更改回其原始值。
编辑:哦,我在想PropertyChangedEventArgs
包含新旧值,但事实并非如此。因此,为了做到这一点,您需要在View Model的属性设置器中添加一些额外的方法调用,以便评估每个属性的旧值和新值。
为了轻松设置启用和禁用“保存”按钮,视图模型中应该有一个bool
属性,您可以将该按钮的启用属性绑定到该属性。
如果新值与原始值匹配,则会从字典中删除项目,如果字典包含任何项目,则“启用保存”按钮属性可以返回true。
编辑2 :对于集合类型,您希望将View绑定到View模型上的ObservableCollection
属性。 Collection更改事件确实为您提供了旧项目和新项目的列表,因此跟踪该事件处理程序中的更改应该相当容易。
答案 1 :(得分:0)
如果ViewModel是您自己的对象并且您可以修改它,请实现ICloneable接口,以便您可以复制它。
接下来在其上实现IComparable接口,其中T是视图模型。所以很容易比较
然后我想你必须为所有属性做一个PropertyChanged
事件,当一个被触发时进行比较。
我猜它与你现在拥有的几乎相同,但是如果你基于ICloneable
和IComparable
编写逻辑,至少你只需要写一次
编辑:如果您只是不想编写自己的比较方法,则会有自动比较所有属性的片段,例如this post。但是,使用类似的东西比编写自己的比较函数慢得多(性能明智)。
答案 2 :(得分:0)
听起来您希望状态管理与财产变更通知相结合。国家管理层真的由您自己决定如何做到这一点。有意义的几个概念是使用对象的备份副本或将原始属性(属性名称)映射到基础字段(属性值)的Dictionary<string, object>
。
至于确定是否有任何更改,我会使用INotifyPropertyChanged接口。这将使状态管理和通知保持在类内部。只需实现一个名为OnPropertyChanged(字符串propName,对象propValue)的包装器(良好实践),它设置一个布尔数组/字典(Dict<string, bool>
),然后设置是否有任何更改,如果有任何属性,HasChanges属性返回true改变了。示例类:
public class TestClass : INotifyPropertyChanged
{
private Dictionary<string, object> BackingStore = new Dictionary<string,object>();
private Dictionary<string, bool> Changes = new Dictionary<string, bool>();
private string _testString;
public string TestString
{
get { return _testString; }
set { _testString = value; OnPropertyChanged("TestString", value); }
}
private bool HasChanges { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
public TestClass(string value)
{
_testString = value;
SaveValues();
}
public void SaveValues()
{
// Expensive, to use reflection, especially if LOTS of objects are going to be used.
// You can use straight properties here if you want, this is just the lazy mans way.
this.GetType().GetProperties().ToList().ForEach(tProp => { BackingStore[tProp.Name] = tProp.GetValue(this, null); Changes[tProp.Name] = false; });
HasChanges = false;
}
public void RevertValues()
{
// Again, you can use straight properties here if you want. Since this is using Property setters, will take care of Changes dictionary.
this.GetType().GetProperties().ToList().ForEach(tProp => tProp.SetValue(this, BackingStore[tProp.Name], null));
HasChanges = false;
}
private void OnPropertyChanged(string propName, object propValue)
{
// If you have any object types, make sure Equals is properly defined to check for correct uniqueness.
Changes[propName] = BackingStore[propName].Equals(propValue);
HasChanges = Changes.Values.Any(tr => tr);
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propName));
}
}
为简单起见,我只使用SaveValues / RevertValues来保存/撤消更改。但是,这些可以很容易地用于实现IEditableObject
接口(BeginEdit,CancelEdit,EndEdit)。然后可以通过绑定对象的任何形式订阅PropertyChanged事件(甚至可以订阅基础BindingList,这样只需要订阅一个实例),它会检查HasChanges标志并设置相应的状态。形式。