Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / Win32

Dynamically Bound Dictionary to Windows Grid

5.00/5 (8 votes)
2 Oct 2012CPOL1 min read 16.7K   165  
This article presents a way to display totally dynamic data in a windows grid using a dictionary.

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:

C#
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:

C++
 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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)