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.
Has been working as a C# developer on contract for the last several years, including 3 years at Microsoft. Previously worked with Visual Basic and Microsoft Access VBA, and have developed code for Word, Excel and Outlook. Started working with WPF in 2007 when part of the Microsoft WPF team. For the last eight years has been working primarily as a senior WPF/C# and Silverlight/C# developer. Currently working as WPF developer with BioNano Genomics in San Diego, CA redesigning their UI for their camera system. he can be reached at qck1@hotmail.com.