2014-01-14 46 views
2

我,因爲我可以,因爲我有一個很難找到關於這一主題的一般信息,並希望與SO社區分享我的發現儘可能多的解釋發佈此綁定。數據與複雜/嵌套的對象(C#)

數據綁定到複雜的對象的集合在C#通常不會允許從類內的嵌套的對象(一個或多個)讀取數據。一個例子是class A的一個實例的成員是class B的一個對象。如果需要時,收集/綁定源被用作數據源你的運氣而無需額外的工作或訪問原始類進行修改(在這種情況下B)從內對象屬性。

的問題是「數據綁定到UI對象時,從內部類如何使用數據,而無需訪問和修改原班?」

回答

5

在內部類的數據可以絕對數據綁定映射可以使用,但不是默認。處理此問題的最佳方法是設置PropertyDescriptorsTypeDescriptors。我會在下面解釋的方式是在大多通用的實現,但將允許到內部對象中的數據綁定訪問,而不需要原班或擴展的任何修改,以實現接口。如果你不是你正在使用的類的作者,或者你正在使用ORM映射類,那麼這很好。

有4份實現此解決方案:

  1. PropertyDescriptor類的擴展來訪問內對象
  2. CustomTypeDescriptor實施
  3. TypeDescriptonProvider實施
  4. 新創建的安裝我們需要訪問數據的類型。

PART 1 - 擴展PropertyDescriptor類:

爲了獲得我們需要得到他們的PropertyDescriptor S中的內部組件,其基本上使用的元數據訪問類的公共屬性。這可以通過擴展PropertyDescriptor來訪問子屬性來完成。另外,在這裏你可以實現如何讀寫這些對象,或者將它們設置爲只讀(就像我一樣)。

class SubPropertyDescriptor : PropertyDescriptor 
{ 
    private PropertyDescriptor _parent; 
    private PropertyDescriptor _child; 

    public SubPropertyDescriptor(PropertyDescriptor parent, PropertyDescriptor child, string propertyDescriptorName) 
     : base(propertyDescriptorName, null) 
    { 
     _child = child; 
     _parent = parent; 
    } 
    //in this example I have made this read-only, but you can set this to false to allow two-way data-binding 
    public override bool IsReadOnly{ get { return true; } } 
    public override void ResetValue(object component) { } 
    public override bool CanResetValue(object component){ return false; } 
    public override bool ShouldSerializeValue(object component){ return true;} 
    public override Type ComponentType{ get { return _parent.ComponentType; } } 
    public override Type PropertyType{ get { return _child.PropertyType; } } 
    //this is how the value for the property 'described' is accessed 
    public override object GetValue(object component) 
    { 
     return _child.GetValue(_parent.GetValue(component)); 
    } 
    /*My example has the read-only value set to true, so a full implementation of the SetValue() function is not necessary. 
    However, for two-day binding this must be fully implemented similar to the above method. */ 
    public override void SetValue(object component, object value) 
    { 
     //READ ONLY 
     /*Example: _child.SetValue(_parent.GetValue(component), value); 
      Add any event fires or other additional functions here to handle a data update*/ 
    } 
} 

2部分 - 實現一個CustomTypeDescriptor

CustomTypeDesciptor就是創建元數據標籤來使數據從內部對象結合。本質上,我們將創建'描述符字符串',鏈接到內部對象的類型屬性,然後將它們添加到父對象。用於內部對象的格式將如下"className_property"其中classname是內部對象的Type

class MyClassTypeDescriptors : CustomTypeDescriptor 
{ 
    Type typeProp; 

    public MyClassTypeDescriptors(ICustomTypeDescriptor parent, Type type) 
     : base(parent) 
    { 
     typeProp = type; 
    } 
    //This method will add the additional properties to the object. 
    //It helps to think of the various PropertyDescriptors are columns in a database table 
    public override PropertyDescriptorCollection GetProperties(Attribute[] attributes) 
    { 
     PropertyDescriptorCollection cols = base.GetProperties(attributes); 
     string propName = ""; //empty string to be populated later 
     //find the matching property in the type being called. 
     foreach (PropertyDescriptor col in cols) 
     { 
      if (col.PropertyType.Name == typeProp.Name) 
       propName = col.Name; 
     } 
     PropertyDescriptor pd = cols[propName]; 
     PropertyDescriptorCollection children = pd.GetChildProperties(); //expand the child object 

     PropertyDescriptor[] propDescripts = new PropertyDescriptor[cols.Count + children.Count]; 
     int count = cols.Count; //start adding at the last index of the array 
     cols.CopyTo(propDescripts, 0); 
     //creation of the 'descriptor strings' 
     foreach (PropertyDescriptor cpd in children) 
     { 
      propDescripts[count] = new SubPropertyDescriptor(pd, cpd, pd.Name + "_" + cpd.Name); 
      count++; 
     } 

     PropertyDescriptorCollection newCols = new PropertyDescriptorCollection(propDescripts); 
     return newCols; 
    } 
} 

在這一點上,我們現在有我們的'描述符字符串'來設置綁定到innre對象。的MyClass內部特性可以被稱爲像"MyOtherClass_Property1"和其他屬性可以被稱爲像往常一樣用自己的變量名"Property1"

3部分 - 實現一個TypeDescriptonProvider

這是最後一個自定義的一塊,我們需要創造。 A TypeDescriptionProvider是數據綁定對象將用於確定對象的屬性是什麼以及什麼用於在需要描述符時實際調用我們的CustomTypeDescriptor類的部分。這也是一個使用泛型的類,但實際上並不是泛型類,因爲我們必須將它連接到我們的外部對象(也就是所用集合的數據類型)。

class MyClassTypeDescProvider<T> : TypeDescriptionProvider 
{ 
    private ICustomTypeDescriptor td; 

    public DigiRecordBindingTypeDescProvider() 
     : this(TypeDescriptor.GetProvider(typeof(MyClass))) 
    { } 

    public MyClassTypeDescProvider(TypeDescriptionProvider parent) 
     : base(parent) 
    { } 

    public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance) 
    { 
     if (td == null) 
     { 
      td = base.GetTypeDescriptor(objectType, instance); 
      td = new MyClassTypeDescriptors(td, typeof(T)); 
     } 
     return td; 
    } 
} 

