2011-04-13 68 views
1

我們的應用程序在特定結構中生成大量結果列表。問題是,如果我想在DataGrid中顯示它,我必須創建一個DataTable並將其設置爲將使用內存的網格的dataSource。因此,我創建了一個我創建的類(名爲myRow)並在myRow的結構中創建了一個BindingList,我將所有字段都作爲屬性指向實際結果列表中的值。但問題是用戶可以添加自定義列的結果列表;我無法動態更改myRow的屬性,而且我不想使用DataTable(因爲它將與我的實際結果重複),並且如果我直接在dataGrid中創建自定義列並將它們的值設置爲cell-單元格內存中網格的大小非常高(我認爲這是因爲逐個單元格的設置值導致單元格的屬性被保存爲每個單元格而不是更大的條件)。所以沒有人知道如何創建一個行的類與使用屬性作爲列不同的策略,以便我可以在運行時動態設置列的數量?網格數據綁定問題

+0

這是WinForms嗎? – Marcie 2011-04-13 20:49:32

+0

是的......其實是一個UltraGrid,它應該和普通網格一樣 – DeveloperInToronto 2011-04-14 12:40:52

回答

3

我認爲這可以通過使用TypeDescriptionProvider來完成。

壞消息是:我從來沒有這樣做,將無法提供太多的幫助

的好消息是:我已經在這裏找到一個例子:DataGridView not showing properites of objects which implement ICustomTypeDescriptor

//編輯

我使用的代碼(參見上面的鏈接)來構建的示例如何避免每個對象的字典...

public class myRow 
{ 
    //your data storage class ... 
    public string txt { get; set; } 
    public int id { get; set; } 
} 
public class MyView:ICustomTypeDescriptor 
{//your extendable view class ... 
    private static PropertyDescriptorCollection props = null; 
    static MyView() 
    { 
     TypeDescriptionProvider defaultProvider = TypeDescriptor.GetProvider(typeof(MyView)); 
     props = new PropertyDescriptorCollection(defaultProvider.GetTypeDescriptor(typeof(MyView)).GetProperties().Cast<PropertyDescriptor>().ToArray(), true); 
    } 

    public static void addProperty(string name, DataTable dt, Func<DataRow, object> getter, Action<DataRow, object> setter, Func<DataTable, MyView, DataRow> rowSelector, Type PropType) 
    { 
     List<PropertyDescriptor> tmp; 
     if (props != null) tmp = props.Cast<PropertyDescriptor>().ToList(); 
     else tmp = new List<PropertyDescriptor>(); 
     PropertyDescriptor pd = TypeDescriptor.CreateProperty(typeof(MyView), name, PropType, null); 
     pd = new MyViewPropertyDescriptor(pd, dt, getter, setter, rowSelector, PropType); 
     tmp.Add(pd); 
     props = new PropertyDescriptorCollection(tmp.ToArray(), true); 
    } 

    //the data storage obj this view is referencing 
    public myRow obj; 

    public string TXT { // view-member known at compile time 
     get { return obj.txt; } 
     set { obj.txt = value; } 
    } 

    internal class MyViewPropertyDescriptor : PropertyDescriptor 
    { // an example property descriptor that can link to data in a DataTable ... 
     DataTable dt; 
     Func<DataRow, object> getter; 
     Action<DataRow, object> setter; 
     Func<DataTable, MyView, DataRow> rowSelector; 
     Type type; 
     public MyViewPropertyDescriptor(PropertyDescriptor descr, DataTable dt, Func<DataRow, object> getter, Action<DataRow, object> setter, Func<DataTable, MyView, DataRow> rowSelector, Type PropType) 
      : base(descr) 
     { 
      this.dt = dt; // storage for additional data referenced by this property 
      this.getter = getter; //a getter that will take a DataRow, and extract the property value 
      this.setter = setter; //a setter that will take a DataRow and a value 
      this.rowSelector = rowSelector;//a row selector ... takes a dataset and the view object and has to return the assiciated datarow 
      this.type = PropType; // the type of this property 
     } 

