只有在数据发生变化时才允许用户保存的更好方法?

时间:2011-12-21 20:01:14

标签: c# wpf silverlight mvvm

我之前做的是制作数据对象的深层副本,然后编写一个通用的比较方法,使用反射器比较两个对象之间是否存在差异。

所以说如果我有一个SaveButton,一个用ViewModel.PropertyA绑定的TextBoxA,则初始PropertyA是=“123”。

当用户在TextBoxA中键入“1234”时,PropertyA set方法将执行compare方法以查找差异。并启用“保存”按钮。

但是当用户将文本“1234”更改回“123”时,“保存”按钮将再次禁用。

1年后,现在我想知道有更好的方法或更简单的方法吗? 即是否有任何框架可以做这种事情?所以我没有深度复制对象的编写代码,自己编写比较方法吗?


我所拥有的实际用户界面并非如此简单,只包含TextBox类型,即用于编辑客户信息的UI,因此具有DateTime,Collection等。这就是为什么我编写深度复制方法来克隆整个对象的原因。

3 个答案:

答案 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事件,当一个被触发时进行比较。

我猜它与你现在拥有的几乎相同,但是如果你基于ICloneableIComparable编写逻辑,至少你只需要写一次

编辑:如果您只是不想编写自己的比较方法,则会有自动比较所有属性的片段,例如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标志并设置相应的状态。形式。