我正在尝试通过反射触发PropertyChanged并且我遇到了一些问题。
以下代码有效:
public abstract class ViewModelBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
private string m_test = string.Empty;
public string Test
{
get
{
return m_test;
}
set
{
m_test = value;
Notify();
}
}
protected void Notify([CallerMemberName] string name = null)
{
var handler = PropertyChanged;
if (handler != null && name != null)
{
handler(this, new PropertyChangedEventArgs(name));
}
}
}
public class ViewModel : ViewModelBase
{
private string m_test2 = string.Empty;
public string Test2
{
get
{
return m_test2;
}
set
{
m_test2 = value;
Notify();
}
}
}
但是,我已经为INotifyPropertyChanged添加了一个扩展方法,可以通过反射来提升它。
而不是Notify()
我可以改为调用this.Notify()
,其定义如下:
/// <summary>
/// Invoke sender's PropertyChanged event via Reflection
/// </summary>
/// <param name="sender">sender of the event</param>
/// <param name="prop">The Property name that has changed</param>
public static void NotifyPropertyChanged(this INotifyPropertyChanged sender, [CallerMemberName] string prop = null)
{
var senderType = sender.GetType();
var methodInfo = senderType.GetField("PropertyChanged", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
if (methodInfo != null)
{
var delegates = (MulticastDelegate)methodInfo.GetValue(sender);
if (delegates != null)
{
foreach (var handler in delegates.GetInvocationList())
{
handler.Method.Invoke(handler.Target, new object[] { sender, new PropertyChangedEventArgs(prop) });
}
}
}
}
不幸的是,GetField
在上面的示例中为ViewModel
返回null。
有没有办法反映父母的事件?
我正在考虑迭代基类,但我希望有更好/更简单的方法。
答案 0 :(得分:1)
我认为你的做法是错误的。
您正在破坏框架对仅由声明(拥有)实例引发的事件的封装。通过使用任何人都可以调用的公开扩展方法,您可以打开一堆蠕虫。
更好的解决方案是在基类中使用受保护的方法,就像在“以下代码工作”示例中所做的那样。
但如果你真的决定这样做,显然可以做到。
如果您想打破正常的保护和事件封装,可以使用下面的扩展方法。
public static class ProperyChangedEventExtensions
{
public static void RaisePropertyChanged<T, P>(this T sender, Expression<Func<T, P>> propertyExpression) where T : INotifyPropertyChanged
{
Raise(typeof(T), sender, (propertyExpression.Body as MemberExpression).Member.Name);
}
public static void RaisePropertyChanged(this INotifyPropertyChanged sender, [CallerMemberName] string prop = null)
{
Raise(sender.GetType(), sender, prop);
}
private static void Raise(Type targetType, INotifyPropertyChanged sender, string propName)
{
var evtPropType = targetType.GetField("PropertyChanged", BindingFlags.Instance | BindingFlags.NonPublic);
var evtPropVal = (PropertyChangedEventHandler)evtPropType.GetValue(sender);
evtPropVal(sender, new PropertyChangedEventArgs(propName));
}
}
用法示例(包括一些会让您重新考虑这种方法的案例):
class MyViewModel : INotifyPropertyChanged
{
// The compiler will complain about this:
// Warning 3 The event 'MyNamespace.MyViewModel.PropertyChanged' is never used
public event PropertyChangedEventHandler PropertyChanged;
private string _myProp;
public string MyProp
{
get { return _myProp; }
set
{
_myProp = value;
this.RaisePropertyChanged();
}
}
public readonly int MyImmutableValue;
}
// ...
var vm = new MyViewModel();
vm.PropertyChanged += (sender, evt) => Console.WriteLine("Prop changed {0}", evt.PropertyName);
vm.MyProp = "abc";
vm.RaisePropertyChanged(x => x.MyProp);
vm.RaisePropertyChanged("MyProp");
vm.RaisePropertyChanged("Un Oh. Do we have a problem");
vm.RaisePropertyChanged(x => x.MyImmutableValue);
vm.RaisePropertyChanged("MyImmutableValue");
答案 1 :(得分:0)
/// <summary>
/// Invoke sender's PropertyChanged event via Reflection???
/// </summary>
/// <param name="sender">sender of the event</param>
/// <param name="prop">The Property name that has changed</param>
public static void NotifyPropertyChanged(this INotifyPropertyChanged sender, PropertyChangedEventHandler handler, [CallerMemberName] string prop = null)
{
handler(sender, new PropertyChangedEventArgs(prop));
}
像这样使用它?
class MyViewModel : INotifyPropertyChanged
{
// The compiler will complain about this:
// Warning 3 The event 'MyNamespace.MyViewModel.PropertyChanged' is never used
public event PropertyChangedEventHandler PropertyChanged;
private string _myProp;
public string MyProp
{
get { return _myProp; }
set
{
_myProp = value;
this.Notify(this.PropertyChanged);
}
}
}