65.9K
CodeProject is changing. Read more.
Home

Dynamically Bound Dictionary to Windows Grid

starIconstarIconstarIconstarIconstarIcon

5.00/5 (8 votes)

Aug 15, 2012

CPOL

1 min read

viewsIcon

16805

downloadIcon

169

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:

  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.