如何创建TwoWay数据绑定代理

本文关键字:TwoWay 数据绑定 代理 创建 何创建 | 更新日期: 2023-09-27 18:29:51

我有以下类。。。

class ExpressionBinder<T>
{
    public Func<T> Getter;
    public Action<T> Setter;
    public T Value
    {
        get { return Getter.Invoke(); }
        set { Setter.Invoke(value); }
    }
    public ExpressionBinder(Func<T> getter, Action<T> setter)
    {
        Getter = getter;
        Setter = setter;
    }
}
class ComparisonBinder<TSource, TValue> : ExpressionBinder<bool>, INotifyPropertyChanged
{
    private TSource instance;
    private TValue comparisonValue;
    private PropertyInfo pInfo;
    public event PropertyChangedEventHandler PropertyChanged;
    public ComparisonBinder(TSource instance, Expression<Func<TSource, TValue>> property, TValue comparisonValue) : base(null,null)
    {
        pInfo = GetPropertyInfo(property);
        this.instance = instance;
        this.comparisonValue = comparisonValue;
        Getter = GetValue;
        Setter = SetValue;
    }
    private bool GetValue()
    {
        return comparisonValue.Equals(pInfo.GetValue(instance, null));
    }
    private void SetValue(bool value)
    {
        if (value)
        {
            pInfo.SetValue(instance, comparisonValue,null);
            NotifyPropertyChanged("CustomerName");
        }
    }
    private void NotifyPropertyChanged(string pName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(pName));
        }
    }
    /// <summary>
    /// Adapted from surfen's answer (https://stackoverflow.com/a/10003320/219838)
    /// </summary>
    private PropertyInfo GetPropertyInfo(Expression<Func<TSource, TValue>> propertyLambda)
    {
        Type type = typeof(TSource);
        MemberExpression member = propertyLambda.Body as MemberExpression;
        if (member == null)
            throw new ArgumentException(string.Format(
                "Expression '{0}' refers to a method, not a property.",
                propertyLambda));
        PropertyInfo propInfo = member.Member as PropertyInfo;
        if (propInfo == null)
            throw new ArgumentException(string.Format(
                "Expression '{0}' refers to a field, not a property.",
                propertyLambda));
        if (type != propInfo.ReflectedType &&
            !type.IsSubclassOf(propInfo.ReflectedType))
            throw new ArgumentException(string.Format(
                "Expresion '{0}' refers to a property that is not from type {1}.",
                propertyLambda,
                type));
        return propInfo;
    }
}

我使用ComparisonBinder类的代码如下:

radioMale.DataBindings.Add(
    "Checked", 
    new ComparisonBinder<DataClass, GenderEnum>(DataObj, (x) => x.Gender, GenderEnum.Male), 
    "Value", 
    false, 
    DataSourceUpdateMode.OnPropertyChanged);
radioFemale.DataBindings.Add(
    "Checked",
    new ComparisonBinder<DataClass, GenderEnum>(DataObj, (x) => x.Gender, GenderEnum.Male), 
    "Value", 
    false, 
    DataSourceUpdateMode.OnPropertyChanged);

它们允许我使用表达式将多个控件绑定到同一属性,以生成控件绑定属性的值。它从控件到对象都工作得很好。(如果你想了解更多关于使用这个类和这个问题的信息)

现在,我需要换一种方式。当我更新我的对象(这将改变get表达式的结果)时,我需要更新绑定控件。我尝试实现INotifyPropertyChanged,但没有任何区别。

有一件奇怪的事:将控件DataSourceUpdateMode设置为OnPropertyChanged不知何故阻止了我更改选中的收音机。

如何创建TwoWay数据绑定代理

快速回答:

  • 您必须使用绑定源
  • 在对象中实现INotifyPropertyChanged
  • 使代理侦听上面的事件
  • 在代理中实现INotifyPropertyChanged
  • 筛选从代理中的对象接收的PropertyChanged事件,并仅在((PropertyChangedEventArgs) args).PropertyName == pInfo.Name时引发
  • 将代理添加到BindSource.DataSource
  • 使用BindingSource将控件绑定到数据对象的属性

最终代码

可用(与其他绑定类一起)位置:http://github.com/svallory/BindingTools

类别

class ExpressionBinder<T> : INotifyPropertyChanged
{
    public Func<T> Getter;
    public Action<T> Setter;
    public event PropertyChangedEventHandler PropertyChanged;
    protected IList<string> WatchingProps;
    public T Value
    {
        get { return Getter.Invoke(); }
        set { Setter.Invoke(value); }
    }
    public ExpressionBinder(Func<T> getter, Action<T> setter)
    {
        WatchingProps = new List<string>();
        Getter = getter;
        Setter = setter;
    }
    public ExpressionBinder(Func<T> getter, Action<T> setter, ref PropertyChangedEventHandler listenToChanges, IList<string> propertyNames)
    {
        Getter = getter;
        Setter = setter;
        listenToChanges += SourcePropertyChanged;
        WatchingProps = propertyNames;
    }
    protected void SourcePropertyChanged(object obj, PropertyChangedEventArgs args)
    {
        if (PropertyChanged != null && WatchingProps.Contains(args.PropertyName))
        {
            PropertyChanged(this, args);
        }
    }
    protected PropertyInfo GetPropertyInfo<TSource, TValue>(Expression<Func<TSource, TValue>> propertyLambda)
    {
        Type type = typeof(TSource);
        var member = propertyLambda.Body as MemberExpression;
        if (member == null)
            throw new ArgumentException(string.Format(
                "Expression '{0}' refers to a method, not a property.",
                propertyLambda));
        var propInfo = member.Member as PropertyInfo;
        if (propInfo == null)
            throw new ArgumentException(string.Format(
                "Expression '{0}' refers to a field, not a property.",
                propertyLambda));
        if (type != propInfo.ReflectedType &&
            !type.IsSubclassOf(propInfo.ReflectedType))
            throw new ArgumentException(string.Format(
                "Expresion '{0}' refers to a property that is not from type {1}.",
                propertyLambda,
                type));
        return propInfo;
    }
}
class ComparisonBinder<TSource, TValue> : ExpressionBinder<bool> where TSource : INotifyPropertyChanged
{
    private readonly TSource instance;
    private readonly TValue comparisonValue;
    private readonly PropertyInfo pInfo;
    public ComparisonBinder(TSource instance, Expression<Func<TSource, TValue>> property, TValue comparisonValue)
        : base(null, null)
    {
        pInfo = GetPropertyInfo(property);
        this.instance = instance;
        this.comparisonValue = comparisonValue;
        Getter = GetValue;
        Setter = SetValue;
        instance.PropertyChanged += SourcePropertyChanged;
        WatchingProps.Add(pInfo.Name);
    }
    private bool GetValue()
    {
        return comparisonValue.Equals(pInfo.GetValue(instance, null));
    }
    private void SetValue(bool value)
    {
        if (value)
        {
            pInfo.SetValue(instance, comparisonValue, null);
        }
    }
}

用法

var bs = new BindingSource();
bs.DataSource = new ComparisonBinder<MySourceObjClass, PropType>(
    MySourceObject, 
    p => p.PropertyToBind, 
    ValueToCompare);
rdBeSmart.DataBindings.Add(
    "Checked", 
    bs, 
    "Value");