2011-07-25 16 views
24
abstract class CustomControl : UserControl 
{ 
    protected abstract int DoStuff(); 
} 

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

He soltado un DetailControl en un formulario. Se representa correctamente en tiempo de ejecución, pero el diseñador muestra un error y no se abrirá porque el control del usuario base es abstracto.Herencia abstracta de UserControl en el diseñador de Visual Studio

Por el momento, estoy contemplando el siguiente parche, lo cual me parece bastante incorrecto, ya que quiero que las clases secundarias se vean obligadas 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 cómo solucionar este problema?

+1

tuve similares recientemente y fui por tener una interfaz independiente que proporciona los métodos que quería. También traté de cambiar la clase base a UserControl cuando necesitaba editarla, pero estaba desordenada. – Deanna

+0

Posible duplicado de [¿Cómo puedo hacer que el diseñador de Windows Forms de Visual Studio 2008 presente un formulario que implemente una clase base abstracta?] (Http://stackoverflow.com/questions/1620847/how-can-i-get-visual- studio-2008-windows-forms-designer-to-render-a-form-that-im) – Breeze

Respuesta

17

Puede usar una TypeDescriptionProviderAttribute para proporcionar una implementación concreta del tiempo de diseño para su clase base abstracta. Vea http://wonkitect.wordpress.com/2008/06/20/using-visual-studio-whidbey-to-design-abstract-forms/ para más detalles.

+1

Funciona solo cuando abro Visual Studio por primera vez. Una vez que realizo algún cambio en el código y reconstruyo el proyecto, deja de funcionar. Gracias de todos modos. – Elephant

+0

@Elephant es posible que haya sido un error en devenv que desde entonces se ha corregido. Estoy usando VS2017 en este momento y puedo abrir el diseñador, cerrar el diseñador, hacer un cambio de código, recompilar el proyecto y el diseñador aún se abrirá. – Coxy

6

Otra forma de resolver esto es mediante el uso de directivas de procesamiento previo.

#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 

    ... 
    } 

Vea este enlace para obtener más información sobre este tema: Inheriting a Form from an Abstract Class (and Making it Work in the Designer)

La misma solución también se menciona en este hilo del foro de MSDN, de una manera más breve: UserControl, Inherited Control, Abstract class, (C#)

Tal vez no es la solución más limpia , pero sigue siendo el más corto que he encontrado.

2

La siguiente es una solución genérica que funciona para mí, principalmente. Se basa en el article de otra respuesta. Algunas veces funcionará, y puedo diseñar mi UserControl, y luego abriré el archivo y aparecerá "El diseñador debe crear una instancia de tipo 'MyApp.UserControlBase' pero no puede porque el tipo se declara como abstracto " Creo que puedo solucionarlo limpiando, cerrando VS, volviendo a abrir VS y reconstruyendo. En este momento parece estar comportándose. 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); 
     } 
    } 
} 
+0

Esto parece funcionar muy bien para editar la interfaz de usuario abstracta en el diseñador, pero cuando heredo de la clase abstracta e intento editar la subclase, obtengo el mismo error acerca de no poder crear una instancia de la clase base abstracta. ¿Estoy haciendo algo estúpido? – Akuma

+2

Tengo el mismo problema. Pruebe algo como crear una solución, abrir/editar el control derivado (sea exitoso o no), cerrar el control derivado, limpiar la solución, cerrar el estudio visual, volver a abrir el estudio visual y la solución, reconstruir la solución. El éxito/fracaso para mí es intermitente. –

+0

+1 Sí, la limpieza y la reapertura después de declarar que el proveedor de tipo trabajó –

0

simplemente hago la clase base abstracta en uno concreto mediante la definición de los métodos "abstractos" como virtual, y lanzar una excepción en ellos, por si acaso alguna clases derivadas traviesas tratan de llamar implementación base.

p. Ej.

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 clase base abstracta verdadera es que se consigue ejecutar los errores de tiempo cuando se llama a la implementación base - mientras que si la base era en realidad abstracta, el compilador no permitir un llamadas accidentales a la clase base implementación. Lo cual no es gran cosa para mí y me permite usar el diseñador sin preocuparse por el trabajo más complejo y lento que otros sugieren ...

