This has become a FAQ over the time and so I have decided to just write a darn blog post about it.
With collection management during design-time is fairly easy. In fact, too easy. However, sooner or later, you would like to have a multi-dimension collection. Let's take for example:
[ParseChildren(true, "Tabs")]
[PersistChildren(false)]
public abstract class Menu : Control {
private MenuItemCollection _MenuItems = new MenuItemCollection();
[NotifyParentProperty(true)]
[PersistenceMode(PersistenceMode.InnerProperty) ]
[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
public MenuItemCollection MenuItems {
get {
return this._MenuItems;
}
}
}
public class MenuItemCollection : CollectionBase {
public new MenuItem this[int index] {
get {
return (MenuItem)base.this[index];
}
}
}
public class MenuItem : Menu {
// ...
}
public class MainMenu : Menu {
// ...
}
One common trait that will build up is the MainMenu control will have the Menu property which will open up a designer to add MenuItem instances. No big deal! However, your menu items also has the Menu property which will open up another designer. So you can add menu items within an menu item. You think that you are finished so you accept the changes and close the designer only to be met with an error message “object reference is not set to an instance object”.
The cause of the problem is very vague. However, let me tell you a little secret -- the problem is not with your code; it is with Microsoft's. Luckily, you can override their designer for the collection management and just put enough effort into the designer to make that pesky error message go away. So, here is the code for the new, overridden designer:
public class MenuItemCollectionEditor : CollectionEditor {
private CollectionForm collectionForm;
public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value) {
if ((collectionForm != null) && (collectionForm.Visible)) {
MenuItemCollectionEditor editor = new MenuItemCollectionEditor(CollectionType);
return editor.EditValue(context, provider, value);
}
else
return base.EditValue(context, provider, value);
}
protected override CollectionEditor.CollectionForm CreateCollectionForm() {
collectionForm = base.CreateCollectionForm();
return collectionForm;
}
public MenuItemCollectionEditor(Type type) : base(type) {
}
}
If you had to look carefully at the code, you will eventually find out that the cause of error message was because only one designer instance can be “open” at any given time. The above code just rectifies that so that it creates multiple instances of the designers.
To use the above code, just pop the following attribute on the collection:
[Editor(typeof(MenuItemCollectionEditor), typeof(UITypeEditor))]
public class MenuItemCollection : CollectionBase {
// ...
}