[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的实现:
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;
}
3.2 实现绑定类IBinding
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
}
IBinding的实现:
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);
}

 

posted @ 2025-08-29 11:24  孤独成派  阅读(47)  评论(0)    收藏  举报