一个老问题,不过...
我最初的方法是将子属性更改为父属性。这有一个好处,消费父母的事件很容易。只需要订阅父级。
public class NotifyChangedBase : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
readonly Dictionary<string, AttachedNotifyHandler> attachedHandlers = new Dictionary<string, AttachedNotifyHandler>();
[NotifyPropertyChangedInvocator]
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
protected void AttachPropertyChanged(INotifyPropertyChanged notifyPropertyChanged,
[CallerMemberName] string propertyName = null)
{
if (propertyName == null) throw new ArgumentNullException(nameof(propertyName));
// ReSharper disable once ExplicitCallerInfoArgument
DetachCurrentPropertyChanged(propertyName);
if (notifyPropertyChanged != null)
{
attachedHandlers.Add(propertyName, new AttachedNotifyHandler(propertyName, this, notifyPropertyChanged));
}
}
protected void DetachCurrentPropertyChanged([CallerMemberName] string propertyName = null)
{
if (propertyName == null) throw new ArgumentNullException(nameof(propertyName));
AttachedNotifyHandler handler;
if (attachedHandlers.TryGetValue(propertyName, out handler))
{
handler.Dispose();
attachedHandlers.Remove(propertyName);
}
}
sealed class AttachedNotifyHandler : IDisposable
{
readonly string propertyName;
readonly NotifyChangedBase currentObject;
readonly INotifyPropertyChanged attachedObject;
public AttachedNotifyHandler(
[NotNull] string propertyName,
[NotNull] NotifyChangedBase currentObject,
[NotNull] INotifyPropertyChanged attachedObject)
{
if (propertyName == null) throw new ArgumentNullException(nameof(propertyName));
if (currentObject == null) throw new ArgumentNullException(nameof(currentObject));
if (attachedObject == null) throw new ArgumentNullException(nameof(attachedObject));
this.propertyName = propertyName;
this.currentObject = currentObject;
this.attachedObject = attachedObject;
attachedObject.PropertyChanged += TrackedObjectOnPropertyChanged;
}
public void Dispose()
{
attachedObject.PropertyChanged -= TrackedObjectOnPropertyChanged;
}
void TrackedObjectOnPropertyChanged(object sender, PropertyChangedEventArgs propertyChangedEventArgs)
{
currentObject.OnPropertyChanged(propertyName);
}
}
}
用法很简单:
public class Foo : NotifyChangedBase
{
Bar bar;
public Bar Bar
{
get { return bar; }
set
{
if (Equals(value, bar)) return;
bar = value;
AttachPropertyChanged(bar);
OnPropertyChanged();
}
}
}
public class Bar : NotifyChangedBase
{
string prop;
public string Prop
{
get { return prop; }
set
{
if (value == prop) return;
prop = value;
OnPropertyChanged();
}
}
}
但是,这种方法不是很灵活,并且无法对其进行控制,至少无需额外的复杂工程。如果订阅系统具有遍历嵌套数据结构的灵活性,则它的适用性仅限于 1 级子级。
虽然这些警告可能是可以接受的,但根据使用情况,我已经放弃了这种方法,因为它永远无法确定最终将如何使用数据结构。目前更喜欢这样的解决方案:
https://github.com/buunguyen/notify
这样即使是复杂的数据结构也是简单和可预测的,它在订阅者控制下如何订阅和如何反应,它与绑定引擎的功能很好地配合。