3

私は次のクラスを持っています...

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、チェックしたラジオを変更できなくなります。

4

1 に答える 1

1

素早い回答:

  • バインディングソースを使用する必要があります
  • オブジェクトに 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");
于 2012-04-04T16:51:26.910 に答える