PS - Akuma - debería poder editar su clase abstracta de IU en el diseñador No tengo tiempo para verificar esto ahora, pero tengo entendido que el diseñador solo necesita crear una instancia de la clase BASE. Siempre que la base de la clase que está diseñando sea concreta, no importa cuál sea la clase diseñada.

+5

¿No es exactamente lo mismo que el parche que publiqué inicialmente en mi pregunta? – asmo

35

Lo que queremos

En primer lugar, vamos a definir la clase final y la clase abstracta de base.

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

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

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, solo aplicamos un atributo TypeDescriptionProvider al control de Abastract.

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

Y eso es todo. No se requiere control medio.

Y la clase de proveedor se puede aplicar a tantas bases abstractas como queramos en la misma solución.

+2

Esto funcionó para mí, aunque en mi caso tengo dos niveles de clases 'abstractas' entre mi subclase concreta y' UserControl'. Para las dos clases "abstractas", necesitaba proporcionar el 'TypeDescriptionProvider' con' TBase' de 'UserControl'. Cambié el nombre de 'TBase' a' TFirstConcreteBase' para una claridad explícita para cualquiera que venga detrás de mí. Esperaba que las dos declaraciones originales de 'TBase' se encadenaran para obtener la base concreta correcta, pero no así. Gracias por la ayuda, @Juan. – JMD

+2

Me alegro de poder ayudar @JMD –

+2

También necesita recompilar su proyecto y cerrar Visual Studio para que desaparezca el mensaje del diseñador – Alex

2

Aunque esta pregunta tiene muchos años, me gustaría agregar lo que he encontrado.

Si usted no quiere tocar su clase base abstracta, se puede hacer esto truco:

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

esta manera, su forma hereda de una forma de base no abstracta y está representada en la ¡diseñador! Y mantienes tu forma abstracta, pero solo un nivel más en la herencia. Extraño, ¿verdad?

+1

Esto realmente no ayuda porque se elimina el beneficio de tener una clase base abstracta, el requisito que implemente el método. Lanzar una excepción solo aplica el requisito en tiempo de ejecución. –

+1

Sí, no es ideal.Pero aún puede mantener su clase abstracta para usar en su aplicación tal como lo planeó. Y esta clase auxiliar intermedia, que acaba de usarse para esta limitación IDE, incluso puede colocarse en el mismo archivo .cs para acercarla de algún modo a lo que haría sin ella: es decir, implementar todos los métodos abstractos en ese archivo, siendo el único diferencia de que tienes que hacerlo dos veces: el * tirar * uno y el real. ¿Hay alguna opción mejor? – Andrew

1

No pude hacer que el trabajo sea la solución de 'Nicole Calinoiu'. Pero hay una otra manera fácil directamente en el estudio visual :)

  1. Crear nuevo proyecto
  2. Añadir nuevo 'control de usuario' elemento y añadir un botón, por ejemplo
  3. Añadir nuevo elemento 'Usercontrol' Inhereted control de usuario luego seleccione el userControl entrado.

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

+0

Esto funcionó para mí, esta es la manera más simple. – Malick

0

que resuelven 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

Sólo anular un estilo maniquí en los recursos genéricos.

<Style TargetType="local:MainModel" /> 

Saludos,

Samuel

0

yo era nuevo en esto en uwp y me estaba volviendo loco. No pensé en una clase base abstracta para UserControl. Fui en una dirección diferente. Creé una clase Helper no-xaml ... HBase. Cada Vista, digamos VContract, tenía un Ayudante correspondiente llamado HContract. Todo el código de especialidad para cada vista se alojó allí.Las conversaciones que habrían tenido lugar entre ViewModel VMContract y View VContract ahora pasan por HContract. Podemos aplicar cómo se comporta HWhatever con IHBase. No es realmente una respuesta a la pregunta del OP, pero muestra un enfoque alternativo. Todas las Vistas ahora son básicamente conchas. Si usted es x: Enlace a VContract o HContract es una decisión que debe tomar. Elegí la forma VContract y al final creo que fue un error.

El problema UWP con excepciones en modo de diseño ha sido arreglado 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    
} 
Cuestiones relacionadas