【问题标题】:how to dynamically create binding proxies?如何动态创建绑定代理?
【发布时间】:2012-04-03 23:15:11
【问题描述】:

我有一个配置对象,它有一个enum,我想将一堆RadioButton 绑定到同一个属性。当我看到自己重复它时,我试图提出一个通用的解决方案,将多个项目绑定到同一个属性。我找到的解决方案是创建一个执行表达式的代理。请参阅下面的代码。

public enum GenderEnum
{
    Male,
    Female
}

public class DataClass
{
    public GenderEnum Gender { get; set; }
}

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

我可以这样使用:

radioMale.Tag = GenderEnum.Male;
radioFemale.Tag = GenderEnum.Female;

var proxyM = new ExpressionBinder<bool>(
    () => DataObj.Gender == (GenderEnum)radioMale.Tag,
    (val) => { if (val) DataObj.Gender = (GenderEnum)radioMale.Tag; });

var proxyF = new ExpressionBinder<bool>(
    () => DataObj.Gender == (GenderEnum)radioFemale.Tag,
    (val) => { if (val) DataObj.Gender = (GenderEnum)radioFemale.Tag; });

radioMale.DataBindings.Add("Checked", proxyM, "Value");
radioFemale.DataBindings.Add("Checked", proxyF, "Value");

这只是一个例子,我想保持简单。但这种方法实际上WORKS。问题是:

我想将比较和设置表达式封装在派生类中。

class ComparisonBinder : ExpressionBinder {}

很遗憾,我只能说我想怎么用。

radioMale.DataBindings.Add("Checked", 
    new ComparisonBinder<ConfigClass>((c) c.Gender, GenderEnum.Male), 
    "Value");
radioFemale.DataBindings.Add("Checked", 
    new ComparisonBinder<ConfigClass>((c) c.Gender, GenderEnum.Female),
    "Value");

那么,您将如何实现呢?如果需要,请随意更改我的 ExpressionBinder 类。


最终代码

(再次编辑:使用对象类型不能省略 TValue 参数。在 lambda 表达式的转换过程中,编译器将添加对Convert() 的调用。)

class ComparisonBinder<TSource, TValue> : ExpressionBinder<bool>
{
    private TSource instance;
    private TValue comparisonValue;
    private PropertyInfo pInfo;

    public ComparisonBinder(TSource instance, Expression<Func<TSource, TValue>> property, TValue comparisonValue)
    {
        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);
        }
    }

    /// <summary>
    /// Adapted from surfen's answer (http://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;
    }
}

用法:

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

【问题讨论】:

    标签: c# .net winforms data-binding


    【解决方案1】:

    我认为这可以使用PropertyInfo 和一些 lambda 魔法来完成:

    class ComparisonBinder<TSource,TValue> : ExpressionBinder<bool>
    {
        public ComparisonBinder(TSource source, Expression<Func<TSource, bool>> propertyLambda, TValue comparisonValue) :base(null,null)
        {
             var propertyInfo = GetPropertyInfo<TSource,bool>(source, propertyLambda);
             this.Getter = () => comarisonValue.Equals((TValue)propertyInfo.GetValue(source));
             this.Setter = (bool value) => { if(value) propertyInfo.SetValue(source, comparisonValue); };
        }
    }
    

    用法:

    radioMale.DataBindings.Add("Checked", 
        new ComparisonBinder<ConfigClass, GenderEnum>(config, c => c.Gender, GenderEnum.Male), 
        "Value");
    radioFemale.DataBindings.Add("Checked", 
        new ComparisonBinder<ConfigClass, GenderEnum>(config, c => c.Gender, GenderEnum.Female),
        "Value");
    

    我尚未对其进行测试,但希望您能够修复任何错误并使用此解决方案。

    GetPropertyInfo():复制自https://stackoverflow.com/a/672212/724944

    public PropertyInfo GetPropertyInfo<TSource, TProperty>(
        TSource source,
        Expression<Func<TSource, TProperty>> 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.ToString()));
    
        PropertyInfo propInfo = member.Member as PropertyInfo;
        if (propInfo == null)
            throw new ArgumentException(string.Format(
                "Expression '{0}' refers to a field, not a property.",
                propertyLambda.ToString()));
    
        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.ToString(),
                type));
    
        return propInfo;
    }
    

    【讨论】:

    • 我刚刚想出了一个非常接近这个的代码。不过谢谢!我将合并此GetPropertyInfo 方法,因为我没有检查我应该检查的所有内容,并更新问题以供进一步参考。现在我必须使边界双向。但这是另一个问题......
    猜你喜欢
    • 2016-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-21
    • 1970-01-01
    • 2012-06-10
    • 2010-10-03
    相关资源
    最近更新 更多