     public override object GetValue(object component) 
     { 
      // using row selector and getter to return the current value ... you should add errorhandling here 
      return getter(rowSelector(dt, (MyView)component)); 
     } 
     public override void SetValue(object component, object value) 
     { // the setter ... needs errorhandling too 
      setter(rowSelector(dt, (MyView)component), value); 
     } 
     public override void ResetValue(object component) 
     { 

     } 
     public override bool CanResetValue(object component) 
     { 
      return false; 
     } 
     public override bool ShouldSerializeValue(object component) 
     { 
      return false; 
     } 
     public override Type PropertyType 
     { 
      get { return type; } 
     } 
     public override bool IsReadOnly 
     { 
      get { return false; } 
     } 
     public override Type ComponentType 
     { 
      get { return typeof(MyView); } 
     } 

    } 

    ICustomTypeDescriptor defaultDescriptor = TypeDescriptor.GetProvider(typeof(MyView)).GetTypeDescriptor(typeof(MyView)); 
    AttributeCollection ICustomTypeDescriptor.GetAttributes() 
    { 
     return defaultDescriptor.GetAttributes(); 
    } 
    string ICustomTypeDescriptor.GetClassName() 
    { 
     return defaultDescriptor.GetClassName(); 
    } 
    string ICustomTypeDescriptor.GetComponentName() 
    { 
     return defaultDescriptor.GetComponentName(); 
    } 
    TypeConverter ICustomTypeDescriptor.GetConverter() 
    { 
     return defaultDescriptor.GetConverter(); 
    } 
    EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() 
    { 
     return defaultDescriptor.GetDefaultEvent(); 
    } 
    PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() 
    { 
     return defaultDescriptor.GetDefaultProperty(); 
    } 
    object ICustomTypeDescriptor.GetEditor(Type editorBaseType) 
    { 
     return defaultDescriptor.GetEditor(editorBaseType); 
    } 
    EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) 
    { 
     return defaultDescriptor.GetEvents(attributes); 
    } 
    EventDescriptorCollection ICustomTypeDescriptor.GetEvents() 
    { 
     return defaultDescriptor.GetEvents(); 
    } 
    PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) 
    { 
     return props; // should really be filtered, but meh! 
    } 
    PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() 
    { 
     return props; 
    } 
    object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) 
    { 
     return this; 
    } 

} 

和一個小例子,它利用這個......

private void button1_Click(object sender, EventArgs e) 
{ 
    if (dataGridView1.DataSource == null) 
    { 
     List<myRow> data = new List<myRow>(); 
     data.Add(new myRow { id = 1, txt = "test 1" }); 
     data.Add(new myRow { id = 2, txt = "test 2" }); 
     data.Add(new myRow { id = 3, txt = "test 3" }); 
     DataTable dt = new DataTable(); 
     dt.Columns.Add("id", typeof(int)); 
     dt.Columns.Add("additionalData1", typeof(int)); 
     dt.Columns.Add("additionalData2", typeof(int)); 
     Random rnd = new Random(); 
     foreach (var item in data) 
     { 
      dt.Rows.Add(new object[] { item.id, rnd.Next(), rnd.Next() }); 
     } 
     MyView.addProperty("additionalData1", dt, row => row["additionalData1"], (row, val) => row["additionalData1"] = val, (tab, v) => tab.Rows.OfType<DataRow>().First(x => x["id"].Equals(v.obj.id)), typeof(int)); 
     MyView.addProperty("additionalData2", dt, row => row["additionalData2"], (row, val) => row["additionalData2"] = val, (tab, v) => tab.Rows.OfType<DataRow>().First(x => x["id"].Equals(v.obj.id)), typeof(int)); 

     dataGridView1.DataSource = new BindingList<MyView>(data.Select(x => new MyView { obj = x }).ToList()); 
    } 
} 

當然,你會想提供更好的rowSelector或你想要一個哈希表或者其他數據結構...只是一個例子

替換數據表
+0

非常有幫助,謝謝 – DeveloperInToronto 2011-04-14 13:01:50

+0

好的,這裏是一個更新。我使用TypeDescriptionProvider來動態定義屬性;但使用它並不完全像常規屬性。它消耗了更大的內存;我想這是因爲我們必須將每個實例的屬性值保存在一個字典中,與常規屬性相比,這個實例的優化程度較低。 – DeveloperInToronto 2011-04-15 17:25:19

+0

我編輯了這篇文章,並添加了一個ICustomTypeDescriptor示例,該示例可以使用一個數據表作爲其他列的後備存儲。也許這有助於減少每個對象的開銷... – DarkSquirrel42 2011-04-15 22:12:54