[英]Issue with Grid data binding
我們的應用程序會在特定結構中生成大量結果。 問題是,如果要在DataGrid中顯示它,則必須創建一個DataTable並將其設置為將使用內存的網格的dataSource。 因此,我創建了一個我創建的類的BindingList (稱為myRow ),並且在myRow的結構中,我擁有了所有需要的字段,這些字段作為屬性指向實際結果列表中的值。 但是問題在於用戶可以添加自定義列結果列表; 我無法動態更改myRow的屬性,並且我不想使用DataTable(因為它將與我的實際結果重復),並且如果我直接在dataGrid中創建自定義列並逐個設置它們的值,單元格,內存中網格的大小真的很高(我認為這是因為逐個單元格設置值會導致為每個單元格保存單元格的屬性,而不是更大的標准)。 那么,有誰知道我該如何使用不同於將屬性用作列的策略來創建行類,以便在運行時動態設置列數?
我認為可以通過使用TypeDescriptionProvider
來完成。
壞消息是:我以前從未做過,也無法提供太多幫助
好消息是:我在這里找到一個示例: DataGridView沒有顯示實現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或將數據表替換為Hashtable或所需的任何數據結構...僅是示例
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.