2012-04-03 3 views
1

Я не знаю, правильно ли я спросил, но я как бы потерялся часами, так голый со мной.Как динамически создавать привязывающие прокси?

Моя первоначальная проблема: у меня есть объект конфигурации, который имеет 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"); 

Это просто пример, я хотел бы сохранить его простым. Но этот подход фактически РАБОТЫ. На вопрос:

Я хочу инкапсулировать сравнение и установить выражения внутри производного класса.

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, используя тип объекта во время преобразования лямбда-выражения компилятор добавит вызов 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"); 
+0

Является ли это WinForms? или WPF? – surfen

+0

Это Windows Forms – svallory

ответ

2

Я думаю, что это может быть сделано с помощью PropertyInfo и некоторые лямбда магии:

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

Я просто придумал код, очень близкий к этому. Но спасибо! Я включу этот метод GetPropertyInfo, поскольку я не проверял все, что мне нужно, и обновил вопрос для дальнейшей справки. Теперь я должен сделать ограничивающий двухсторонний подход. Но это еще один вопрос ... – svallory

Смежные вопросы