How to create a proxy server for TwoWay data transfer?

I have the following classes ...

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 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;
    }
}

      

My code using the class ComparisonBinder

looks like this:

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);

      

They allow me to bind many controls to the same property using an expression to generate a value for the control-related property. It works great from control to object. (If you want to know more about using this class and this question )

Now I need it differently. When I update my object (which will change the result of the get expression), I need the associated control to update. I tried to implement INotifyPropertyChanged

it but it didn't make any difference.

One weird thing: setting the controls DataSourceUpdateMode

to OnPropertyChanged

somehow prevents me from changing the checked radio.

+3


source to share


1 answer


Quick response:

  • use binding source
  • Implement INotifyPropertyChanged in your object
  • Make proxy server listening event above
  • Implement INotifyPropertyChanged in proxy
  • Filter out PropertyChanged events received from your object in the proxy and only raise when ((PropertyChangedEventArgs) args).PropertyName == pInfo.Name

  • Add a proxy server to BindSource.DataSource

  • Use BindingSource

    to bind a control to a property of a data object

Final code

Available (along with other binding classes) at: http://github.com/svallory/BindingTools



Classes

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);
        }
    }
}

      

Using

var bs = new BindingSource();
bs.DataSource = new ComparisonBinder<MySourceObjClass, PropType>(
    MySourceObject, 
    p => p.PropertyToBind, 
    ValueToCompare);

rdBeSmart.DataBindings.Add(
    "Checked", 
    bs, 
    "Value");

      

+1


source







All Articles