我按照许多线程的建议实现了INotifyPropertyChanged
。
实施 1
public class Notifier : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string pName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(pName));
}
}
public class Model : Notifier, IDataErrorInfo
{
private string name;
public string Name
{
get { return name; }
set { name = value; OnPropertyChanged("name_changed"); }
}
}
视图模型由模型和用于更改模型属性的命令组成。
public class ViewModel : Notifier
{
private Model _model;
public Model Model
{
get { return _model; }
set { _model = value; OnPropertyChanged("model_changed"); }
}
private ICommand _cmd;
public ICommand Command
{
get { return _cmd; }
set { _cmd = value; }
}
public void ExecuteCommand(object para)
{
Console.WriteLine("Command executed");
Model.Name = "new name";
}
}
然后将 VM 绑定到视图。
<TextBox HorizontalAlignment="Center" VerticalAlignment="Center" Width="100">
<TextBox.Text>
<Binding Path="Model.Name" Mode="TwoWay" NotifyOnValidationError="True" UpdateSourceTrigger="PropertyChanged" ValidatesOnDataErrors="True">
<Binding.ValidationRules>
<ExceptionValidationRule></ExceptionValidationRule>
</Binding.ValidationRules>
</Binding>
</TextBox.Text>
</TextBox>
执行命令时,TextBox
不会更新为新值。
但是,如果我像此指令一样实现INotifyPropertyChanged
,则绑定有效。
实施 2
public class Notifier : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool SetProperty<T>(ref T field, T newValue, [CallerMemberName]string propertyName = null)
{
if (!EqualityComparer<T>.Default.Equals(field, newValue))
{
field = newValue;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
return true;
}
return false;
}
}
public class Model : Notifier, IDataErrorInfo
{
private string name;
public string Name
{
get { return name; }
set { SetProperty(ref name, value); }
}
}
第一种方法遗漏了什么?
实现 1 的主要问题是 OnPropertyChanged
方法的字符串参数需要是正在更改的确切属性名称。对于您的两个示例,"model_changed"
应更改为"Model"
"name_changed"
应该读"Name"
.以下是两种很好的技术,可以减少键入文本字符串名称时的潜在人为错误:
1. 使用 CallerMemberName
属性
如果允许并有权访问 System.Runtime.CompilerServices
命名空间,则可以这样编写基类,以便将属性名称作为 OnPropertyChanged
方法的字符串参数自动传递:
using System.ComponentModel;
using System.Runtime.CompilerServices;
public class Notifier : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged([CallerMemberName] string pName = "")
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(pName));
}
}
然后,您只需在财产的getter中调用OnPropertyChanged()
即可。
2. 使用 nameof
关键字
或者,您可以简单地将文本类型化属性名称替换为 nameof(<InsertPropertyNameHere>)
这将返回该名称,而不会有任何错误键入的风险,如以下示例所示:
public class Model : Notifier, IDataErrorInfo
{
private string name;
public string Name
{
get { return name; }
set { name = value; OnPropertyChanged(nameof(Name)); }
}
}
请像这样添加属性名称。
public class Model : Notifier, IDataErrorInfo
{
private string name;
public string Name
{
get { return name; }
set { name = value; OnPropertyChanged("Name"); }
}
}