I had wanted to bind a Windows form DataGridView
DataSource
to a list of Dictionary
types, and initially attempted to use a DynamicObject
as an adapter. This would have been nice because then I would just need to write the DynamicObject
code to look up in the dictionary. Never did get this to work. Think it is a serious oversight the Microsoft has not supported binding to DynamicObject
instances, but we have to live with it. I did implement the interface with a Table
, but this required conversion of the dictionary to fields in a row. Somebody pointed me to another solution, and it does have certain advantages, and probably a whole lot less overhead than using a table. It uses the ICustomTypeDescriptor
. The ICustomTypeDescriptor
provides an interface that supplies dynamic custom type information for an object.
All that is needed is to create a class that inherits from ICustomTypeDescriptor
, which provides a variable for the Dictionary
, and returns a customized PropertyDescriptor
that will look up the value in the dictionary of the class inheriting from ICustomTypeDescriptor
. The PropertyDescriptor
does pretty much all the work:
public class DictionaryTypeDescriptor : ICustomTypeDescriptor
{
private readonly Dictionary<string, object> _properties =
new Dictionary<string, object>();
public object this[string name]
{
get { return _properties.ContainsKey(name) ? _properties[name] : null; }
set { _properties[name] = value; }
}
#region ICustomTypeDescriptor Members
public AttributeCollection GetAttributes()
{
return AttributeCollection.Empty;
}
public string GetClassName()
{
return TypeDescriptor.GetClassName(this, true);
}
public string GetComponentName()
{
return TypeDescriptor.GetComponentName(this, true);
}
public TypeConverter GetConverter()
{
return null;
}
public EventDescriptor GetDefaultEvent()
{
return null;
}
public PropertyDescriptor GetDefaultProperty()
{
return null;
}
public object GetEditor(Type editorBaseType)
{
return null;
}
public EventDescriptorCollection GetEvents(Attribute[] attributes)
{
return EventDescriptorCollection.Empty;
}
public EventDescriptorCollection GetEvents()
{
return EventDescriptorCollection.Empty;
}
public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
{
return new PropertyDescriptorCollection(
_properties.Keys.Select(key => new DictionaryDescriptor(key)).ToArray());
}
public PropertyDescriptorCollection GetProperties()
{
return GetProperties(null);
}
public object GetPropertyOwner(PropertyDescriptor pd)
{
return this;
}
#endregion
class DictionaryDescriptor : PropertyDescriptor
{
public DictionaryDescriptor(string name) : base(name, null) { }
public override bool CanResetValue(object component)
{
return true;
}
public override Type ComponentType
{
get { return typeof(DictionaryTypeDescriptor); }
}
public override object GetValue(object component)
{
return ((DictionaryTypeDescriptor)component)[Name];
}
public override void SetValue(object component, object value)
{
((DictionaryTypeDescriptor)component)[Name] = value;
}
public override bool IsReadOnly
{
get { return false; }
}
public override Type PropertyType
{
get { return typeof(object); }
}
public override void ResetValue(object component)
{
((DictionaryTypeDescriptor)component)._properties.Remove(Name);
}
public override bool ShouldSerializeValue(object component)
{
return ((DictionaryTypeDescriptor)component)._properties.ContainsKey(Name);
}
}
}
Using this code is very simple. In the code behind the form I have the following:
public partial class DynamicallyBoundForm : Form
{
public DynamicallyBoundForm()
{
InitializeComponent();
var obj1 = new DictionaryTypeDescriptor();
obj1["test1"] = 100;
obj1["test2"] = 200;
obj1["test3"] = 300;
var objects = new List<DictionaryTypeDescriptor>() { obj1 };
dataGrid.DataSource = objects;
}
}
I made a few changes for this example from what I implemented: I made class completely dynamic so that fields could be added as needed. Also, in my code I provide a dictionary of Dictionary<string, string>
.
History
10/02/12: Added source code.