泛型類「T」是用來指定,我們需要鏈接到我們的父對象內對象屬性的Type。你會看到這是如何在下一步工作。

4部分 - 連接我們的供應商,以父類型:

現在,我們已經創建的基礎設施,以訪問存儲在內部屬性中的數據,我們必須告訴系統使用我們定製的提供者仰視當我們的TypeDescriptors。這是使用靜態方法來完成:

TypeDescriptor.AddProvider(provider,type) 

這應該針對每個內部Type,我們需要訪問內部的屬性來實現。例如,在設置UI對象的DataSource屬性時,應該在將數據綁定到綁定對象之前添加提供者。

IQueryable<MyClass> myData = PopulateCollectionWithData(); 
TypeDescriptor.AddProvider(new MyClassTypeDescProvider<MyOtherClass>(), typeof(MyClass)); 
TypeDescriptor.AddProvider(new MyClassTypeDescProvider<MyThirdClass>(), typeof(MyClass)); 
DataGridView1.DataSource = myData; //don't bind directly to a collection if you are doing two-way binding. Use a BindingSource instead! 

最後,如果由於某種原因,你需要刪除該供應商並恢復到默認,你可以反向執行完全相同的操作來:

TypeDescriptor.RemoveProvider(new MyClassTypeDescProvider<MyOtherClass>(), typeof(MyClass)); 
TypeDescriptor.RemoveProvider(new MyClassTypeDescProvider<MyThirdClass>(), typeof(MyClass)); 

有關更多信息,請參見TypeDescriptor Class - MSDNThe MSDN blog that put me on the right track。另外,在我對此的研究中,我偶然發現了this這個問題,這促使我發佈了一個完整的解釋,因爲它實際上只是要求回答這個問題的第4部分。我希望這能幫助別人,所以他們不需要深入挖掘System.ComponentModel圖書館,就像我不必要的那樣!