[C#] 基于Prism实现弱委托、弱事件、INotifyPropertyChanged之间的双向绑定
一、基于Prism的DelegateReference实现弱委托
DelegateReference就是Target=Delegate的一个WeakReference,是Prism用于实现PubSubEvent的一个对象,基于DelegateReference实现的WeakDelegate实现了+、-操作符,Delegate本质是链式结构,WeakDelegate存储了多个DelegateReference,WeakDelegate.Target就是通过Delegate.Combine方法合并多个Delegate,基于WeakDelegate的WeakDelegate<T>是支持泛型的弱委托。
WeakReference的实现:
弱事件的监听与取消监听:
3.2 实现绑定类IBinding
IBinding的定义:
定义绑定模式:
IBinding的实现:
DelegateReference就是Target=Delegate的一个WeakReference,是Prism用于实现PubSubEvent的一个对象,基于DelegateReference实现的WeakDelegate实现了+、-操作符,Delegate本质是链式结构,WeakDelegate存储了多个DelegateReference,WeakDelegate.Target就是通过Delegate.Combine方法合并多个Delegate,基于WeakDelegate的WeakDelegate<T>是支持泛型的弱委托。
WeakReference的实现:
public class WeakDelegate : IDelegateReference { #region Static Method public static WeakDelegate operator +(WeakDelegate weakDelegate, Delegate handler) { if (handler == null) { return; } if (weakDelegate != null) { weakDelegate._delegates.Add(new DelegateReference(handler, false)); return weakDelegate; } return new WeakDelegate(handler); } public static WeakDelegate operator -(WeakDelegate weakDelegate, Delegate handler) { if (handler == null) { return weakDelegate; } if (weakDelegate != null) { weakDelegate._delegates.RemoveAll(q => q. q.TargetEquals(handler)); } return weakDelegate; } #endregion public WeakDelegate() { _delegates = new List<DelegateReference>(); } public WeakDelegate(Delegate handler) : this() { if (handler != null) { _delegates.Add(new DelegateReference(handler, false)); } } public Delegate Target { get { var targets = new List<Delegate>(_delegates.Count()); foreach (var handler in _delegates) { var target = handler.Target; if (target != null) { targets.Add(target); } } if (targets.Count == 0) { return null; } else if (targets.Count == 1) { return targets[0]; } return Delegate.Combine(targets.ToArray()); } } public bool IsAlive => _delegates.Any(q => q.Target != null); protected List<DelegateReference> _delegates; } public class WeakDelegate<T> : WeakDelegate where T : Delegate { public static WeakDelegate<T> operator +(WeakDelegatte<T> weakDelegate, I t) { if (t == null) { return } if (weakDelegate != null) { weakDelegate._delegates.Add(new DelegateReference(t, false)); return weakDelegate; } return new WeakDelegate<T>(t); } public static WeakDelegate<T> operator -(WeakDelegatte<T> weakDelegate, I t) { if (t == null) { return weakDelegate; } if (weakDelegate != null) { weakDelegate._delegates.RemoveAll(q => q. q.TargetEquals(t)); } return weakDelegate; } public WeakDelegate() : base() { } public WeakDelegate(T t) : base(t) { } public bool TryGetTarget(out T t) => (t = Target) != null; public new T Target => (T)base.Target; }
二、基于WeakReference封装弱事件
事件是一种特殊的委托,下面请看如何将委托封装成事件,首先定义一个委托管理器,实现AddHandler、RemoveHandler。
WeakEventsManager的实现:
public class WeakEventsManager { private readonly Dictionary<string, WeakDelegate> eventMap = new Dictionary<string, WeakDelegate>(); private readonly HashSet<string> eventNameSet = new HashSet<string>(); public string WeakEventNames => eventNameSet.ToArray(); public void AppendSupportEvents(params string eventNames) { foreach (var eventName in eventNames) { eventNameSet.Add(eventName); } } public void AddHandler(Delegate handler, [CallerMeemberName] string eventName = null) { if (handler == null) { return; } if (eventMap. TryGetValue (eventName, out var weakDelegate)) { weakDelegate += handler; } else { eventMap.Add(eventName, new WeakDelegate(handler)); eventNameSet.Add(eventName); } } public void RemoveHandler(Delegate handler, [CallerMemberName] string eventName = null) { if (handler== null) { return; } if (eventMap.TryGetValue(eventName, out var weakDelegate)) { weakDelegate -= handler; } } public void RaiseEvent(string eventName, params object[]args) { if (eventMap.TryGetValue(eventName, out var weakDelegate) && weakDelegate.TryGetTarget(out var handler)) { handler.DynamicInvoke(args); } } }
public event PropertyChangedEventHandler WeakPropertyChanged{ add => WeakEvents.AddHandler(value); remove => WeakEvents. RemoveHandler(value); }
三、实现INotifyPropertyChanged之间的双向绑定
3.1 定义支持弱事件的IWeakNotifyPropertyChanged
由于弱事件是由弱引用包装的,相比直接引用获取弱引用对象性能更差,特别是频繁触发事件时,所以特别定义了弱事件WeakPropertyChanged,一般的属性变更仅触发PropertyChanged,只有绑定的属性变更才会触发WeakPropertyChanged。
IWeakNotifyPropertyChanged的定义:
public interface IWeakNotifyPropertyChanged : INotifyPropertyChanged, ISupportWeakEvents { event PropertyChangedEventHandler WeakPropertyChanged; }
IBinding的定义:
public interface IBinding { object Owner { get; } object Source { get; } string TartgetPropertyName { get; } string SourcePropertyName { get;} void UnBind(); void Bind(); }
public enum BindingMode { TwoWay = 0, OneWay = 1, OneWayToSource = 3 }
public class Binding<TTarget, TTargetProperty, TSource,TSourceProperty>: IBinding, IDisposable where TTarget :class where TSource : class { private bool disposed; private WeakReference<TSource> weakSoure; public TTarget Owner { get; } public TSource Source => (weakSoure != null && weakSoure.TryGetTarget(out var source)) ? source : null; public Expression<Func<TTarget, TTargetProperty>> TargetProperty { get; } public Expression<Func<TSource, TSourceProperty>> SourceProperty { get; } public Action<TTarget, TSourceProperty> SetTargetProperty { get; set; } public Action<TSource, TTargetProperty> SetSourceProperty { get; set; } public Expression<Func<TTarget, TSourceProperty>> SourcePropertyByTarget { get; set; } public Action<TTarget, TTargetProperty> SetSourcePropertyByTarget { get; set; } public Action<TSourceProperty> SetTargetPropertyOnlyWaluee { get; set; } public Action<TTargetProperty> SetSourcePropertyOnlyWalule { get; set; } public BindingMode BindingMode { get; private set; } public string TartgetPropertyName => TargetProperty == null ? null : ((MemberExpression)TargetProperty.Body).Member.Name; public string SourcePropertyName => SourceProperty == null ? (SourcePropertyByTarget == rnull ? null : ((MemberExpression)SourcePropertyByTarget.Body).Membper.Name) : ((MemberExpression)SourceProperty.Body).Member.Name; object IBinding.Owner => Owner; object IBinding.Source => Source; public Binding(TTarget owner, Expression<Func<TTarget, TTargetProperty>> targetProperty, TSource source, Expression<Func<TSource, TSourceProperty>> sourceProperty, Action<TTarget, TSourceProperty> setTargetProperty, Actiion<TSource, TTargetProperty> setSourceProperty) { Owner = owner; weakSoure = new WeakReference<TSource>(source); TargetProperty = targetProperty; SourceProperty = sourceProperty; SetTargetProperty = setTargetProperty; SetSourceProperty = setSourceProperty; Bind(); } public Binding(TTarget owner, Expression<Func<TTarget, TTargetProperty>> targetProperty, Expression<Func<TTarget, TSourceProperty>> sourcePropertyByTarget, Action<TTarget, TSourceProperty> setTargetProperty, Action<TTarget, TTargetProperty> setSourcePropertyByTarget) { Owner = owner; if (owner is IDataContext dc && dc.DataContextis TSource source) { weakSoure = new WeakReference<TSource>(source); } TargetProperty = targetProperty; SourcePropertyByTarget = sourcePropertyByTarget; SetTargetProperty = setTargetProperty; SetSourcePropertyByTarget = setSourcePropertyByTarget; Bind(): } public Binding(TTarget owner, Expression<Func<TTarget,TTargetProperty>> targetProperty, Expression<Func<ITarrget, TSourceProperty>> sourcePropertyByTarget, Action<TSourceProperty> setTargetPropertyOnlyValue, Actiion<TTargetProperty> setSourcePropertyOnlyValue) { Owner = owner: if (owner is IDataContext dc && dc.DataContexIt is TSource source) { weakSoure = new WeakReference<TSource>(source); } TargetProperty = targetProperty; SourcePropertyByTarget = sourcePropertyByTarget; SetTargetPropertyOnlyValue = setTargetPropertyOnlyvalue; SetSourcePropertyOnlyValue = setSourcePropertyOnlyValue; Bind(); } ~Binding() { Dispose(false); } public void UnBind() { switch (BindingMode) { case BindingMode.Twoway: { if (Owner is INotifyPropertyChanged notifyOwner) { notifyOwner.PropertyChanged -= OnownerPropertyChanged; } if (Source is INotifyPropertyChanged notifySource) { if (notifySource is IWeakNotifyPropertyChanged weakSource && weakSource.WeakEventNames.Contains(nameof(weakSource.WeakPropertyChanged))) { weakSource.WeakPropertyChanged -= OnSourcePropertyCChanged; } else { notifySource.PropertyChanged -= OnSourcePropertyChanged; } } } break; case BindingMode.OneWay: { if (Source is INotifyPropertyChanged notifySource) { if (notifySource is IWeakNotifyPropertyChanged weakSource && weakSource.WeakEventNames.Contains(nameof(weakSource.WeakPropertyChanged))) { weakSource.WeakPropertyChanged -= OnSourcePropertyChanged; } else { notifySource.PropertyChanged -= OnSourcePropertyChaanged } } } break case BindingMode.OneWayToSource: { if (Owner is INotifyPropertyChanged notifyOwner) { notify0wner.PropertyChanged -= OnownerPropertyChangged } } break } } public void Bind() { if (SetTargetProperty != null) { if (SetSourceProperty != null || SetSourcePropertyByTarg¿et != null) { BindingMode = BindingMode.Twoway; } else { BindingMode = BindingMode.OneWay; } } else if (SetSourceProperty != mull || SetSourcePropertyByTarget != null) { BindingMode = BindingMode.OneWayToSource; } switch (BindingMode) { case BindingMode.Twoway: { SourceToTarget(); if (Owner is INotifyPropertyChanged notifyOwner) { notifyOwner.PropertyChanged += OnownerPropertyChanged; } if (Source is INotifyPropertyChanged notifySource) { if (notifySource is IWeakNotifyPropertyChanged weakSource && wveakSource.WeakEventNames.Contains(nameof(weakSource.WeakPropertyChanged))) { weakSource.WeakPropertyChanged += OnSourcePropertyChanged; } else { notifySource.PropertyChanged += OnSourcePropertyChanged; } } } break case BindingMode.OneWay: { SourceToTarget(); if (Source is INotifyPropertyChanged notifySource) { if (notifySource is IWeakNotifyPropertyChanged weakSource && weakSource.WeakEventNames.Contains(nameof(weakSource.WeakPropertyChanged))) { weakSource.WeakPropertyChanged += OnSourcePropertyChanged; } else { notifySource.PropertyChanged += OnSourcePropertyChanged; } } } break; case BindingMode.OneWayToSource: { TargetToSource(); if (Owner is INotifyPropertyChanged notifyOwner) { notifyOwner.PropertyChanged += Onowner PropertyChanged; } } break; default: throw new InvalidOperationException($"不支持绑定模式:{}BindingMode}") } } private void OnOwnerPropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == TartgetPropertyName) { TargetToSource(); } } private void OnSourcePropertyChanged(object sender, PropertyChangedEventArgs e) { if (e.PropertyName == SourcePropertyName) { SourceToTarget(); } } private void SourceToTarget() { if (SourceProperty != null) { var sourcePropertyValue = SourceProperty.Compile().Invoke(Soturce); if (SetTargetProperty != null) { SetTargetProperty(Owner, sourcePropertyWalue); } else if (SetTargetPropertyOnlyValue != null) { SetTargetPropertyOnlyValue(sourcePropertyValue); } } else if (SourcePropertyByTarget != null) { var sourcePropertyValue = SourcePropertyByTarget.Compile().Invoke(Owner); if (SetTargetProperty ! = null) { SetTargetProperty(Owner, sourcePropertyValue); } else if (SetTargetPropertyOnlyValue != null) { SetTargetPropertyOnlyValue(sourcePropertyValue); } } } private void TargetToSource() { var targetPropertyWalue = TargetProperty.Compile().Invoke(Owner); if (SetSourceProperty != null) { if (Source is TSource source) { SetSourceProperty(source,targetPropertyValue); } } else if (SetSourcePropertyByTarget != null) { SetSourcePropertyByTarget(Owner, targetPropertyWalue); } else if (SetSourcePropertyOnlyValue != null) { SetSourcePropertyOnlyValue(targetPropertyValue); } } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } private void Dispose(bool disposing) { if (!disposed) { if (disposing) { UnBind(); } disposed = true; } } }
这里巧妙地利用表达式树获取属性名和对属性进行赋值;绑定模式根据参数自动识别。
四、使用扩展方法简化绑定
BindingExtension的实现:
public static class BindingExtension { private static readonly string BindingsPropertyName = "Bindings"; public static void SetBinding<TTarget, TSourceProperty>(this TTarget owner, Expression<Func<TTarget, TSourceProperty>> sourceProperty, Action<TSourceProperty> setTargetPropertyOnlyValue) where TTarget : class, IDataContext => SetBinding<TTarget, bool, TSourceProperty>(owner, nul1, sourceProperty, setTargetPropertyOnlyValue, null); public static void SetBinding<TTarget, TTargetProperty, TSourcePropperty>(this TTarget owner, Expression<Func<TTarget, TTargetProperty>> targetProperty, Expression<Func<TTarget, TSourceProperty>> sourceProjperty, Action<TSourceProperty> setTargetPropertyOnlyValue, Action<TTargetProperty> setSourcePropertyOnlyValue) where TTarget : olass, IDataContext { var binding = new Binding<TTarget, TTargetProperty, object,TSourceProperty>(owner, targetProperty, sourceProperty, setTaargetPropertyOnlyValue, setSourcePropertyOnlyValue); SetBinding(owner, binding); } public static void SetBinding<TTarget, TSourceProperty>(this TTarget owner, Expression<Func<TTarget, TSourceProperty>> sourceProperty, Action<TTarget, TSourceProperty> setTargetProperty) where TTarget : class, IDataContext => SetBinding<TTarget, bool, TSourceProperty>(owner, null, sourceProperty, setTargetProperty, null); public static void SetBinding<TTarget, TTargetProperty, TSourceProperty>(this TTarget owner, Expression<Func<TTarget, TlargetProperty>> targetProperty, Expression<Func<TTarget, TSourceProperty>> sourceProperty, Action<TTarget, TSourceProperty> setTargetProperty, Actionn<TTarget, TTargetProperty> setSourcePropertyByTarget) where TTarget : class, IDataContext { var binding = new Binding<TTarget, TTargetProperty, objectt, TSourceProperty>(owner, targetProperty, sourceProperty,setTargetProperty, setSourcePropertyByTarget); SetBinding (owner, binding); } public static void SetBinding<TTarget, TSource, TSourceProperty>(this TTarget owner, TSource source, Expressionn<Func<TSource, TSourceProperty>> sourceProperty, Action<TTarget, TSourceProperty> setTargetProperty) where TTarget : class where TSource : class => SetBinding<TTarget, bool, TSource, TSourceProperty>(ownerr, null, source, sourceProperty, setTargetProperty, null); public static void SetBinding<TTarget, TTargetProjperty, TSource, TSourceProperty>(this TTarget owner, Expression<Func<TTarget, TTargetProperty>> targetProperty, TSource source, Expression<Func<TSource, TSourceProperty>> sourceProperty, Action<TTarget, TSourceProperty> setTargetProperty, Action<TSource, TTargetProperty> setSourceProperty) where TTarget : class where TSource : class { var binding = new Binding<TTarget, TTargetProperty, TSouro:e, TSourceProperty>(owner, targetProperty, source, sourceProperty, setTargetProperty, setSourceProperty) SetBinding (owner, binding); } public static void SetBinding<TTarget, TTargetProperty,TSource, TSourceProperty>(this TTarget owner, Binding<TTarget, TTargetProperty, TSource, TSourceProperty> binding) where TTarget : class where TSource : class { if (OwnerResourceManager.TryGetResource(owner, BindingsPropertyName, out List<IBinding> bindings)) { bindings.Add(binding); } else { OwnerResourceManager.AddResource(owner, BindingsPropertyName, new List<IBinding> { binding }); } } public static void ClearBindings(this objectowner, Predicate<IBinding> filter = null) { if (OwnerResourceManager.TryGetResource(owner, BindingsPropertyName, out List<IBinding> bindings)) { var unBindings = new List<IBinding>(); foreach (var binding in bindings) { if (filter== null || filter.Invoke(binding)) { binding.UnBind(); unBindings.Add(binding); } } foreach (var unBinding in unBindings) { bindings.Remove(unBinding); } } } }
OwnerResourceManager是资源管理器,当对象销毁时,其注册的资源自动销毁。
使用示例:
class Person : BindableBase { private string _name; public string Name { get => _name; set => SetProperty(ref _name, value); } private string _age; public string Age { get => _age; set => SetProperty(ref _age, value); } } void Main() { var personA = new Person(); var personB = new Person(); // 单向绑定,A.Name = B.Name + "的爸爸" personA.SetBinding(personB, q => q.Name, (t, v) => t.Name = $"{v}的爸爸"); // 双向绑定,A.Age = B.Age + 30,B.Age = A.Age - 30 personA.SetBinding(q => q.Age, personB, q => q.Age, (t, v) => t.Age = v + 30, (s, v) => s.Age = v - 30); }
浙公网安备 33010602011771号