从Model中更新ViewModel中属性的正确方法

时间:2016-12-27 15:04:47

标签: wpf mvvm model notifications viewmodel

我是WPF的新手。我的理解是模型中的数据发生了变化,它应该通知viewmodel,视图将​​绑定到viewmodel中的属性和类似的东西。它是否正确?如果是这样,我一直在读这个模型应该实现INotifyPropertyChanged,看起来像这样

 public class LoginModel : INotifyPropertyChanged
{

    public event PropertyChangedEventHandler PropertyChanged;

    public void NotifyPropertyChanged(string propName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
    }

    public bool Authenticated { get; set; }
}

在我的ViewModel中,我有一个属性“AuthResult”,应该从Model属性“Authenticated”获取更新

public partial class view1 : UserControl, INotifyPropertyChanged{
 public bool AuthResult
    {
        get
        {
            return _authVal;
        }
        set
        {
            _authVal = value;
            NotifyPropertyChanged("AuthResult");
        }
    }

public event PropertyChangedEventHandler PropertyChanged;

    public void NotifyPropertyChanged(string propName)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propName));
    }
}

我知道当前的实现是不正确的。我发现我应该从我的模型中订阅PropertyChanged通知,如下所示:

    LoginModel.PropertyChanged += new System.ComponentModel.PropertyChangedEventHandler(LoginModel_PropertyChanged);

void LoginModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
{
    if(e.PropertyName == "Authenticated")
    {
         //do something
    }
}

我没有看到应该更新“AuthResult”属性的位置。我会在If AuthResult = _model.Authenticated;中的If语句中做些什么吗?

编辑:

并在我的构造函数中?

LoginModel _model;

        public view1(LoginModel model)
        {
            _model = model;
            InitializeComponent();           
        }

2 个答案:

答案 0 :(得分:3)

如果模型实现了INotifyPropertyChanged接口,您可以从视图直接绑定到它:

<Button Content="Button" IsEnabled="{Binding Authenticated}" />

请注意,只要Authenticated属性设置为新值,LoginModel类就必须引发PropertyChanged事件。

您还可以通过视图模型类公开整个模型实体:

public class ViewModel
{
    public ViewModel(LoginModel model)
    {
        Model = model;
    }

    public LoginModel Model { get; }
}

...并像这样绑定它:

<Button Content="Button" IsEnabled="{Binding Model.Authenticated}" />

仍然是模型类必须实现INotifyPropertyChanged接口并引发更改通知。

另一个选项是视图模型从视图中包装您希望能够绑定到的模型类的任何属性。然后绑定到视图模型类的属性,该属性又包装模型类的属性,如下所示:

public class ViewModel
{
    private readonly LoginModel _model;
    public ViewModel(LoginModel model)
    {
        _model = model;
    }

    public bool AuthResult
    {
        get
        {
            return _model.Authenticated;
        }
        set
        {
            _model.Authenticated = value;
            NotifyPropertyChanged("AuthResult");
        }
    }
}

<Button Content="Button" IsEnabled="{Binding AuthResult}" />

使用后一种方法的好处是视图不依赖于模型类。它仅绑定到视图模型类,这就是MVVM设计模式通常要实现的方式。

但是如果你绑定到视图模型的(包装器)属性并且希望每当设置模型类的属性时更新视图,模型必须通知视图模型它已经改变了一种方式或者另一个,即它必须提出某种事件或类似事件。这通常意味着实现INotifyPropertyChanged接口。然后,视图模型可以订阅模型的PropertyChanged事件,并在模型更新时为数据绑定属性引发自己的PropertyChanged事件,例如:

public class ViewModel
{
    private readonly LoginModel _model;

    public ViewModel(LoginModel model)
    {
        if (model == null)
            throw new ArgumentNullException("model");

        _model = model;
        _model.PropertyChanged += OnModelChanged;
    }

    private void OnModelChanged(object sender, PropertyChangedEventArgs e)
    {
        if (e.PropertyName == "Authenticated")
            NotifyPropertyChanged("AuthResult");
    }

    public bool AuthResult
    {
        get
        {
            return _model.Authenticated;
        }
        set
        {
            _model.Authenticated = value;
            NotifyPropertyChanged("AuthResult");
        }
    }
}

答案 1 :(得分:1)

只需在ViewModel中使用Model作为成员

public class ViewModel
{
    private Model _myModel;
    public Model MyModel 
    {
        get { return _myModel; }
        set
        {
            if (Equals(_myModel, value)) return;
            _myModel = value;
            NotifyPropertyChanged(nameof(MyModel));
        }
    }
}

然后在xaml中,您可以绑定Model

的属性
<CheckBox IsChecked="{Binding MyModel.Authenticated}" />

使用这种方法,您的ViewModel将围绕您的模型“构建” 如果您不希望模型实现INotifyPropertyChanged而不是创建一个“Facade”类模型,请使用它与上一个示例相同。

public class ModelFacade : INotifyPropertyChanged
{
    private Model _myModel;

    public bool Authenticated
    {
        get { return _myModel.Authenticated; }
        set
        {
            _myModel.Authenticated = value;
            NotifyPropertyChanged(nameof(Authenticated));
        }
    }
}

public class ViewModel
{
    private ModelFacade _myModel;
    public ModelFacade MyModel 
    {
        get { return _myModel; }
        set
        {
            if (Equals(_myModel, value)) return;
            _myModel = value;
            NotifyPropertyChanged(nameof(MyModel));
        }
    }
}