2009-09-23 33 views
6

To prawdopodobnie dotyczy innych miejsc, ale w WinForms, kiedy używam wiązania, znajduję wiele metod, które chcą wziąć nazwę właściwości, z którą się wiąże. Coś jak:Czy istnieje sposób statycznego wykonywania wiązania C#?

class Person 
{ 
    public String Name { get { ... } set { ... } } 
    public int Age { get { ... } set { ... } } 
} 

class PersonView 
{ 
    void Bind(Person p) 
    { 
     nameControl.Bind(p,"Name"); 
     ageControl.Bind(p,"Age"); 
    } 
} 

Dużym problemem Wciąż mając z tego jest to, że „Nazwa” i „Wiek” są określone jako ciągi. Oznacza to, że kompilator nie pomoże, jeśli ktoś zmieni jedną z właściwości danej osoby. Kod zostanie skompilowany poprawnie, ale powiązania zostaną zerwane.

Czy istnieje standardowy sposób rozwiązania tego, co przeoczyłem? Wydaje mi się, że potrzebuję jakiegoś słowa kluczowego, może nazywać się stringof, aby pasowało do istniejącego typeof. Można go używać coś takiego:

ageControl.Bind(p,stringof(p.Age).Name); 

stringof mógł wrócić jakąś klasę, która ma właściwości dla uzyskania pełnej ścieżki, część ścieżki lub łańcucha, dzięki czemu można analizować go samemu.

Czy coś takiego można już zrobić?

+1

nie jest to C#, który wiąże się dynamicznie, że dużo, to są WinForm. –

+0

Prawda. Tego rodzaju funkcja języka może się przydać, nawet jeśli nie korzystasz z WinForm. –

Odpowiedz

2

Można użyć wyrażeń, aby uzyskać sprawdzane kompilacje. Na przykład, w jednym z bieżących projektów możemy skonfigurować powiązania tak:

DataBinder 
    .BindToObject(this) 
    .ObjectProperty(c => c.IsReadOnly) 
     .Control(nameTextBox, n => n.ReadOnly) 
     .Control(addressControl, n => n.ReadOnly) 

Kodeks wspiera ten styl jest podzielone na kilka klas:

public static class DataBinder 
{ 
    public static DataBinderBindingSourceContext<TDataSource> BindToObject<TDataSource>(TDataSource dataSource) 
    { 
     return new DataBinderBindingSourceContext<TDataSource>(dataSource); 
    } 
} 

public class DataBinderBindingSourceContext<TDataSource> 
{ 
    public readonly object DataSource; 

    public DataBinderBindingSourceContext(object dataSource) 
    { 
     DataSource = dataSource; 
    } 

    public DataBinderControlContext<TDataSource, TProperty> ObjectProperty<TProperty>(Expression<Func<TDataSource, TProperty>> property) 
    { 
     return new DataBinderControlContext<TDataSource, TProperty>(this, property); 
    } 
} 

public class DataBinderControlContext<TDataSource, TProperty> 
{ 
    readonly DataBinderBindingSourceContext<TDataSource> BindingSourceContext; 
    readonly string ObjectProperty; 

    public DataBinderControlContext 
     (
      DataBinderBindingSourceContext<TDataSource> bindingSourceContext, 
      Expression<Func<TDataSource, TProperty>> objectProperty 
     ) 
    { 
     BindingSourceContext = RequireArg.NotNull(bindingSourceContext); 
     ObjectProperty = ExpressionHelper.GetPropertyName(objectProperty); 
    } 

    public DataBinderControlContext<TDataSource, TProperty> Control<TControl>(TControl control, Expression<Func<TControl, TProperty>> property) 
     where TControl : Control 
    { 
     var controlPropertyName = ExpressionHelper.GetPropertyName(property); 
     control.DataBindings.Add(controlPropertyName, BindingSourceContext.DataSource, ObjectProperty, true); 

     return this; 
    } 
} 

public static class ExpressionHelper 
{ 
    public static string GetPropertyName<TResult>(Expression<Func<TResult>> property) 
    { 
     return GetMemberNames(((LambdaExpression)property).Body).Skip(1).Join("."); 
    } 

    public static string GetPropertyName<T, TResult>(Expression<Func<T, TResult>> property) 
    { 
     return GetMemberNames(((LambdaExpression)property).Body).Join("."); 
    } 

    static IEnumerable<string> GetMemberNames(Expression expression) 
    { 
     if (expression is ConstantExpression || expression is ParameterExpression) 
      yield break; 

     var memberExpression = (MemberExpression)expression; 

     foreach (var memberName in GetMemberNames(memberExpression.Expression)) 
      yield return memberName; 

     yield return memberExpression.Member.Name; 
    } 
} 

public static class StringExtentions 
{ 
    public static string Join(this IEnumerable<string> values, string separator) 
    { 
     if (values == null) 
      return null; 

     return string.Join(separator, values.ToArray()); 
    } 
} 
0

Można użyć refleksji, aby znaleźć nazwę ;-)

To oczywiście byłoby odwołanie cykliczne, można użyć nazwy, które uważasz, że jest znaleźć taką samą nazwę (lub nie znaleźć niczego , co oznacza, że ​​właściwość została zmieniona ... Ale jest pewien pomysł (lub raczej sztuczka): dokonując odniesienia do nieruchomości, której chcesz użyć, otrzymasz potwierdzenie czasu kompilacji, że nadal istnieje. Problem polega na tym, że ktoś wymienia po prostu różne nazwy właściwości, w tym przypadku nazwy nadal istnieją (bez błędu podczas kompilacji), ale mają różną semantykę na poziomie aplikacji (możliwe niespodzianki w wynikach aplikacji)

4

Zobacz to code snippet Napisałem w innym pytaniu, może ci pomóc! (Ale tylko, jeśli używasz .NET 3.5)

Pozdrawiam
Oliver Hanappi

4

Można to zrobić z drzewami ekspresyjnych, jak wyjaśniono in this question

protected static string GetPropertyName<TSource, TResult>(Expression<Func<TSource, TResult>> expression) 
{ 
    if (expression.NodeType == ExpressionType.Lambda && expression.Body.NodeType == ExpressionType.MemberAccess) 
    { 
     PropertyInfo prop = (expression.Body as MemberExpression).Member as PropertyInfo; 
     if (prop != null) 
     { 
      return prop.Name; 
     } 
    } 
    throw new ArgumentException("expression", "Not a property expression"); 
} 

... 

ageControl.Bind(p, GetPropertyName((Person p) => p.Age)); 
Powiązane problemy