微软应该为INotifyPropertyChanged实现一些时髦的东西,就像在自动属性中,只需要指定{get;设置;通知;} 我认为这样做很有意义。或者做这个手术有什么并发症吗?
我们能在属性中实现类似notify的东西吗。在你的类中实现INotifyPropertyChanged是否有一个优雅的解决方案,或者唯一的方法是在每个属性中引发PropertyChanged事件。
如果不是,我们可以写一些东西来自动生成一段代码来引发PropertyChanged事件?
微软应该为INotifyPropertyChanged实现一些时髦的东西,就像在自动属性中,只需要指定{get;设置;通知;} 我认为这样做很有意义。或者做这个手术有什么并发症吗?
我们能在属性中实现类似notify的东西吗。在你的类中实现INotifyPropertyChanged是否有一个优雅的解决方案,或者唯一的方法是在每个属性中引发PropertyChanged事件。
如果不是,我们可以写一些东西来自动生成一段代码来引发PropertyChanged事件?
当前回答
它是2022。现在有了官方的解决方案。
使用Microsoft MVVM工具包中的MVVM源生成器。
This
[ObservableProperty]
private string? name;
将生成:
private string? name;
public string? Name
{
get => name;
set
{
if (!EqualityComparer<string?>.Default.Equals(name, value))
{
OnNameChanging(value);
OnPropertyChanging();
name = value;
OnNameChanged(value);
OnPropertyChanged();
}
}
}
// Property changing / changed listener
partial void OnNameChanging(string? value);
partial void OnNameChanged(string? value);
protected void OnPropertyChanging([CallerMemberName] string? propertyName = null)
{
PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(propertyName));
}
protected void OnPropertyChanged([CallerMemberName] string? propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
它支持。net标准2.0和。net >= 5.0。
其他回答
从。net 4.5开始,终于有了一个简单的方法。
. net 4.5引入了一个新的调用者信息属性。
private void OnPropertyChanged<T>([CallerMemberName]string caller = null) {
// make sure only to call this if the value actually changes
var handler = PropertyChanged;
if (handler != null) {
handler(this, new PropertyChangedEventArgs(caller));
}
}
在函数中添加比较器可能也是一个好主意。
EqualityComparer<T>.Default.Equals
更多的例子在这里和这里
参见调用者信息(c#和Visual Basic)
Prism 5实现:
public abstract class BindableBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual bool SetProperty<T>(ref T storage,
T value,
[CallerMemberName] string propertyName = null)
{
if (object.Equals(storage, value)) return false;
storage = value;
this.OnPropertyChanged(propertyName);
return true;
}
protected void OnPropertyChanged(string propertyName)
{
var eventHandler = this.PropertyChanged;
if (eventHandler != null)
{
eventHandler(this, new PropertyChangedEventArgs(propertyName));
}
}
protected void OnPropertyChanged<T>(Expression<Func<T>> propertyExpression)
{
var propertyName = PropertySupport.ExtractPropertyName(propertyExpression);
this.OnPropertyChanged(propertyName);
}
}
public static class PropertySupport
{
public static string ExtractPropertyName<T>(Expression<Func<T>> propertyExpression)
{
if (propertyExpression == null)
{
throw new ArgumentNullException("propertyExpression");
}
var memberExpression = propertyExpression.Body as MemberExpression;
if (memberExpression == null)
{
throw new ArgumentException("The expression is not a member access expression.", "propertyExpression");
}
var property = memberExpression.Member as PropertyInfo;
if (property == null)
{
throw new ArgumentException("The member access expression does not access a property.", "propertyExpression");
}
var getMethod = property.GetMethod;
if (getMethod.IsStatic)
{
throw new ArgumentException("The referenced property is a static property.", "propertyExpression");
}
return memberExpression.Member.Name;
}
}
我在我的基本库中创建了一个扩展方法用于重用:
public static class INotifyPropertyChangedExtensions
{
public static bool SetPropertyAndNotify<T>(this INotifyPropertyChanged sender,
PropertyChangedEventHandler handler, ref T field, T value,
[CallerMemberName] string propertyName = "",
EqualityComparer<T> equalityComparer = null)
{
bool rtn = false;
var eqComp = equalityComparer ?? EqualityComparer<T>.Default;
if (!eqComp.Equals(field,value))
{
field = value;
rtn = true;
if (handler != null)
{
var args = new PropertyChangedEventArgs(propertyName);
handler(sender, args);
}
}
return rtn;
}
}
这适用于. net 4.5,因为有CallerMemberNameAttribute。 如果你想在一个早期的。net版本中使用它,你必须改变方法声明:…,[CallerMemberName] string propertyName = "",………,字符串propertyName…
用法:
public class Dog : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
string _name;
public string Name
{
get { return _name; }
set
{
this.SetPropertyAndNotify(PropertyChanged, ref _name, value);
}
}
}
这就是大规模的过度工程。这比仅仅以正确的方式进行要复杂得多,而且几乎没有任何好处。如果你的IDE支持代码片段(Visual Studio/MonoDevelop支持),那么你可以让这个实现变得非常简单。你需要输入的只是属性的类型和属性名。额外的三行代码将自动生成。
是的,更好的办法当然存在。 下面就是:
循序渐进的教程由我缩减,基于这篇有用的文章。
创建新项目 将城堡核心包安装到项目中
安装包的城堡。核心
只安装mvvm light库
安装包MvvmLightLibs
在项目中添加两个类:
NotifierInterceptor
public class NotifierInterceptor : IInterceptor
{
private PropertyChangedEventHandler handler;
public static Dictionary<String, PropertyChangedEventArgs> _cache =
new Dictionary<string, PropertyChangedEventArgs>();
public void Intercept(IInvocation invocation)
{
switch (invocation.Method.Name)
{
case "add_PropertyChanged":
handler = (PropertyChangedEventHandler)
Delegate.Combine(handler, (Delegate)invocation.Arguments[0]);
invocation.ReturnValue = handler;
break;
case "remove_PropertyChanged":
handler = (PropertyChangedEventHandler)
Delegate.Remove(handler, (Delegate)invocation.Arguments[0]);
invocation.ReturnValue = handler;
break;
default:
if (invocation.Method.Name.StartsWith("set_"))
{
invocation.Proceed();
if (handler != null)
{
var arg = retrievePropertyChangedArg(invocation.Method.Name);
handler(invocation.Proxy, arg);
}
}
else invocation.Proceed();
break;
}
}
private static PropertyChangedEventArgs retrievePropertyChangedArg(String methodName)
{
PropertyChangedEventArgs arg = null;
_cache.TryGetValue(methodName, out arg);
if (arg == null)
{
arg = new PropertyChangedEventArgs(methodName.Substring(4));
_cache.Add(methodName, arg);
}
return arg;
}
}
ProxyCreator
public class ProxyCreator
{
public static T MakeINotifyPropertyChanged<T>() where T : class, new()
{
var proxyGen = new ProxyGenerator();
var proxy = proxyGen.CreateClassProxy(
typeof(T),
new[] { typeof(INotifyPropertyChanged) },
ProxyGenerationOptions.Default,
new NotifierInterceptor()
);
return proxy as T;
}
}
创建你的视图模型,例如:
-
public class MainViewModel
{
public virtual string MainTextBox { get; set; }
public RelayCommand TestActionCommand
{
get { return new RelayCommand(TestAction); }
}
public void TestAction()
{
Trace.WriteLine(MainTextBox);
}
}
将绑定放入xaml: <TextBox Text="{绑定MainTextBox}" ></TextBox> . <Button Command="{Binding TestActionCommand}" >Test</Button> . 在代码隐藏文件MainWindow.xaml.cs中放入如下代码行:
DataContext = ProxyCreator.MakeINotifyPropertyChanged<MainViewModel>();
享受。
注意! !所有有界属性都应该用 关键字virtual,因为它们被城堡代理用于重写。