允许用户仅在数据更改时保存的更好方法
本文关键字:保存 方法 更好 数据 许用户 用户 | 更新日期: 2023-09-27 18:18:28
我之前所做的是对数据对象进行深度复制,然后编写一个通用的比较方法,该方法使用反射器比较两个对象之间是否存在差异。
假设我有一个SaveButton,一个TextBoxA和ViewModel绑定。PropertyA, initial PropertyA is = "123".
当用户在TextBoxA中输入"1234"时,PropertyA set方法将执行比较方法来查找差异。并启用保存按钮。
但是当用户将文字"1234"改回"123"时,保存按钮将再次失效。
1年后,现在我想知道是否有更好或更简单的方法来做到这一点?也就是说,有什么框架可以做这种事情吗?我不需要写代码来深度复制对象,自己写比较方法?
我拥有的实际UI并不是那么简单,只包含TextBox类型,这是一个用于编辑客户信息的UI,因此有DateTime, Collection等。这就是为什么我写了深度复制方法来克隆整个对象。
假设您的视图模型上的这些属性正在以某种方式引发PropertyChanged
事件,因为问题被标记为MVVM
。
这里有一个方法。为ViewModel的PropertyChanged
事件编写一个事件处理程序。只有当属性发生变化时,才将原始值保存在私有Dictionary<string, string>
中。这就避免了为了防止有人进行编辑而复制整个对象的需要。如果属性已经存在于字典中,那么您可以很容易地确定它是否被更改回其原始值。
编辑:哦,我认为PropertyChangedEventArgs
包含新的和旧的值,但它没有。因此,为了做到这一点,你需要在视图模型的属性设置器中添加一些额外的方法调用,可以计算每个属性的旧值和新值。
为了方便地设置启用和禁用保存按钮,在视图模型中应该有一个bool
属性,您可以将保存按钮的启用属性绑定到该属性。
如果每当新值与原始值匹配时,项就从字典中删除,那么如果字典包含任何项,则Save按钮启用属性可以返回true。
编辑2:对于集合类型,您希望将视图绑定到视图模型上的ObservableCollection
属性。Collection changed事件确实为您提供了一个旧项和新项的列表,因此在该事件处理程序中跟踪更改应该相当容易。
如果ViewModel是你自己的对象,你可以修改它,实现iclonable接口,这样你就可以复制它。
接下来在其上实现IComparable接口,其中T是视图模型。所以比较
很容易那么我想你必须为所有属性创建一个PropertyChanged
事件,当一个被触发时进行比较。
我猜它与你现在已经拥有的基本相同,但如果你基于ICloneable
和IComparable
编写逻辑,至少你只需要编写一次
听起来您希望将状态管理与属性更改通知相结合。状态管理实际上取决于您想要如何做。少数几个有意义的概念是使用对象的备份副本或将原始属性(属性名)映射到底层字段(属性值)的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标志并设置窗体的适当状态。