c# - Trying to use DataGridView together with ICustomTypeDescriptor -
i'm trying use datagridview display list of objects. in class want present properties have c# properties , want create properties dynamically reasons.
here have example, works fine c# property (featureid) dynamically created property (name) returns value of first instance instances. why?
first class implements icustompropertydescriptor interface
public abstract class propertypresentationsubbase : icustomtypedescriptor { public string getclassname() { return typedescriptor.getclassname(this, true); } public attributecollection getattributes() { return typedescriptor.getattributes(this, true); } public string getcomponentname() { return typedescriptor.getcomponentname(this, true); } public typeconverter getconverter() { return typedescriptor.getconverter(this, true); } public eventdescriptor getdefaultevent() { return typedescriptor.getdefaultevent(this, true); } public propertydescriptor getdefaultproperty() { return typedescriptor.getdefaultproperty(this, true); } public object geteditor(type editorbasetype) { return typedescriptor.geteditor(this, editorbasetype, true); } public eventdescriptorcollection getevents(attribute[] attributes) { return typedescriptor.getevents(this, attributes, true); } public eventdescriptorcollection getevents() { return typedescriptor.getevents(this, true); } public virtual propertydescriptorcollection getproperties(attribute[] attributes) { propertydescriptorcollection rtn = typedescriptor.getproperties(this); //rtn = filterreadonly(rtn, attributes); return new propertydescriptorcollection(rtn.cast<propertydescriptor>().toarray()); } public virtual propertydescriptorcollection getproperties() { return typedescriptor.getproperties(this, true); } public object getpropertyowner(propertydescriptor pd) { return this; } [browsable(false)] public propertypresentationsubbase parent { { return m_parent; } set { m_parent = value; } } propertypresentationsubbase m_parent = null; [browsable(false)] public type valuetype { { return valuetype; } set { valuetype = value; } } private type valuetype = null; [browsable(false)] public string name { { return sname; } set { sname = value; } } public abstract object getvalue(); private string sname = string.empty; public abstract void change(object value); } }
i have class inherit propertydescriptor
public class mycustompropertydescriptor : propertydescriptor { propertypresentationsubbase m_property; public mycustompropertydescriptor(propertypresentationsubbase myproperty, attribute[] attrs, int propertyno) : base(myproperty.name + propertyno, attrs) { m_property = myproperty; } #region propertydescriptor specific public override bool canresetvalue(object component) { return false; } public override string name { { return "myname"; } } public override type componenttype { { return null; } } public override object getvalue(object component) { return m_property.getvalue(); } public override string description { { return "description"; } } public object value { { return m_property; } } public override string category { { return "category"; } } public override string displayname { { return m_property.name; } } public override bool isreadonly { { return false; } } public override void resetvalue(object component) { //have implement } public override bool shouldserializevalue(object component) { return false; } public override void setvalue(object component, object value) { m_property.change(value); } public override type propertytype { { if ((m_property != null) && (m_property.valuetype != null)) { return m_property.valuetype; } else { return system.type.missing.gettype(); } } } #endregion
}
a small class holds data:
public class quadrifeatureitem { public quadrifeatureitem(int featureid, string name) { m_featureid = featureid; m_name = name; } public int m_featureid; public string m_name; }
my class sent grid (containing both featureid property , dynamically created property)
class featurepropertypresentation : propertypresentationsubbase { public int featureid { { return m_feature.m_featureid; } set { m_feature.m_featureid = value; } } public featurepropertypresentation(quadrifeatureitem item) { m_feature = item; } private quadrifeatureitem m_feature; public override propertydescriptorcollection getproperties(attribute[] attributes) { propertydescriptorcollection rtn = base.getproperties(attributes); createnameattribute(ref rtn, attributes); return rtn; } private void createnameattribute(ref propertydescriptorcollection pdc, attribute[] attributes) { nameproperty namepres = null; namepres = new nameproperty(m_feature, this); pdc.add(new mycustompropertydescriptor(namepres, attributes, pdc.count)); } public override void change(object value) { throw new notimplementedexception(); } public override object getvalue() { return this; }
}
a class implements nameproperty:
class nameproperty : propertypresentationsubbase { public nameproperty(quadrifeatureitem feature, featurepropertypresentation parent) : base() { m_quadrifeatureitem = feature; parent = parent; valuetype = typeof(string); } private quadrifeatureitem m_quadrifeatureitem; public override void change(object value) { m_quadrifeatureitem.m_name = (string)value; } public override object getvalue() { return m_quadrifeatureitem.m_name; } }
and formcode:
public form1() { initializecomponent(); showgrid(); } private void showgrid() { quadrifeatureitem no1 = new quadrifeatureitem(1, "nummer1"); quadrifeatureitem no2 = new quadrifeatureitem(2, "nummer2"); quadrifeatureitem no3 = new quadrifeatureitem(3, "nummer3"); bindingsource source = new bindingsource(); featurepropertypresentation no1pres = new featurepropertypresentation(no1); featurepropertypresentation no2pres = new featurepropertypresentation(no2); featurepropertypresentation no3pres = new featurepropertypresentation(no3); source.add(no1pres); source.add(no2pres); source.add(no3pres); datagridview1.datasource = source; show(); }
but grid shows "nummer1" rows. why? use presentation classes in propertygrid , works fine. use mycustompropertydescriptor in propertygrid.
my wish able reuse presentationclasses , mycustompropertydescriptor in datagridview. possible modification in mycustompropertydescriptor or propertypresentationsubbase?
the problem custom property descriptor bound concrete instance. works when use single item data binding (like textbox
object property or selecting object in propertygrid
control). however, when use control requires list data binding (like datagridview
, listview
, listbox
, combobox
list etc.) technique doesn't work. in order auto populate columns, datagridview
needs set of properties common items. in order that, tries several ways obtain information (a explanation can found here datagridview not showing properites of objects implement icustomtypedescriptor), , 1 of them take first item of list , ask properties (hence debugging experience). anyway, in order make work in list binding scenarios, property descriptor needs implemented differently.
notice signature of propertydescriptor
s getvalue/setvalue
methods. both have argument object component
. object instance need return or set value. can think of property descriptor being inverse of use in programming language. instead of
var val = obj.property; obj.property = val;
we have
var val = propertydescriptor.getvalue(obj); propertydescriptor.setvalue(obj, val);
in other words, should not "embed" object instance inside property descriptor, use passed argument.
here sample generic implementation of property descriptor doing that:
public class simplepropertydescriptor<tcomponent, tvalue> : propertydescriptor tcomponent : class { private readonly func<tcomponent, tvalue> getvalue; private readonly action<tcomponent, tvalue> setvalue; private readonly string displayname; public simplepropertydescriptor(string name, attribute[] attrs, func<tcomponent, tvalue> getvalue, action<tcomponent, tvalue> setvalue = null, string displayname = null) : base(name, attrs) { debug.assert(getvalue != null); this.getvalue = getvalue; this.setvalue = setvalue; this.displayname = displayname; } public override string displayname { { return displayname ?? base.displayname; } } public override type componenttype { { return typeof(tcomponent); } } public override bool isreadonly { { return setvalue == null; } } public override type propertytype { { return typeof(tvalue); } } public override bool canresetvalue(object component) { return false; } public override bool shouldserializevalue(object component) { return false; } public override void resetvalue(object component) { } public override object getvalue(object component) { return getvalue((tcomponent)component); } public override void setvalue(object component, object value) { setvalue((tcomponent)component, (tvalue)value); } }
sample usage stuff:
public override propertydescriptorcollection getproperties(attribute[] attributes) { var properties = base.getproperties(attributes); // custom name property properties.add(new simplepropertydescriptor<featurepropertypresentation, string>("featurename", attributes, getvalue: component => component.m_feature.m_name, setvalue: (component, value) => component.m_feature.m_name = value, // remove line make readonly displayname: "feature name" )); return properties; }
and, putting together, small example equivalent yours:
using system; using system.componentmodel; using system.diagnostics; using system.linq; using system.windows.forms; namespace samples { // generic implemenation of property descriptor public class simplepropertydescriptor<tcomponent, tvalue> : propertydescriptor tcomponent : class { private readonly func<tcomponent, tvalue> getvalue; private readonly action<tcomponent, tvalue> setvalue; private readonly string displayname; public simplepropertydescriptor(string name, attribute[] attrs, func<tcomponent, tvalue> getvalue, action<tcomponent, tvalue> setvalue = null, string displayname = null) : base(name, attrs) { debug.assert(getvalue != null); this.getvalue = getvalue; this.setvalue = setvalue; this.displayname = displayname; } public override string displayname { { return displayname ?? base.displayname; } } public override type componenttype { { return typeof(tcomponent); } } public override bool isreadonly { { return setvalue == null; } } public override type propertytype { { return typeof(tvalue); } } public override bool canresetvalue(object component) { return false; } public override bool shouldserializevalue(object component) { return false; } public override void resetvalue(object component) { } public override object getvalue(object component) { return getvalue((tcomponent)component); } public override void setvalue(object component, object value) { setvalue((tcomponent)component, (tvalue)value); } } // stuff public abstract class propertypresentationsubbase : icustomtypedescriptor { public string getclassname() { return typedescriptor.getclassname(this, true); } public attributecollection getattributes() { return typedescriptor.getattributes(this, true); } public string getcomponentname() { return typedescriptor.getcomponentname(this, true); } public typeconverter getconverter() { return typedescriptor.getconverter(this, true); } public eventdescriptor getdefaultevent() { return typedescriptor.getdefaultevent(this, true); } public propertydescriptor getdefaultproperty() { return typedescriptor.getdefaultproperty(this, true); } public object geteditor(type editorbasetype) { return typedescriptor.geteditor(this, editorbasetype, true); } public eventdescriptorcollection getevents(attribute[] attributes) { return typedescriptor.getevents(this, attributes, true); } public eventdescriptorcollection getevents() { return typedescriptor.getevents(this, true); } public virtual propertydescriptorcollection getproperties(attribute[] attributes) { propertydescriptorcollection rtn = typedescriptor.getproperties(this); //rtn = filterreadonly(rtn, attributes); return new propertydescriptorcollection(rtn.cast<propertydescriptor>().toarray()); } public virtual propertydescriptorcollection getproperties() { return typedescriptor.getproperties(this, true); } public object getpropertyowner(propertydescriptor pd) { return this; } [browsable(false)] public propertypresentationsubbase parent { { return m_parent; } set { m_parent = value; } } propertypresentationsubbase m_parent = null; [browsable(false)] public type valuetype { { return valuetype; } set { valuetype = value; } } private type valuetype = null; [browsable(false)] public string name { { return sname; } set { sname = value; } } public abstract object getvalue(); private string sname = string.empty; public abstract void change(object value); } public class quadrifeatureitem { public quadrifeatureitem(int featureid, string name) { m_featureid = featureid; m_name = name; } public int m_featureid; public string m_name; } class featurepropertypresentation : propertypresentationsubbase { public int featureid { { return m_feature.m_featureid; } set { m_feature.m_featureid = value; } } public featurepropertypresentation(quadrifeatureitem item) { m_feature = item; } private quadrifeatureitem m_feature; public override propertydescriptorcollection getproperties(attribute[] attributes) { var properties = base.getproperties(attributes); // custom name property properties.add(new simplepropertydescriptor<featurepropertypresentation, string>("featurename", attributes, getvalue: component => component.m_feature.m_name, setvalue: (component, value) => component.m_feature.m_name = value, // remove line make readonly displayname: "feature name" )); return properties; } public override void change(object value) { throw new notimplementedexception(); } public override object getvalue() { return this; } } // test static class test { [stathread] static void main() { application.enablevisualstyles(); application.setcompatibletextrenderingdefault(false); var dataset = enumerable.range(1, 10).select(n => new featurepropertypresentation(new quadrifeatureitem(n, "nummer" + n))).tolist(); var form = new form(); var dg = new datagridview { dock = dockstyle.fill, parent = form }; dg.datasource = dataset; application.run(form); } } }
result:
Comments
Post a Comment