2009-09-23 79 views
6

這可能適用於其他地方,但在WinForms中,當我使用綁定時,我發現許多方法想要綁定屬性的名稱。例如:有沒有辦法使靜態的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"); 
    } 
} 

我一直對此產生的大問題是,「名稱」和「年齡」被指定爲字符串。這意味着如果有人重命名其中一個人的屬性,編譯器就沒有幫助。該代碼將很好地編譯,但綁定將被打破。

有沒有解決這個問題的標準方法,我錯過了?這感覺就像我需要一些關鍵字,也許稱爲stringof匹配現有的typeof。你可以使用它像:

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

stringof可以返回一些類,它具有用於獲取的完整路徑,路徑,或字符串的一部分屬性,以便你可以自己分析它。

是這樣的事情已經可以嗎?

+1

這不是動態的結合,許多C#,這是WinForms。 – 2009-09-23 15:12:13

+0

是的。即使您不使用WinForms,這種語言功能也可能派上用場。 – 2009-09-23 15:26:45

回答

2

您可以使用表達式來獲得編譯器檢查的綁定。 例如,在當前的項目之一,我們建立綁定像這樣:

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

代碼支持這種風格被分爲幾類:

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

你可以使用反射來查找名稱;-)

當然,這將是一個循環引用,你應該使用名稱,你認爲它是要找到相同的名稱(或沒有發現什麼,這意味着該屬性被重新命名......但是有一個想法(或者說,一個技巧):通過對您希望使用的屬性做一個無所作爲的引用,您會得到編譯時確認它仍然存在。問題是如果有人只是交換各種屬性名稱;在這種情況下,名稱仍然存在(沒有編譯時錯誤),但具有不同的應用程序級別的語義(應用程序的輸出中可能會出現的驚喜)

4

看看這code snippet我已經發布了另一個問題,它可以幫助你! (但是,只有,如果你使用的是.NET 3.5)

問候
奧利弗Hanappi

4

你可以做到這一點與表達式樹,如解釋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));