Resumen UserControl herencia en el diseñador de Visual Studio

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

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

Se me cayó un DetailControl en un formulario. Se hace correctamente en tiempo de ejecución, pero el diseñador se muestra un mensaje de error y no se abre debido a que la base de control de usuario es abstracta.

Por el momento, estoy contemplando el siguiente parche, que parece muy mal para mí, como yo quiero a los niños de las clases para ser obligados a implementar el método.

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

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

Alguien tiene una mejor idea sobre la manera de trabajar a mi manera alrededor de este problema?

InformationsquelleAutor asmo | 2011-07-25

9 Kommentare

  1. 20

    Puede utilizar un TypeDescriptionProviderAttribute para proporcionar un diseño de concreto de tiempo de implementación de la clase base abstracta. Ver http://wonkitect.wordpress.com/2008/06/20/using-visual-studio-whidbey-to-design-abstract-forms/ para más detalles.

    • Funciona sólo cuando yo abra Visual Studio por primera vez. Una vez que realice cualquier cambio en el código y volver a generar el proyecto deja de funcionar. De todos modos gracias.
    • es posible que fue un error en devenv que desde entonces ha sido un fijo. Estoy usando VS2017 derecho y ahora puedo abrir el diseñador, cierre el diseñador, hacer un cambio en el código, compilar el proyecto y el diseñador sigue abierta.
  2. 48

    Lo que queremos

    Primero, vamos a definir el final de la clase y la base de la clase abstracta.

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

    Ahora todo lo que necesitamos es un Descripción proveedor de.

    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);
        }
    }

    Finalmente, sólo nos aplicar un TypeDescriptionProvider atributo Abstracto de control.

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

    Y eso es todo. Ningún medio de control requerido.

    Y el proveedor de clase se puede aplicar a muchos Abstracto bases como queremos en la misma solución.

    • Esto funcionó para mí, aunque en mi caso tengo dos niveles de abstract clases entre el concreto de la subclase y UserControl. Por tanto de la abstract clases que se necesitan para proporcionar el TypeDescriptionProvider con un TBase de UserControl. He cambiado el nombre de TBase a TFirstConcreteBase explícita de claridad para el que viene después de mí. Yo tenía la esperanza de las dos originales TBase declaración de la cadena para obtener el derecho de hormigón de la base, pero no es así. Gracias por la ayuda, @Juan.
    • Me alegro de que podría ayudar a @JMD
    • También es necesario volver a compilar el projekt y cerca de Visual Studio para el diseñador mensaje a desaparecer
    • He probado con un resumen de control que también los usos genéricos. Al principio pensé que quizás era el trabajo, pero al final no pude conseguir Diseñador para abrir el resumen de la base de control o el derivado de los controles. Cerrar y volver a abrir VS hizo capaz de formularios que contengan la clase derivada. Finalmente se decidió a punt, y no hacer de mi clase base abstracta!
    • Esto funcionó para mí hasta que yo cerrado de Visual Studio. Después de la apertura y de cierre VS nuevamente, veo el mismo error.
    • y es útil para poner la clave siguiente en el archivo de configuración. <packages> ... <add key="EnableOptimizedDesignerReloading" value="false" /> </packages>
    • Lamentablemente esto no funciona en generic resumen de los controles de usuario, que se aborda también en el stackoverflow.com/q/38262541/2505186, que se refiere precisamente a esa respuesta. Y tiene el inconveniente de no mostrar los controles que ya se han añadido en el resumen de control de usuario. Así que para mí la única solución era utilizar un intermedio de control de usuario, como se describe en stackoverflow.com/a/677630/2505186

  3. 6

    Otra manera de resolver este problema es el uso de pre-procesamiento de directivas.

    #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
    
        ...
      }

    Consulte este enlace para obtener más información sobre este tema: La herencia de un Formulario de una Clase Abstracta (y lo que es Trabajar en el Diseñador)

    La misma solución fue también mencionado en este hilo en el foro de MSDN, en un breve manera: UserControl, Heredó el Control Abstracto de la clase, (C#)

    Tal vez no es la solución más limpia, pero aún así es el más corto que he encontrado.

  4. 2

    La siguiente es una solución genérica que funciona para mí, en su mayoría. Se basa en la artículo de otra respuesta. A veces funciona, y puedo diseñar mi UserControl, y luego voy a abrir el archivo y se le dé el «El diseñador debe crear una instancia de tipo ‘MyApp.UserControlBase’ pero no puede porque el tipo está declarada como abstracta.» Creo que se puede arreglar por la limpieza, el cierre de VS, la reapertura de VS, y la reconstrucción. Ahora parece que se comporta. La buena suerte.

    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);
            }
        }
    }
    • Esto parece un gran trabajo para la edición de el resumen de interfaz de usuario del diseñador, pero cuando voy a heredar de la clase abstracta e intentar modificar la sub clase me sale el mismo error acerca de no ser capaz de crear una instancia de la clase base abstracta. Estoy haciendo algo estúpido?
    • Tengo el mismo problema. Intente algo como la construcción de la solución, la apertura/edición de la derivada de control (ya sea con éxito o no), el cierre de la derivada de control, la limpieza de la solución, cierre visual studio, vuelva a abrir visual studio y la solución, volver a generar la solución. El éxito/fracaso para mí es intermitente.
    • +1 si la limpieza y reapertura después de declarar el tipo de proveedor trabajado
  5. 2

    Aunque esta pregunta es años de edad, me gustaría añadir que lo he encontrado.

    Si usted no quiere tocar su clase base abstracta, usted puede hacer esto 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;
        }
    }

    De esta manera, su forma hereda de un no-base abstracta formulario y se muestra en el diseñador! Y mantener su forma abstracta, pero sólo en un nivel más arriba en la herencia. Extraño, ¿no?

    • Esto en realidad no ayuda porque se elimina el beneficio de tener una clase base abstracta, el requisito de que se implemente el método. Lanzar una excepción sólo se aplica el requisito de tiempo de ejecución.
    • Sí, no es lo ideal. Pero todavía puede mantener su clase abstracta para ser utilizada a través de su aplicación como lo planeado. Y este intermedio de ayudante de clase, para este IDE limitación, incluso puede ser colocado en el mismo .cs archivo para hacer de alguna manera que se aproxime a lo que usted haría sin ella: que es, implementar todos los métodos abstractos en ese archivo, siendo la única diferencia de que tienes que hacerlo dos veces: la lanzando uno y el real. Existe una mejor opción?
  6. 2

    No podía hacer el trabajo de la solución de ‘Nicole Calinoiu’. Pero hay otra manera sencilla directamente en visual studio:)

    1. Crear nuevo proyecto
    2. Agregar nuevo elemento «control de usuario» y agregar un botón, por ejemplo
    3. Agregar nuevo elemento ‘userControl’ Heredó UserControl, a continuación, seleccione la heredó userControl.

    Más detalles aquí : ‘http://www.codeproject.com/Articles/20845/How-to-derive-from-a-parent-form

    • Esto funcionó para mí, esta es la forma más sencilla.
  7. 0

    Acabo de hacer la clase base abstracta en una de concreto mediante la definición de la palabra «abstracto» de los métodos como virtual, y lanzar una excepción en ellos, sólo en caso de que algún travieso clases derivadas tratar de llamar a la Base de la aplicación.

    por ejemplo,

        class Base : UserControl
        {
            protected virtual void BlowUp()
            {
                throw new NotSupportedException("This method MUST be overriden by ALL derived classes.");
            }
    
        class Derived : Base
        {
            protected override void BlowUp()
            {
                //Do stuff, but don't call base implementation,
                //just like you wouldn't (can't actually) if the Base was really abstract. 
                //BTW - doesn't blow up any more  ;)
            }

    La principal diferencia práctica entre este y una verdadera clase base abstracta que es conseguir errores en tiempo de ejecución cuando se llama a la base de la implementación, mientras que si la Base era en realidad abstracta, el compilador podría impedir un choque accidental de llamadas para la implementación de la clase Base. Que no es una gran cosa para mí y me permite usar el diseñador sin preocuparse más complejo y lleva mucho tiempo de trabajo soluciones sugeridas por otros…

    PS – Akuma – usted debe ser capaz de editar su resumen de interfaz de usuario de la clase en el diseñador. No tengo tiempo para comprobar esto ahora mismo, pero es mi entendimiento de que el diseñador sólo necesita crear una instancia de la clase BASE. Mientras la base de la clase que va a diseñar es de hormigón, no importa lo que el diseño de la clase es.

    • No es este exactamente el mismo que el parche que he publicado inicialmente en mi pregunta?
  8. 0

    He resuelto este problema en UWP en mi control personalizado.

    Mi Caso

    public abstract class BaseModel : DependencyObject 
    
    {
    ...
    }
    
    public class MainModel : BaseModel
    
    {
    
    public bool ShowLabel
    
    {
        get{ return (bool)GetValue(ShowLabelProperty); }
        set{ SetValue(ShowLabelProperty, value) }
    }
    
    public static readonly DependencyProperty ShowLabelProperty =
    
        DependencyProperty.Register("ShowLabel",typeof(bool), typeof(MainModel), new PropertyMetadata(false));
    
    }

    Declaración

    < MyCustomControl:MainModel ShowLabel=True />

    Solución

    Simplemente reemplazar un muñeco de estilo en los recursos genéricos.

    <Style TargetType="local:MainModel" />

    Saludos,

    Samuel

  9. 0

    Yo era nuevo en este UWP y me estaba volviendo loco. No creo que de una clase base abstracta para un control de usuario. Yo iba en una dirección diferente. He creado un no-xaml clase Auxiliar … HBase. Cada punto de Vista, decir VContract, corresponde a un Ayudante llamado HContract. Todas la especialidades de código para cada vista se presentó allí. Las conversaciones que habría sido entre el ViewModel VMContract y Ver VContract ahora pasar a través de HContract. Podemos exigir cómo HWhatever se comporta con IHBase. No es realmente una respuesta a la OP de la pregunta, pero que muestran un enfoque alternativo. Todas las Opiniones son ahora, básicamente, las conchas. Si x:se Unen a la VContract o HContract es una decisión para que usted pueda hacer. Elegí el VContract forma y en el fondo creo que fue un error.

    El UWP problema con excepciones en el Modo de Diseño ahora se fija fácilmente con:

    if (false == Windows.ApplicationModel.DesignMode.DesignModeEnabled)
    {
                HContract = new HContract(this);
    //  Put code here that fails in Design mode but must at run time               
    }

Kommentieren Sie den Artikel

Bitte geben Sie Ihren Kommentar ein!
Bitte geben Sie hier Ihren Namen ein

Pruebas en línea