title: PropertyGrid expandable collection
body: <p>I want to automatically show every <code>IList</code> as expandable in my <code>PropertyGrid</code> (By "expandable", I obviously mean that the items will be shown).
  I don't want to use attributes on each list (Once again, I want it to work for EVERY <code>IList</code>)</p>
  
  <p>I tried to achive it by using a custom <code>PropertyDescriptor</code> and an <code>ExpandableObjectConverter</code>. It works, but after I delete items from the list, the <code>PropertyGrid</code> is not being refreshed, still displaying the deleted items.</p>
  
  <p>I tried to use <code>ObservableCollection</code> along with raising <code>OnComponentChanged</code>, and also <code>RefreshProperties</code> attribute, but nothing worked.</p>
  
  <p>This is my code:</p>
  
  <pre><code>public class ExpandableCollectionPropertyDescriptor : PropertyDescriptor
  {
      private IList _collection;
  
      private readonly int _index = -1;
  
      internal event EventHandler RefreshRequired;
  
      public ExpandableCollectionPropertyDescriptor(IList coll, int idx) : base(GetDisplayName(coll, idx), null)
      {
          _collection = coll
          _index = idx;
      }
  
      public override bool SupportsChangeEvents
      {
          get { return true; }
      }
  
      private static string GetDisplayName(IList list, int index)
      {
  
          return "[" + index + "]  " + CSharpName(list[index].GetType());
      }
  
      private static string CSharpName(Type type)
      {
          var sb = new StringBuilder();
          var name = type.Name;
          if (!type.IsGenericType)
              return name;
          sb.Append(name.Substring(0, name.IndexOf('`')));
          sb.Append("&lt;");
          sb.Append(string.Join(", ", type.GetGenericArguments()
                                          .Select(CSharpName)));
          sb.Append("&gt;");
          return sb.ToString();
      }
  
      public override AttributeCollection Attributes
      {
          get 
          { 
              return new AttributeCollection(null);
          }
      }
  
      public override bool CanResetValue(object component)
      {
  
          return true;
      }
  
      public override Type ComponentType
      {
          get 
          { 
              return _collection.GetType();
          }
      }
  
      public override object GetValue(object component)
      {
          OnRefreshRequired();
  
          return _collection[_index];
      }
  
      public override bool IsReadOnly
      {
          get { return false;  }
      }
  
      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 true;
      }
  
      public override void SetValue(object component, object value)
      {
           _collection[_index] = value;
      }
  
      protected virtual void OnRefreshRequired()
      {
          var handler = RefreshRequired;
          if (handler != null) handler(this, EventArgs.Empty);
      }
  }
  </code></pre>
  
  <p>.</p>
  
  <pre><code>internal class ExpandableCollectionConverter : ExpandableObjectConverter
  {
      public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destType)
      {
          if (destType == typeof(string))
          {
              return "(Collection)";
          }
          return base.ConvertTo(context, culture, value, destType);
      }
  
      public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
      {
          IList collection = value as IList;
          PropertyDescriptorCollection pds = new PropertyDescriptorCollection(null);
  
          for (int i = 0; i &lt; collection.Count; i++)
          {
              ExpandableCollectionPropertyDescriptor pd = new ExpandableCollectionPropertyDescriptor(collection, i);
              pd.RefreshRequired += (sender, args) =&gt;
              {
                  var notifyValueGivenParentMethod = context.GetType().GetMethod("NotifyValueGivenParent", BindingFlags.NonPublic | BindingFlags.Instance);
                  notifyValueGivenParentMethod.Invoke(context, new object[] {context.Instance, 1});
              };
              pds.Add(pd);
          }
          // return the property descriptor Collection
          return pds;
      }
  }
  </code></pre>
  
  <p>And I use it for all <code>IList</code>s with the following line:</p>
  
  <pre><code>TypeDescriptor.AddAttributes(typeof (IList), new TypeConverterAttribute(typeof(ExpandableCollectionConverter)));
  </code></pre>
  
  <p><strong>Some Clarifications</strong></p>
  
  <p>I want the grid to automatically update when I change the list. Refreshing when another property changes, does not help.</p>
  
  <p>A solution that works, is a solution where:</p>
  
  <ol>
  <li>If you expand the list while it is empty, and then add items, the grid is refreshed with the items expanded</li>
  <li>If you add items to the list, expand it, and then remove items (without collapsing), the grid is refreshed with the items expanded, and not throwing <code>ArgumentOutOfRangeException</code> because it is trying to show items that were deleted already</li>
  <li>I want this whole thing for a configuration utility. Only the <code>PropertyGrid</code> should change the collections</li>
  </ol>
  
  <p><strong>IMPORTANT EDIT:</strong></p>
  
  <p>I did manage to make the expanded collections update with <code>Reflection</code>, and calling <a href="http://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/PropertyGridInternal/PropertyDescriptorGridEntry.cs,1c96835b83a9107a" rel="noreferrer"><code>NotifyValueGivenParent</code></a> method on the <code>context</code> object when the <code>PropertyDescriptor</code> GetValue method is called (when <code>RefreshRequired</code> event is raised):</p>
  
  <pre><code>var notifyValueGivenParentMethod = context.GetType().GetMethod("NotifyValueGivenParent", BindingFlags.NonPublic | BindingFlags.Instance);
  notifyValueGivenParentMethod.Invoke(context, new object[] {context.Instance, 1});
  </code></pre>
  
  <p>It works perfectly, except it causes the event to be raised infinite times, because calling <code>NotifyValueGivenParent</code> causes a reload of the <code>PropertyDescriptor</code>, and therfore, raising the event, and so on.</p>
  
  <p>I tried to solve it by adding a simple flag that will prevent the reloading if it is already reloading, but for some reason <code>NotifyValueGivenParent</code> behaves asynchronously, and therefore the reloading happens after the flag is turned off.
  Maybe it is another direction to explore. The only problem is the recursion</p>