微软应该为INotifyPropertyChanged实现一些时髦的东西,就像在自动属性中,只需要指定{get;设置;通知;} 我认为这样做很有意义。或者做这个手术有什么并发症吗?
我们能在属性中实现类似notify的东西吗。在你的类中实现INotifyPropertyChanged是否有一个优雅的解决方案,或者唯一的方法是在每个属性中引发PropertyChanged事件。
如果不是,我们可以写一些东西来自动生成一段代码来引发PropertyChanged事件?
微软应该为INotifyPropertyChanged实现一些时髦的东西,就像在自动属性中,只需要指定{get;设置;通知;} 我认为这样做很有意义。或者做这个手术有什么并发症吗?
我们能在属性中实现类似notify的东西吗。在你的类中实现INotifyPropertyChanged是否有一个优雅的解决方案,或者唯一的方法是在每个属性中引发PropertyChanged事件。
如果不是,我们可以写一些东西来自动生成一段代码来引发PropertyChanged事件?
当前回答
另一个想法……
public class ViewModelBase : INotifyPropertyChanged
{
private Dictionary<string, object> _propertyStore = new Dictionary<string, object>();
protected virtual void SetValue<T>(T value, [CallerMemberName] string propertyName="") {
_propertyStore[propertyName] = value;
OnPropertyChanged(propertyName);
}
protected virtual T GetValue<T>([CallerMemberName] string propertyName = "")
{
object ret;
if (_propertyStore.TryGetValue(propertyName, out ret))
{
return (T)ret;
}
else
{
return default(T);
}
}
//Usage
//public string SomeProperty {
// get { return GetValue<string>(); }
// set { SetValue(value); }
//}
public event PropertyChangedEventHandler PropertyChanged;
protected void OnPropertyChanged(string propertyName)
{
var temp = PropertyChanged;
if (temp != null)
temp.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
其他回答
我真的很喜欢Marc的解决方案,但我认为可以稍微改进一下,以避免使用“魔法字符串”(不支持重构)。与其将属性名作为字符串使用,不如将其创建为lambda表达式:
private string name;
public string Name
{
get { return name; }
set { SetField(ref name, value, () => Name); }
}
只需要在Marc的代码中添加以下方法,就可以了:
protected virtual void OnPropertyChanged<T>(Expression<Func<T>> selectorExpression)
{
if (selectorExpression == null)
throw new ArgumentNullException("selectorExpression");
MemberExpression body = selectorExpression.Body as MemberExpression;
if (body == null)
throw new ArgumentException("The body must be a member expression");
OnPropertyChanged(body.Member.Name);
}
protected bool SetField<T>(ref T field, T value, Expression<Func<T>> selectorExpression)
{
if (EqualityComparer<T>.Default.Equals(field, value)) return false;
field = value;
OnPropertyChanged(selectorExpression);
return true;
}
顺便说一句,这是受到这篇博客文章的启发。
=>这里我的解决方案具有以下特征
public ResourceStatus Status
{
get { return _status; }
set
{
_status = value;
Notify(Npcea.Status,Npcea.Comments);
}
}
没有refelction 短的符号 业务代码中没有神奇的字符串 PropertyChangedEventArgs在应用程序中的可重用性 在一条语句中通知多个属性的可能性
使用这个
using System;
using System.ComponentModel;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.Remoting.Messaging;
using System.Runtime.Remoting.Proxies;
public static class ObservableFactory
{
public static T Create<T>(T target)
{
if (!typeof(T).IsInterface)
throw new ArgumentException("Target should be an interface", "target");
var proxy = new Observable<T>(target);
return (T)proxy.GetTransparentProxy();
}
}
internal class Observable<T> : RealProxy, INotifyPropertyChanged, INotifyPropertyChanging
{
private readonly T target;
internal Observable(T target)
: base(ImplementINotify(typeof(T)))
{
this.target = target;
}
public override IMessage Invoke(IMessage msg)
{
var methodCall = msg as IMethodCallMessage;
if (methodCall != null)
{
return HandleMethodCall(methodCall);
}
return null;
}
public event PropertyChangingEventHandler PropertyChanging;
public event PropertyChangedEventHandler PropertyChanged;
IMessage HandleMethodCall(IMethodCallMessage methodCall)
{
var isPropertySetterCall = methodCall.MethodName.StartsWith("set_");
var propertyName = isPropertySetterCall ? methodCall.MethodName.Substring(4) : null;
if (isPropertySetterCall)
{
OnPropertyChanging(propertyName);
}
try
{
object methodCalltarget = target;
if (methodCall.MethodName == "add_PropertyChanged" || methodCall.MethodName == "remove_PropertyChanged"||
methodCall.MethodName == "add_PropertyChanging" || methodCall.MethodName == "remove_PropertyChanging")
{
methodCalltarget = this;
}
var result = methodCall.MethodBase.Invoke(methodCalltarget, methodCall.InArgs);
if (isPropertySetterCall)
{
OnPropertyChanged(methodCall.MethodName.Substring(4));
}
return new ReturnMessage(result, null, 0, methodCall.LogicalCallContext, methodCall);
}
catch (TargetInvocationException invocationException)
{
var exception = invocationException.InnerException;
return new ReturnMessage(exception, methodCall);
}
}
protected virtual void OnPropertyChanged(string propertyName)
{
var handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
protected virtual void OnPropertyChanging(string propertyName)
{
var handler = PropertyChanging;
if (handler != null) handler(this, new PropertyChangingEventArgs(propertyName));
}
public static Type ImplementINotify(Type objectType)
{
var tempAssemblyName = new AssemblyName(Guid.NewGuid().ToString());
var dynamicAssembly = AppDomain.CurrentDomain.DefineDynamicAssembly(
tempAssemblyName, AssemblyBuilderAccess.RunAndCollect);
var moduleBuilder = dynamicAssembly.DefineDynamicModule(
tempAssemblyName.Name,
tempAssemblyName + ".dll");
var typeBuilder = moduleBuilder.DefineType(
objectType.FullName, TypeAttributes.Public | TypeAttributes.Interface | TypeAttributes.Abstract);
typeBuilder.AddInterfaceImplementation(objectType);
typeBuilder.AddInterfaceImplementation(typeof(INotifyPropertyChanged));
typeBuilder.AddInterfaceImplementation(typeof(INotifyPropertyChanging));
var newType = typeBuilder.CreateType();
return newType;
}
}
}
我写了一篇文章来帮助你(https://msdn.microsoft.com/magazine/mt736453)。你可以使用SolSoft。DataBinding NuGet包。然后你可以这样写代码:
public class TestViewModel : IRaisePropertyChanged
{
public TestViewModel()
{
this.m_nameProperty = new NotifyProperty<string>(this, nameof(Name), null);
}
private readonly NotifyProperty<string> m_nameProperty;
public string Name
{
get
{
return m_nameProperty.Value;
}
set
{
m_nameProperty.SetValue(value);
}
}
// Plus implement IRaisePropertyChanged (or extend BaseViewModel)
}
好处:
基类是可选的 没有对每个“设定值”进行反思 可以具有依赖于其他属性的属性,并且它们都自动引发适当的事件(本文有一个这样的示例)
我使用以下扩展方法(使用c# 6.0)使INPC实现尽可能简单:
public static bool ChangeProperty<T>(this PropertyChangedEventHandler propertyChanged, ref T field, T value, object sender,
IEqualityComparer<T> comparer = null, [CallerMemberName] string propertyName = null)
{
if (comparer == null)
comparer = EqualityComparer<T>.Default;
if (comparer.Equals(field, value))
{
return false;
}
else
{
field = value;
propertyChanged?.Invoke(sender, new PropertyChangedEventArgs(propertyName));
return true;
}
}
INPC实现可以归结为(你可以每次都实现它,也可以创建一个基类):
public class INPCBaseClass: INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected bool changeProperty<T>(ref T field, T value,
IEqualityComparer<T> comparer = null, [CallerMemberName] string propertyName = null)
{
return PropertyChanged.ChangeProperty(ref field, value, this, comparer, propertyName);
}
}
然后像这样写属性:
private string testProperty;
public string TestProperty
{
get { return testProperty; }
set { changeProperty(ref testProperty, value); }
}
注意:如果您愿意,可以在扩展方法中省略[CallerMemberName]声明,但我想保持它的灵活性。
如果你有一个没有支持字段的属性,你可以重载changeProperty:
protected bool changeProperty<T>(T property, Action<T> set, T value,
IEqualityComparer<T> comparer = null, [CallerMemberName] string propertyName = null)
{
bool ret = changeProperty(ref property, value, comparer, propertyName);
if (ret)
set(property);
return ret;
}
一个例子是:
public string MyTestProperty
{
get { return base.TestProperty; }
set { changeProperty(base.TestProperty, (x) => { base.TestProperty = x; }, value); }
}