Thursday, 6 December 2012

Cannot create an instance of ...type... because it is an abstract class

When you are using the propertygrid to bind a generic list the system tends to work allowing you to add new items to the list as long as the items in List<T> are all of the exact same type.

However if the List<T> is an abstract class and you want to select which item type to add to the list you will find the following error.

Cannot create an instance of Type because it is an abstract class.














What you want in this case it to be able to add any subclass of the abstract class defined as T in the list selecting the appropriate type. This can be done simply by inheriting from the CollectionEditor and letting the system know what types of object are valid for selection.

I this example rather than manually return a list we use Reflection to read all of the types defined in the assembly and determine dynamically which ones are inherited from the base class T.


/// <summary>
/// Provides an editor to modify a list of variable definitions
/// </summary>
public class DynamicCollectionEditor : CollectionEditor
{
    /// <summary>
    /// Creates a new collection editor
    /// </summary>
    /// <param name="type">The type of the collection for this editor to edit.</param>
    public DynamicCollectionEditor(Type type)
        : base(type)
    {
        ItemType = type.GetGenericArguments()[0];
    }
 
    /// <summary>
    /// Gets or sets the object type that is contained within the list
    /// </summary>
    public Type ItemType;
 
    /// <summary>
    /// Determines the items that can be created by the editor
    /// </summary>
    /// <returns>An array of item types that are valid for the editor</returns>
    protected override Type[] CreateNewItemTypes()
    {
        List<Type> ValidTypes = new List<Type>();
        Type[] Types = Assembly.GetAssembly(ItemType).GetTypes();
        foreach (Type Type in Types)
        {
            if (Type.IsSubclassOf(ItemType)) { ValidTypes.Add(Type); }
        }
        return ValidTypes.ToArray();
    }
}
 
 
Then simply decorate the list you wish to edit with the following line

[Editor(typeof(DynamicCollectionEditor), typeof(System.Drawing.Design.UITypeEditor))]


For example...


/// <summary>
/// Gets or sets the items in the collection
/// </summary>
[Editor(typeof(DynamicCollectionEditor), typeof(System.Drawing.Design.UITypeEditor))]
public List<VariableDefinitionBase> Items
{
    get { return _Items; }
    set { _Items = value; }
}
private List<VariableDefinitionBase> _Items = new List<VariableDefinitionBase>();

 
 
The property grid now renders as follows, automatically displaying any subclass of them item in the list
 
For more information about our software see our Server Documentation page on our site
 
 
 

1 comment:

  1. I've just realised that you might want to update the CreateNewItemTypes method to

    if (Type.IsSubclassOf(ItemType) && Type.IsAbstract == false) { ValidTypes.Add(Type); }

    Otherwise you can have abstract classes that derive from the original abstract class and cannot be added to the list

    Dave

    ReplyDelete