从模型更新 ViewModel 中 属性 的正确方法
Correct way to update property in ViewModel from Model
我是 WPF 的新手。据我了解,模型中的数据发生变化,它应该通知视图模型,视图将绑定到视图模型中的属性和类似的东西。这个对吗?如果是这样,我一直在阅读该模型应该实现 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",应该从模型 属性 "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;
?
已编辑:
在我的构造函数中?
LoginModel _model;
public view1(LoginModel model)
{
_model = model;
InitializeComponent();
}
只需使用 Model 作为 ViewModel 中的成员
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 将 "build" 围绕您的模型。
如果您不希望该模型实现 INotifyPropertyChanged
而不是创建一个 "Facade" class 模型,请按照与前面示例相同的方式使用它。
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));
}
}
}
如果模型实现了 INotifyPropertyChanged 接口,您可以直接从视图绑定到它:
<Button Content="Button" IsEnabled="{Binding Authenticated}" />
请注意,每当将 Authenticated 属性 设置为新值时,LoginModel class 必须引发 PropertyChanged 事件。
您还可以通过视图模型公开整个模型实体class:
public class ViewModel
{
public ViewModel(LoginModel model)
{
Model = model;
}
public LoginModel Model { get; }
}
...并像这样绑定到它:
<Button Content="Button" IsEnabled="{Binding Model.Authenticated}" />
仍然是模型 class 必须实现 INotifyPropertyChanged 接口并引发更改通知。
视图模型的另一个选项是包装您希望能够从视图绑定到的模型 class 的任何 属性。然后你绑定到视图模型 class 的 属性 ,它又包装了模型 class 的 属性 ,如下所示:
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}" />
使用后一种方法的好处是视图不依赖于模型 class。它仅绑定到视图模型 class,这就是 MVVM 设计模式通常的实现方式。
但是,如果您确实绑定到视图模型的(包装器)属性,并且希望在设置模型 class 的 属性 时更新视图,则模型必须通知视图模型它已经以某种方式更改,即它必须引发某种事件或类似事件。这通常意味着实现 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");
}
}
}
我是 WPF 的新手。据我了解,模型中的数据发生变化,它应该通知视图模型,视图将绑定到视图模型中的属性和类似的东西。这个对吗?如果是这样,我一直在阅读该模型应该实现 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",应该从模型 属性 "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;
?
已编辑:
在我的构造函数中?
LoginModel _model;
public view1(LoginModel model)
{
_model = model;
InitializeComponent();
}
只需使用 Model 作为 ViewModel 中的成员
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 将 "build" 围绕您的模型。
如果您不希望该模型实现 INotifyPropertyChanged
而不是创建一个 "Facade" class 模型,请按照与前面示例相同的方式使用它。
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));
}
}
}
如果模型实现了 INotifyPropertyChanged 接口,您可以直接从视图绑定到它:
<Button Content="Button" IsEnabled="{Binding Authenticated}" />
请注意,每当将 Authenticated 属性 设置为新值时,LoginModel class 必须引发 PropertyChanged 事件。
您还可以通过视图模型公开整个模型实体class:
public class ViewModel
{
public ViewModel(LoginModel model)
{
Model = model;
}
public LoginModel Model { get; }
}
...并像这样绑定到它:
<Button Content="Button" IsEnabled="{Binding Model.Authenticated}" />
仍然是模型 class 必须实现 INotifyPropertyChanged 接口并引发更改通知。
视图模型的另一个选项是包装您希望能够从视图绑定到的模型 class 的任何 属性。然后你绑定到视图模型 class 的 属性 ,它又包装了模型 class 的 属性 ,如下所示:
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}" />
使用后一种方法的好处是视图不依赖于模型 class。它仅绑定到视图模型 class,这就是 MVVM 设计模式通常的实现方式。
但是,如果您确实绑定到视图模型的(包装器)属性,并且希望在设置模型 class 的 属性 时更新视图,则模型必须通知视图模型它已经以某种方式更改,即它必须引发某种事件或类似事件。这通常意味着实现 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");
}
}
}