Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Abstract UserControl inheritance in Visual Studio designer

What we want

First, let's define the final class and the base abstract class.

public class MyControl : AbstractControl
...
public abstract class AbstractControl : UserControl // Also works for Form
...

Now all we need is a Description provider.

public class AbstractControlDescriptionProvider<TAbstract, TBase> : TypeDescriptionProvider
{
    public AbstractControlDescriptionProvider()
        : base(TypeDescriptor.GetProvider(typeof(TAbstract)))
    {
    }

    public override Type GetReflectionType(Type objectType, object instance)
    {
        if (objectType == typeof(TAbstract))
            return typeof(TBase);

        return base.GetReflectionType(objectType, instance);
    }

    public override object CreateInstance(IServiceProvider provider, Type objectType, Type[] argTypes, object[] args)
    {
        if (objectType == typeof(TAbstract))
            objectType = typeof(TBase);

        return base.CreateInstance(provider, objectType, argTypes, args);
    }
}

Finally we just apply a TypeDescriptionProvider attribute to the Abstract control.

[TypeDescriptionProvider(typeof(AbstractControlDescriptionProvider<AbstractControl, UserControl>))]
public abstract class AbstractControl : UserControl
...

And that's it. No middle control required.

And the provider class can be applied to as many Abstract bases as we want in the same solution.


You can use a TypeDescriptionProviderAttribute to provide a concrete design-time implementation for your abstract base class. See http://wonkitect.wordpress.com/2008/06/20/using-visual-studio-whidbey-to-design-abstract-forms/ for details.


Another way to solve this is using pre-processing directives.

#if DEBUG
  public class UserControlAdmonEntidad : UserControl, IAdmonEntidad
#else
  public abstract class UserControlAdmonEntidad : UserControl, IAdmonEntidad
#endif
  {
    ...
    #if DEBUG
    public virtual object DoSomething()
    {
        throw new NotImplementedException("This method must be implemented!!!");
    }
    #else
    public abstract object DoSomething();
    #endif

    ...
  }

See this link for more information regarding this topic: Inheriting a Form from an Abstract Class (and Making it Work in the Designer)

The same solution was also mentioned in this MSDN forum thread, in a briefer way: UserControl, Inherited Control, Abstract class, (C#)

Maybe is not the cleaner solution, but it's still the shortest I have found.


Even though this question is years old, I'd like to add what I've found.

If you don't want to touch your abstract base class, you can do this hack:

abstract class CustomControl : UserControl 
{
    protected abstract int DoStuff();
}

class BaseDetailControl : CustomControl
{
    protected override int DoStuff()
    {
        throw new InvalidOperationException("This method must be overriden.");
    }
}

class DetailControl : BaseDetailControl
{
    protected override int DoStuff()
    { 
        // do stuff
        return result;
    }
}

This way, your form inherits from a non-abstract base form and it's displayed in the designer! And you keep your abstract form, but only one more level up in the inheritance. Strange, isn't it?


I couldn't make work the solution of 'Nicole Calinoiu'. But there is an other easy way directly in visual studio:)

  1. Create new project
  2. Add new element 'userControl' and add one button for example
  3. Add new element 'userControl' Inhereted UserControl then select the inhereted userControl.

More details here : 'http://www.codeproject.com/Articles/20845/How-to-derive-from-a-parent-form


The following is a generic solution that works for me, mostly. It is based on the article from another answer. Sometimes it will work, and I can design my UserControl, and then later I'll open the file and it will give the "The designer must create an instance of type 'MyApp.UserControlBase' but it cannot because the type is declared as abstract." I think I can fix it by cleaning, closing VS, reopening VS, and rebuilding. Right now it seems to be behaving. Good luck.

namespace MyApp
{
    using System;
    using System.ComponentModel;

    /// <summary>
    /// Replaces a class  of <typeparamref name="T"/> with a class of
    /// <typeparamref name="TReplace"/> during design.  Useful for
    /// replacing abstract <see cref="Component"/>s with mock concrete
    /// subclasses so that designer doesn't complain about trying to instantiate
    /// abstract classes (designer does this when you try to instantiate
    /// a class that derives from the abstract <see cref="Component"/>.
    /// 
    /// To use, apply a <see cref="TypeDescriptionProviderAttribute"/> to the 
    /// class <typeparamref name="T"/>, and instantiate the attribute with
    /// <code>SwitchTypeDescriptionProvider<T, TReplace>)</code>.
    /// 
    /// E.g.:
    /// <code>
    /// [TypeDescriptionProvider(typeof(ReplaceTypeDescriptionProvider<T, TReplace>))]
    /// public abstract class T
    /// {
    ///     // abstract members, etc
    /// }
    /// 
    /// public class TReplace : T
    /// {
    ///     // Implement <typeparamref name="T"/>'s abstract members.
    /// }
    /// </code>
    /// 
    /// </summary>
    /// <typeparam name="T">
    /// The type replaced, and the type to which the 
    /// <see cref="TypeDescriptionProviderAttribute"/> must be
    /// applied
    /// </typeparam>
    /// <typeparam name="TReplace">
    /// The type that replaces <typeparamref name="T"/>.
    /// </typeparam>
    class ReplaceTypeDescriptionProvider<T, TReplace> : TypeDescriptionProvider
    {
        public ReplaceTypeDescriptionProvider() :
            base(TypeDescriptor.GetProvider(typeof(T)))
        {
            // Nada
        }

        public override Type GetReflectionType(Type objectType, object instance)
        {
            if (objectType == typeof(T))
            {
                return typeof(TReplace);
            }
            return base.GetReflectionType(objectType, instance);
        }

        public override object CreateInstance(
            IServiceProvider provider,
            Type objectType,
            Type[] argTypes,
            object[] args)
        {

            if (objectType == typeof(T))
            {
                objectType = typeof(TReplace);
            }

            return base.CreateInstance(provider, objectType, argTypes, args);
        }
    }
}