2011-04-13 67 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