2009-09-23 13 views
6

Dies gilt wahrscheinlich für andere Orte, aber in WinForms, wenn ich Binding verwende finde ich viele Methoden wollen den Namen der Eigenschaft zu binden nehmen. Etwas wie:Gibt es eine Möglichkeit, die C# -Bindung statisch zu machen?

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

Das große Problem, das ich mit diesem halten ist, dass „Name“ und „Alter“ als Zeichenkette angegeben. Dies bedeutet, dass der Compiler keine Hilfe bietet, wenn jemand eine der Eigenschaften von Person umbenennt. Der Code wird gut kompiliert, aber die Bindungen werden unterbrochen.

Gibt es eine Standardlösung, die ich verpasst habe? Es fühlt sich an, als ob ich ein Schlüsselwort benötige, vielleicht auch stringof, um mit dem vorhandenen Typ übereinzustimmen. Man kann es benutzen so etwas wie:

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

stringof könnte eine Klasse zurück, die Eigenschaften für immer den vollständigen Pfad, einen Teil des Weges hat, oder die Zeichenfolge, so dass Sie es selbst bis analysieren können.

Ist so etwas schon machbar?

+1

Dies ist nicht C#, die viel dynamisch, dass bindet, diese sind WinForms. –

+0

Wahr. Diese Art von Sprachfeature kann jedoch nützlich sein, auch wenn Sie keine WinForms verwenden. –

Antwort

2

erklärt können Sie Ausdrücke benutzen Sie Compiler-geprüfte Bindungen. Zum Beispiel in einem der aktuellen Projekte setzen wir Bindungen wie auf den Punkt:

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

-Code diese Art Unterstützung in mehrere Klassen unterteilt:

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

Sie Reflektion verwenden könnten, den Namen zu finden ;-)

Dies würde natürlich einen zirkulären Verweis sein, dann würden Sie den Namen verwenden, den Sie denken, es ist mit dem gleichen Namen zu finden (oder auch nicht, etwas zu finden Das bedeutet, dass die Eigenschaft umbenannt wurde ... Aber es gibt eine Idee (oder besser gesagt einen Trick): Wenn Sie einen Verweis auf die Eigenschaft machen, die Sie verwenden möchten, erhalten Sie eine Kompilierzeit-Bestätigung, dass sie noch vorhanden ist Problem ist, wenn jemand nur verschiedene Eigenschaftsnamen umtauscht, in diesem Fall sind die Namen noch vorhanden (kein Kompilierzeitfehler), aber unterschiedliche Semantik auf Anwendungsebene (mögliche Überraschungen in der Ausgabe der Anwendung)

4

Werfen Sie einen Blick auf dies code snippet Ich habe in einer anderen Frage geschrieben, es kann Ihnen helfen! (Aber nur, wenn Sie mit .NET 3.5)

Mit freundlichen Grüßen
Oliver Hanappi

4

Sie können das tun, mit Ausdruck Bäume, wie 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)); 
Verwandte Themen