c# mvvm IEqualityComparer 与 IChangeTracking

c# mvvm IEqualityComparer with IChangeTracking

我正在努力实现以下目标: 我有一个表单,用户必须输入姓名、姓氏、地址……以及保存更改按钮。 我想让用户仅在他确实进行了任何更改时才单击 保存更改 按钮。我发现通过简单地使用 IChangeTracking 它只跟踪用户是否对工具进行了 any 更改,但是他是否应该将更改还原回来,这不再反映。因此,每当我看到已进行更改时,我都会触发 IEqualityComparer;但是没有运气,因为它给了我以下错误:

An unhandled exception of type 'System.WhosebugException' occurred in Unknown Module.

这是我的简单 class 人:

public class Person : ViewModelBase
{
    private string _Name;
    public string Name
    {
        get { return _Name; }
        set
        {
            _Name = value;
            OnPropertyChanged("Name");
        }
    }

    private string _Surname;
    public string Surname
    {
        get { return _Surname; }
        set
        {
            _Surname = value;
            OnPropertyChanged("Surname");
        }
    }
}

这是我的 ViewModelBase 结构:

public abstract class ViewModelBase : INotifyPropertyChanged, IChangeTracking, IEqualityComparer<Person>
{
    public event Action ValueChanged;
    protected ViewModelBase()
    {
        this.PropertyChanged = new PropertyChangedEventHandler(OnNotifiedOfPropertyChanged);
        ValueChanged += ViewModelBase_ValueChanged;
    }

    void ViewModelBase_ValueChanged() {/*do nothing*/ }

    public bool Equals(Person x, Person y)
    {
        if (x == null || y == null)
            return false;

        return (x.Name == y.Name && x.Surname == y.Surname);
    }

    public int GetHashCode(Person obj)
    {
        return obj.GetHashCode();
    }

    private void OnNotifiedOfPropertyChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e != null && !String.Equals(e.PropertyName, "IsChanged", StringComparison.Ordinal))
        {
            this.IsChanged = true;
        }
    }

    public void AcceptChanges()
    {
        this.IsChanged = false;
    }

    public bool IsChanged
    {
        get
        {
            lock (_notifyingObjectIsChangedSyncRoot)
            {
                return _notifyingObjectIsChanged;
            }
        }

        set
        {
            lock (_notifyingObjectIsChangedSyncRoot)
            {
                    _notifyingObjectIsChanged = value;

                    this.OnPropertyChanged("IsChanged");

                    this.ValueChanged.Invoke();
            }
        }
    }

    private bool _notifyingObjectIsChanged;
    private readonly object _notifyingObjectIsChangedSyncRoot = new Object();

    public event PropertyChangedEventHandler PropertyChanged;
    protected void OnPropertyChanged(string name)
    {
        PropertyChangedEventHandler handler = PropertyChanged;
        if (handler != null)
        {
            handler(this, new PropertyChangedEventArgs(name));//<<<<<Mentioned Error occures here
        }
    }
}

最后是我设置 ViewModel 的方式:

public class MainViewModel
{
    public Person osoba { get; set; }
    public Person OldOsoba { get; set; }
    public MainViewModel()
    {
        osoba = new Person();
        osoba.Name = "John";
        osoba.Surname = "Doe";
        osoba.AcceptChanges();

        OldOsoba = new Person();
        OldOsoba.Name = "John";
        OldOsoba.Surname = "Doe";
        OldOsoba.AcceptChanges();

        osoba.ValueChanged += osoba_ValueChanged;
    }

    void osoba_ValueChanged()
    {
        osoba.IsChanged = osoba.Equals(OldOsoba);
    }
}

我愿意接受任何建议,因为我不太确定这是否是正确的方法。

这是一个替代方案:

在您的模型中创建一个 IsDirty 属性,如果 Name 或 Surname 属性更改,它将被设置为 true:

public class Person : ViewModelBase
{
    public string OldName { get; set; }
    public string OldSurname { get; set; }

    private string _Name;
    public string Name
    {
        get { return _Name; }
        set
        {
            _Name = value;
            OnPropertyChanged("Name");
            OnPropertyChanged("IsDirty");
        }
    }

    private string _Surname;
    public string Surname
    {
        get { return _Surname; }
        set
        {
            _Surname = value;
            OnPropertyChanged("Surname");
            OnPropertyChanged("IsDirty");
        }
    }

    public bool IsDirty
    {
        get 
        { 
           return this.Name != this.OldName && this.Surname != this.OldSurname; 
        }
    }
}

注意:为 IsDirty 属性 使用接口可能是个好主意,甚至是基础 class.

然后,您可以为您的 ViewModel 执行一个命令,该命令将测试列表中的任何人是否已更改:

public class SaveChangesCommand : ICommand
{
    private MainViewModel _viewModel;

    public SaveChangesCommand(MainViewModel viewModel)
    {
        _viewModel = viewModel;
    }

    public bool CanExecute(object parameter)
    {
        //People should be an ObservableCollection<Person> in your view model.
        return _viewModel.People.Any(x => x.IsDirty);
    }

    public event EventHandler CanExecuteChanged
    {
        add { CommandManager.RequerySuggested += value; }
        remove { CommandManager.RequerySuggested -= value; }
    }

    public void Execute(object parameter)
    {
        _viewModel.SaveChanges();
    }
}

在您的视图模型中创建此命令的实例:

public SaveChangesCommand SaveChangesCommand { get; set; }

别忘了实例化它(在你的构造函数中会是个好地方)

并在您的视图中绑定一个按钮:

<Button Command="{Binding SaveChangesCommand}" Content="Save Changes"/>

现在,有了这一切,当您的任何模型变脏时,将启用保存更改按钮。单击后,它将在您的视图模型中调用 SaveChanges 方法。在这里您将需要实现保存更改实现并将所有模型重置为默认状态 (IsDirty = false)

我建议完成 MVVM 教程 here