c# mvvm IEqualityComparer with IChangeTracking

标签 c# wpf mvvm equality change-tracking

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

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



这是我的简单类Person:
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);
    }
}

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

最佳答案

这是一个替代方案:

在您的模型中创建 是脏的属性,如果 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 属性甚至基类使用接口(interface)可能是个好主意。

然后,您可以为您的 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();
    }
}

在您的 View 模型中创建此命令的实例:
public SaveChangesCommand SaveChangesCommand { get; set; }

不要忘记实例化它(在您的构造函数中将是一个好地方)

并在您的 View 中绑定(bind)一个按钮:
<Button Command="{Binding SaveChangesCommand}" Content="Save Changes"/>

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

我建议通过 MVVM 教程 here .

关于c# mvvm IEqualityComparer with IChangeTracking,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27921844/

相关文章:

c# - 如何计算 C# 中字符串传递的函数

c# - 根据导入的十进制值应用颜色值

c# - 用C#制作一个简单的数据库程序

c# - 使用 DataService 的性能

c# - 模型列表<T> 和 ViewModel ObservableCollection<T> 重复数据?

c#通过字符串访问对象实例

wpf - 在代码wpf中访问资源字典

c# - 在 WPF 中访问 Window 类中的 Application 对象?

c# - 覆盖 WPF 控件

wpf - 测试错误和 “other”值