Would like to display extracted data from xml file on property grid. XML file looks something like this:
<?xml version="1.0" encoding="utf-8" ?>
<customer>
<cust id="1">
<id>120</id>
<name>hello</name>
</cust>
<cust id="2">
<name>hello12</name>
<id>12012</id>
</cust>
</customer>
Now I want to extract data from XML on a property grid with server id as category(i.e it has to display cust id="1" in one category and cust id="2" in second category)
Try this code sample:
[TypeConverter(typeof(CustomerObjectConverter))]
public class Customer
{
internal readonly int ServerId;
public int Id { get; set; }
public string Name { get; set; }
public Customer(int serverId)
{
ServerId = serverId;
}
public override string ToString()
{
return Id + ", " + Name;
}
}
public class CustomerCollection : CollectionBase, ICustomTypeDescriptor
{
public CustomerCollection()
{
}
public CustomerCollection(IEnumerable<Customer> collection)
{
foreach (var item in collection)
Add(item);
}
public void Add(Customer emp)
{
List.Add(emp);
}
public void Remove(Customer emp)
{
List.Remove(emp);
}
public Customer this[int index]
{
get { return (Customer)List[index]; }
}
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 object GetPropertyOwner(PropertyDescriptor pd)
{
return this;
}
public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
return GetProperties();
}
public PropertyDescriptorCollection GetProperties()
{
PropertyDescriptorCollection pds = new PropertyDescriptorCollection(null);
for (int i = 0; i < List.Count; i++)
{
CustomersCollectionPropertyDescriptor pd = new CustomersCollectionPropertyDescriptor(this, i);
pds.Add(pd);
}
return pds;
}
}
internal class CustomersCollectionPropertyDescriptor : PropertyDescriptor
{
private readonly CustomerCollection collection;
private readonly int index = -1;
public CustomersCollectionPropertyDescriptor(CustomerCollection coll, int idx)
: base("#" + idx.ToString(), null)
{
collection = coll;
index = idx;
}
public override AttributeCollection Attributes
{
get { return new AttributeCollection(null); }
}
public override string Category
{
get { return "Server " + collection[index].ServerId; }
}
public override bool CanResetValue(object component)
{
return true;
}
public override Type ComponentType
{
get { return collection.GetType(); }
}
public override string DisplayName
{
get { return "Customer " + (index + 1); }
}
public override string Description
{
get { return "Customer"; }
}
public override object GetValue(object component)
{
return collection[index];
}
public override bool IsReadOnly
{
get { return true; }
}
public override string Name
{
get { return "#" + index.ToString(); }
}
public override Type PropertyType
{
get { return collection[index].GetType(); }
}
public override void ResetValue(object component) {}
public override bool ShouldSerializeValue(object component)
{
return false;
}
public override void SetValue(object component, object value)
{
}
}
internal class CustomerObjectConverter : ExpandableObjectConverter
{
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
{
return false;
}
public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
{
return destinationType == typeof(string) && value is Customer
? value.ToString()
: base.ConvertTo(context, culture, value, destinationType);
}
}
Usage. Paste the following code into constructor of the form containing PropertyGrid with the name propertyGrid:
const string xmlString = "<customer><cust id=\"1\"><id>120</id><name>hello</name></cust><cust id=\"2\"><name>hello12</name><id>12012</id></cust></customer>";
XmlDocument xml = new XmlDocument();
xml.LoadXml(xmlString);
XmlNode root = xml["customer"];
CustomerCollection customers = new CustomerCollection();
foreach (XmlNode node in root.ChildNodes)
customers.Add(new Customer(int.Parse(node.Attributes["id"].Value))
{
Id = int.Parse(node["id"].InnerText),
Name = node["name"].InnerText
});
propertyGrid.SelectedObject = customers;
Related
I've implemented a XmlReader class inheritor. My new class provides an xml data such as:
<?xml version="1.0" encoding="UTF-8"?>
<root>
<child xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="" />
<child xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="" />
</root>
The problem is that one namespace declaration is duplicated multiple times. This namespace declarations is caused because when the reader reached "type" attribute its properties have the following values: LocalName = "type", Prefix = "xsi" and NamespaceURI = "http://www.w3.org/2001/XMLSchema-instance". I want to prevent this duplications by declaring napespace in "root" node. Is it possible?
Here is example of my code:
using System;
using System.Collections;
using System.Collections.Generic;
using System.Xml;
using System.IO;
namespace Test
{
internal class Node
{
public string LocalName;
public string Prefix;
public string NamespaceURI;
public string Value;
public XmlNodeType NodeType;
}
public class MyXmlReader : XmlReader
{
private readonly Queue<Node> _data;
private string _localName;
private string _prefix;
private string _namespaceURI;
private string _value;
private XmlNodeType _nodeType;
private ReadState _readState;
public MyXmlReader()
{
_data = new Queue<Node>();
_data.Enqueue(new Node { LocalName = "root", NodeType = XmlNodeType.Element });
_data.Enqueue(new Node { LocalName = "child", NodeType = XmlNodeType.Element });
_data.Enqueue(new Node
{
LocalName = "type",
Prefix = "xsi",
NamespaceURI = "http://www.w3.org/2001/XMLSchema-instance",
NodeType = XmlNodeType.Attribute
});
_data.Enqueue(new Node { LocalName = "child", NodeType = XmlNodeType.Element });
_data.Enqueue(new Node
{
LocalName = "type",
Prefix = "xsi",
NamespaceURI = "http://www.w3.org/2001/XMLSchema-instance",
NodeType = XmlNodeType.Attribute
});
_data.Enqueue(new Node { NodeType = XmlNodeType.EndElement });
_readState = ReadState.Initial;
}
public override bool Read()
{
if (_data.Count > 0)
{
Node node = _data.Dequeue();
_localName = node.LocalName;
_prefix = node.Prefix;
_namespaceURI = node.NamespaceURI;
_value = node.Value;
_nodeType = node.NodeType;
return true;
}
return false;
}
public override void Close()
{
_readState = ReadState.Closed;
}
public override bool MoveToFirstAttribute()
{
return MoveToNextAttribute();
}
public override bool MoveToNextAttribute()
{
if (_data.Peek().NodeType == XmlNodeType.Attribute)
return Read();
return false;
}
public override bool MoveToElement()
{
_nodeType = XmlNodeType.Element;
_localName = "child";
return true;
}
public override bool ReadAttributeValue()
{
return false;
}
public override XmlNodeType NodeType
{
get { return _nodeType; }
}
public override string LocalName
{
get { return _localName; }
}
public override string NamespaceURI
{
get { return _namespaceURI; }
}
public override string Prefix
{
get { return _prefix; }
}
public override string Value
{
get { return _value; }
}
public override string BaseURI
{
get { return "Test"; }
}
public override bool IsEmptyElement
{
get
{
if (_localName == "root")
return false;
return true;
}
}
public override ReadState ReadState
{
get { return _readState; }
}
public override bool EOF
{
get { throw new NotImplementedException(); }
}
public override int Depth
{
get { throw new NotImplementedException(); }
}
public override string GetAttribute(string name)
{
throw new NotImplementedException();
}
public override string GetAttribute(string name, string namespaceURI)
{
throw new NotImplementedException();
}
public override string GetAttribute(int i)
{
throw new NotImplementedException();
}
public override bool MoveToAttribute(string name)
{
throw new NotImplementedException();
}
public override bool MoveToAttribute(string name, string ns)
{
throw new NotImplementedException();
}
public override string LookupNamespace(string prefix)
{
throw new NotImplementedException();
}
public override void ResolveEntity()
{
throw new NotImplementedException();
}
public override int AttributeCount
{
get { throw new NotImplementedException(); }
}
public override XmlNameTable NameTable
{
get { throw new NotImplementedException(); }
}
}
public class Program
{
public static void Main()
{
XmlDocument doc = new XmlDocument();
doc.Load(new MyXmlReader());
return;
}
}
}
I know some nasty and tricky approach: generate some fake attribute for root in xsi namespace. Then xml data will looks like
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:fake="fake">
But I also have XSD validation and such approach is not situable for me (there is no definition of "fake" attr)
I searched this, but I didn't find the answer to this specific question (although there are many similar ones).
I have a Column class in my ViewModel like this:
public class Column
{
public string Header { get; set; }
public ObservableCollection<double> Data { get; private set; }
public DummyColumn()
{
Data = new ObservableCollection<double>();
}
}
In MyViewModel class itself, I have Columns property:
public class MyViewModel
{
public ObservableCollection<Column> { get; private set; }
public MyViewModel()
{
Columns = new ObservableCollection<DummyColumn>();
var c1 = new DummyColumn() { Header = "A" };
c1.Data.Add(5);
c1.Data.Add(6);
Columns.Add(c1);
var c2 = new DummyColumn() { Header = "B" };
c2.Data.Add(51);
c2.Data.Add(61);
Columns.Add(c2);
}
}
I want to bind the Columns property of the latter class to the columns of a DataGrid in view. For each Column instance, I want to show the Header property as the column's header and its Data property as cells' values. How can I do that?
I reached this goal using ideas from here and here. I implemented three classes: Table, Row and RowPropertyDescriptor in my VM. Here is the code:
class RowPropertyDescriptor : PropertyDescriptor
{
private int index;
public RowPropertyDescriptor(string name, int index)
: base(name, null)
{
this.index = index;
}
#region PropertyDescriptor
public override string DisplayName { get { return Name; } }
public override Type ComponentType { get { return typeof(double); } }
public override bool IsReadOnly { get { return false; } }
public override Type PropertyType { get { return typeof(double); } }
public override object GetValue(object component)
{
return ((Row)component)[index];
}
public override void SetValue(object component, object value)
{
((Row)component)[index] = (double)value;
}
public override bool CanResetValue(object component)
{
return false;
}
public override void ResetValue(object component)
{
}
public override bool ShouldSerializeValue(object component)
{
return false;
}
#endregion
}
class Row : DynamicObject
{
private Table table;
private int row;
public Row(Table namedArraysView, int row)
{
this.table = namedArraysView;
this.row = row;
}
public double this[int col]
{
get { return table.RawData[col].Data[row]; }
set { table.RawData[col].Data[row] = value; }
}
public override bool TrySetMember(SetMemberBinder binder, object value)
{
int idx;
bool found = table.PropertiesIndex.TryGetValue(binder.Name, out idx);
if (found)
{
try
{
this[idx] = Convert.ToDouble(value);
return true;
}
catch (Exception ex)
{
return false;
}
}
return base.TrySetMember(binder, value);
}
public override bool TryGetMember(GetMemberBinder binder, out object result)
{
int idx;
bool found = table.PropertiesIndex.TryGetValue(binder.Name, out idx);
if (found)
{
result = this[idx];
return true;
}
return base.TryGetMember(binder, out result);
}
public override IEnumerable<string> GetDynamicMemberNames()
{
return table.PropertyNames;
}
}
class Table : BindingList<Row>, ITypedList
{
public ObservableCollection<INamedArray> RawData { get; private set; }
internal List<string> PropertyNames { get; private set; }
internal Dictionary<string, int> PropertiesIndex { get; private set; }
public Table(ObservableCollection<INamedArray> headeredArrays)
{
bind(headeredArrays);
headeredArrays.CollectionChanged += (object sender, NotifyCollectionChangedEventArgs e) => { bind(headeredArrays); };
}
private void bind(ObservableCollection<INamedArray> headeredArrays)
{
Clear();
if (headeredArrays == null)
{
RawData = null;
PropertyNames = null;
PropertiesIndex = null;
return;
}
RawData = headeredArrays;
PropertyNames = RawData.Select(d => d.Name).ToList();
PropertiesIndex = new Dictionary<string, int>();
for (int i = 0; i < RawData.Count; i++)
PropertiesIndex.Add(RawData[i].Name, i);
int nRows = headeredArrays[0].Data.Count;
for (int i = 0; i < nRows; i++)
Add(new Row(this, i));
}
#region ITypedList
public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
{
var dynamicDescriptors = new List<PropertyDescriptor>();
if (this[0].GetDynamicMemberNames() == null) return new PropertyDescriptorCollection(new PropertyDescriptor[] { });
var memberNames = this[0].GetDynamicMemberNames().ToArray();
for (int i = 0; i < memberNames.Length; i++)
dynamicDescriptors.Add(new RowPropertyDescriptor(memberNames[i], i));
return new PropertyDescriptorCollection(dynamicDescriptors.ToArray());
}
public string GetListName(PropertyDescriptor[] listAccessors)
{
return null;
}
#endregion
}
Then, one can easily create a Table with passing his/her columns to this class. This Table can be visualised correctly in view. The only limitation is that binding is one way which is not very hard to work around.
let's imagine i have the following classes that i am not allowed to change:
public class BaseType
{
public UInt32 m_baseMember = 1;
public bool m_baseMemberBool = false;
}
public class ComposedType
{
public ComposedType()
{
m_baseData = new BaseType();
}
public UInt32 m_newMember = 2;
public BaseType m_baseData;
}
Now i want to edit those data by putting it in an PropertyGrid. I created two Wrapper classes like those ( http://msdn.microsoft.com/en-us/magazine/cc163816.aspx )
public class FieldsToPropertiesProxyTypeDescriptor : ICustomTypeDescriptor
{
private object _target;
// object to be described
public FieldsToPropertiesProxyTypeDescriptor(object target)
{
if (target == null)
throw new ArgumentNullException("target");
_target = target;
}
public object GetProxiedObject()
{
return _target;
}
object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)
{
return _target;
// properties belong to the target object
}
AttributeCollection ICustomTypeDescriptor.GetAttributes()
{
// Gets the attributes of the target object
return TypeDescriptor.GetAttributes(_target, true);
}
string ICustomTypeDescriptor.GetClassName()
{
// Gets the class name of the target object
return TypeDescriptor.GetClassName(_target, true);
}
string ICustomTypeDescriptor.GetComponentName()
{
return TypeDescriptor.GetComponentName(_target, true);
}
TypeConverter ICustomTypeDescriptor.GetConverter()
{
return TypeDescriptor.GetConverter(_target, true);
}
EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
{
return TypeDescriptor.GetDefaultEvent(_target, true);
}
PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
{
return TypeDescriptor.GetDefaultProperty(_target, true);
}
object ICustomTypeDescriptor.GetEditor(Type editorBaseType)
{
return TypeDescriptor.GetEditor(_target, editorBaseType);
}
EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
{
return TypeDescriptor.GetEvents(_target, attributes);
}
EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
{
return TypeDescriptor.GetEvents( _target );
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
{
return ((ICustomTypeDescriptor)this).GetProperties(null);
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties( Attribute[] attributes)
{
bool filtering = (attributes != null && attributes.Length > 0);
PropertyDescriptorCollection props = new PropertyDescriptorCollection(null);
foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(_target, attributes, true))
{
props.Add(prop);
}
foreach (FieldInfo field in _target.GetType().GetFields())
{
FieldPropertyDescriptor fieldDesc = new FieldPropertyDescriptor(field);
if (!filtering || fieldDesc.Attributes.Contains(attributes))
props.Add(fieldDesc);
}
return props;
}
}
public class FieldPropertyDescriptor : PropertyDescriptor
{
private FieldInfo _field;
public FieldPropertyDescriptor(FieldInfo field) : base(field.Name, (Attribute[])field.GetCustomAttributes(typeof(Attribute), true))
{
_field = field;
}
public FieldInfo Field
{
get { return _field; }
}
public override bool Equals(object obj)
{
FieldPropertyDescriptor other = obj as FieldPropertyDescriptor;
return other != null && other._field.Equals(_field);
}
public override int GetHashCode()
{
return _field.GetHashCode();
}
public override bool IsReadOnly
{
get { return false; }
}
public override AttributeCollection Attributes
{
get
{
if (_field.FieldType.IsClass || _field.FieldType.IsArray)
{
Attribute[] expandable = new Attribute[1];
expandable[0] = new ExpandableObjectAttribute();
return AttributeCollection.FromExisting(base.Attributes, expandable);
}
return base.Attributes;
}
}
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 _field.DeclaringType; }
}
public override Type PropertyType
{
get { return _field.FieldType; }
}
public override object GetValue(object component)
{
if (component is FieldsToPropertiesProxyTypeDescriptor)
{
FieldsToPropertiesProxyTypeDescriptor proxy = (FieldsToPropertiesProxyTypeDescriptor)component;
return _field.GetValue(proxy.GetProxiedObject());
}
return _field.GetValue(component);
}
public override void SetValue(object component, object value)
{
if (component is FieldsToPropertiesProxyTypeDescriptor)
{
FieldsToPropertiesProxyTypeDescriptor proxy = (FieldsToPropertiesProxyTypeDescriptor)component;
_field.SetValue(proxy.GetProxiedObject(), value);
OnValueChanged(proxy.GetProxiedObject(), EventArgs.Empty);
return;
}
_field.SetValue(component, value);
OnValueChanged(component, EventArgs.Empty);
}
}
I can see and edit the 'm_newMember' in the PropertyGrid but i need to wrap the access to 'm_baseData' via FieldsToPropertiesProxyTypeDescriptor. How could i achieve this. Or is there a better way to wrap fields into Properties?
You can change attributes of a given class at runtime without changing that class. So you could write a custom TypeConverter and set it to your classes, something like this:
TypeDescriptor.AddAttributes(typeof(ComposedType), new TypeConverterAttribute(typeof(FieldsExpandableObjectConverter)));
TypeDescriptor.AddAttributes(typeof(BaseType), new TypeConverterAttribute(typeof(FieldsExpandableObjectConverter)));
With the following TypeConverter (re-using your FieldDescriptor class):
public class FieldsExpandableObjectConverter : ExpandableObjectConverter
{
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
{
List<PropertyDescriptor> properties = new List<PropertyDescriptor>(base.GetProperties(context, value, attributes).OfType<PropertyDescriptor>());
if (value != null)
{
foreach (FieldInfo field in value.GetType().GetFields())
{
FieldPropertyDescriptor fieldDesc = new FieldPropertyDescriptor(field);
{
properties.Add(fieldDesc);
}
}
}
return new PropertyDescriptorCollection(properties.ToArray());
}
}
This is a variation of Marc Gravels's answer
I'd extent Marc Gravels's helpful example to view nested objects in a generic way. But System throws an System.Reflection.TargetException.
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows.Forms;
public class Author
{
public string LastName { get; set; }
public string FirstName { get; set; }
}
public class BookDetails
{
public string Title { get; set; }
public int TotalRating { get; set; }
public int Occurrence { get; set; }
public Author Author { get; set; }
}
//public class MyBindingSource<T> : BindingSource
class MyList<T> : List<T>, ITypedList
{
public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
{
var origProps = TypeDescriptor.GetProperties(typeof(T));
List<PropertyDescriptor> newProps = new List<PropertyDescriptor>(origProps.Count);
foreach (PropertyDescriptor prop in origProps)
{
var childProps = TypeDescriptor.GetProperties(prop.GetType());
if ((childProps.Count == 0) || isSimpleType(prop))
{
newProps.Add(prop);
}
else
{
foreach (PropertyDescriptor childProp in childProps)
{
newProps.Add(new MyPropertyDescriptor(prop, childProp));
}
}
}
return new PropertyDescriptorCollection(newProps.ToArray());
}
private static bool isSimpleType(PropertyDescriptor prop)
{
if (prop.PropertyType.BaseType == typeof(ValueType)) return true;
if (prop.PropertyType == typeof(string)) return true;
if (prop.PropertyType.BaseType != typeof(Object)) return true;
return false;
}
public string GetListName(PropertyDescriptor[] listAccessors)
{
return "";
}
}
class MyPropertyDescriptor : PropertyDescriptor
{
private static readonly Attribute[] nix = new Attribute[0];
private readonly PropertyDescriptor parent;
private readonly PropertyDescriptor child;
public MyPropertyDescriptor(PropertyDescriptor parent, PropertyDescriptor child)
: base(parent.Name + "." + child.Name, nix)
{
this.parent = parent;
this.child = child;
}
public override object GetValue(object component)
{
return child.GetValue(component);
}
public override Type PropertyType
{
get { return child.PropertyType; }
}
public override bool IsReadOnly
{
get { return true; }
}
public override void SetValue(object component, object value)
{
throw new NotSupportedException();
}
public override void ResetValue(object component)
{
throw new NotSupportedException();
}
public override bool CanResetValue(object component)
{
return false;
}
public override Type ComponentType
{
get { return parent.ComponentType; }
}
public override bool ShouldSerializeValue(object component)
{
return false;
}
}
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
var author1 = new Author() { FirstName = "Victor", LastName = "Hugo" };
var author2 = new Author() { FirstName = "Moore", LastName = "Thomas" };
var data = new MyList<BookDetails> {
new BookDetails { Title = "abc", TotalRating = 3, Occurrence = 2, Author = author1 },
new BookDetails { Title = "def", TotalRating = 3, Occurrence = 2, Author = author2 },
new BookDetails { Title = "ghi", TotalRating = 3, Occurrence = 2, Author = author1 },
new BookDetails { Title = "jkl", TotalRating = 3, Occurrence = 2, Author = author2 },
};
Application.Run(new Form
{
Controls = {
new DataGridView {
Dock = DockStyle.Fill,
DataSource = data
}
}
});
}
}
You should Change prop.GetType() to prop.PropertyType.
I found the solution by myself:
The error was in the MyPropertyDescriptor!
Correction:
class MyPropertyDescriptor : PropertyDescriptor
{
private static readonly Attribute[] nix = new Attribute[0];
private readonly PropertyDescriptor parent;
private readonly PropertyDescriptor child;
public MyPropertyDescriptor(PropertyDescriptor parent, PropertyDescriptor child)
: base(parent.Name + "." + child.Name, nix)
{
this.parent = parent;
this.child = child;
}
public override object GetValue(object component)
{
var temp = parent.GetValue(component);
if (temp == null) return null;
var temp2 = child.GetValue(temp);
return temp2;
}
public override Type PropertyType
{
get { return child.PropertyType; }
}
public override bool IsReadOnly
{
get { return true; }
}
public override void SetValue(object component, object value)
{
throw new NotSupportedException();
}
public override void ResetValue(object component)
{
throw new NotSupportedException();
}
public override bool CanResetValue(object component)
{
return false;
}
public override Type ComponentType
{
get { return parent.ComponentType; }
}
public override bool ShouldSerializeValue(object component)
{
return false;
}
}
I'd like to edit a list of key value(string, string) items using a propertygrid. When I use a Dictionary<string,string> as type the propertygrid will show a GUI, but it does not seem "enabled", ie. I can't add any items.
Is the Dictionary object supported, or is there any other object with which I could solve this problem?
I have done it following this code in the past:
class DictionaryPropertyGridAdapter : ICustomTypeDescriptor
{
IDictionary _dictionary;
public DictionaryPropertyGridAdapter(IDictionary d)
{
_dictionary = d;
}
public string GetComponentName()
{
return TypeDescriptor.GetComponentName(this, true);
}
public EventDescriptor GetDefaultEvent()
{
return TypeDescriptor.GetDefaultEvent(this, true);
}
public string GetClassName()
{
return TypeDescriptor.GetClassName(this, true);
}
public EventDescriptorCollection GetEvents(Attribute[] attributes)
{
return TypeDescriptor.GetEvents(this, attributes, true);
}
EventDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetEvents()
{
return TypeDescriptor.GetEvents(this, true);
}
public TypeConverter GetConverter()
{
return TypeDescriptor.GetConverter(this, true);
}
public object GetPropertyOwner(PropertyDescriptor pd)
{
return _dictionary;
}
public AttributeCollection GetAttributes()
{
return TypeDescriptor.GetAttributes(this, true);
}
public object GetEditor(Type editorBaseType)
{
return TypeDescriptor.GetEditor(this, editorBaseType, true);
}
public PropertyDescriptor GetDefaultProperty()
{
return null;
}
PropertyDescriptorCollection
System.ComponentModel.ICustomTypeDescriptor.GetProperties()
{
return ((ICustomTypeDescriptor)this).GetProperties(new Attribute[0]);
}
public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
ArrayList properties = new ArrayList();
foreach (DictionaryEntry e in _dictionary)
{
properties.Add(new DictionaryPropertyDescriptor(_dictionary, e.Key));
}
PropertyDescriptor[] props =
(PropertyDescriptor[])properties.ToArray(typeof(PropertyDescriptor));
return new PropertyDescriptorCollection(props);
}
}
class DictionaryPropertyDescriptor : PropertyDescriptor
{
IDictionary _dictionary;
object _key;
internal DictionaryPropertyDescriptor(IDictionary d, object key)
: base(key.ToString(), null)
{
_dictionary = d;
_key = key;
}
public override Type PropertyType
{
get { return _dictionary[_key].GetType(); }
}
public override void SetValue(object component, object value)
{
_dictionary[_key] = value;
}
public override object GetValue(object component)
{
return _dictionary[_key];
}
public override bool IsReadOnly
{
get { return false; }
}
public override Type ComponentType
{
get { return null; }
}
public override bool CanResetValue(object component)
{
return false;
}
public override void ResetValue(object component)
{
}
public override bool ShouldSerializeValue(object component)
{
return false;
}
}
private void Form1_Load(object sender, System.EventArgs e)
{
IDictionary d = new Hashtable();
d["Hello"] = "World";
d["Meaning"] = 42;
d["Shade"] = Color.ForestGreen;
propertyGrid1.SelectedObject = new DictionaryPropertyGridAdapter(d);
}
Worked well for us.
If you are trying to bind a class that contains a Dictionary property to a PropertyGrid, rather than binding the Dictionary itself directly, then here is a free (LGPL licensed) component that does exactly that: http://gendictedit.codeplex.com/