SABÍA que tenía que haber una manera de hacer esto (y encontré una manera de hacerlo limpiamente). La solución de Sheng es exactamente lo que se me ocurrió como una solución temporal, pero después de que un amigo señaló que la Form
clase eventualmente heredada de una abstract
clase, DEBEMOS poder hacer esto. Si pueden hacerlo, nosotros podemos hacerlo.
Pasamos de este código al problema.
Form1 : Form
Problema
public class Form1 : BaseForm
...
public abstract class BaseForm : Form
Aquí es donde entró en juego la pregunta inicial. Como se dijo antes, un amigo señaló que System.Windows.Forms.Form
implementa una clase base que es abstracta. Pudimos encontrar ...
Prueba de una mejor solución
A partir de esto, sabíamos que era posible que el diseñador mostrara una clase que implementara una clase abstracta base, simplemente no podía mostrar una clase de diseñador que implementara inmediatamente una clase abstracta base. Tenía que haber un máximo de 5 entremedias, pero probamos 1 capa de abstracción e inicialmente se nos ocurrió esta solución.
Solución inicial
public class Form1 : MiddleClass
...
public class MiddleClass : BaseForm
...
public abstract class BaseForm : Form
...
Esto realmente funciona y el diseñador lo muestra bien, problema resuelto ... ¡excepto que tiene un nivel adicional de herencia en su aplicación de producción que solo era necesario debido a una deficiencia en el diseñador de winforms!
Esta no es una solución 100% infalible, pero es bastante buena. Básicamente, usas #if DEBUG
para llegar a la solución refinada.
Solución refinada
Form1.cs
#if DEBUG
public class Form1 : MiddleClass
#else
public class Form1 : BaseForm
#endif
...
MiddleClass.cs
public class MiddleClass : BaseForm
...
BaseForm.cs
public abstract class BaseForm : Form
...
Lo que hace esto es usar solo la solución descrita en "solución inicial", si está en modo de depuración. La idea es que nunca lanzará el modo de producción a través de una compilación de depuración y que siempre diseñará en modo de depuración.
El diseñador siempre se ejecutará con el código creado en el modo actual, por lo que no puede usar el diseñador en el modo de lanzamiento. Sin embargo, siempre que diseñe en modo de depuración y publique el código integrado en el modo de lanzamiento, está listo para comenzar.
La única solución infalible sería si puede probar el modo de diseño a través de una directiva de preprocesador.
@smelch, existe una solución mejor, sin tener que crear un control medio, incluso para depuración.
Lo que nosotros queremos
Primero, definamos la clase final y la clase abstracta base.
Ahora todo lo que necesitamos es un proveedor de descripciones .
Finalmente, solo aplicamos un atributo TypeDescriptionProvider al control Abastract.
Y eso es. No se requiere control intermedio.
Y la clase de proveedor se puede aplicar a tantas bases abstractas como queramos en la misma solución.
* EDITAR * También se necesita lo siguiente en la app.config
Gracias @ user3057544 por la sugerencia.
fuente
TypeDescriptionProvider
@Smelch, gracias por la útil respuesta, ya que me encontré con el mismo problema recientemente.
A continuación se muestra un cambio menor en su publicación para evitar advertencias de compilación (colocando la clase base dentro de la
#if DEBUG
directiva del preprocesador):fuente
Tuve un problema similar pero encontré una manera de refactorizar las cosas para usar una interfaz en lugar de una clase base abstracta:
Esto puede no ser aplicable a todas las situaciones, pero cuando es posible da como resultado una solución más limpia que la compilación condicional.
fuente
UserControl
propiedad a la interfaz y la mencioné cada vez que necesitaba acceder directamente. En mis implementaciones de interfaz, extiendo UserControl y configuro laUserControl
propiedad enthis
Estoy usando la solución en esta respuesta a otra pregunta, que vincula este artículo . El artículo recomienda usar un personalizado
TypeDescriptionProvider
implementación y concreta de la clase abstracta. El diseñador le preguntará al proveedor personalizado qué tipos usar, y su código puede devolver la clase concreta para que el diseñador esté contento mientras usted tiene control total sobre cómo aparece la clase abstracta como una clase concreta.Actualización: incluí un ejemplo de código documentado en mi respuesta a esa otra pregunta. El código allí funciona, pero a veces tengo que pasar por un ciclo de limpieza / compilación como se indica en mi respuesta para que funcione.
fuente
Tengo algunos consejos para las personas que dicen que el
TypeDescriptionProvider
de Juan Carlos Díaz no funciona y tampoco les gusta la compilación condicional:En primer lugar, es posible que deba reiniciar Visual Studio para que los cambios en su código funcionen en el diseñador de formularios (tuve que hacerlo, la reconstrucción simple no funcionó, o no siempre).
Presentaré mi solución a este problema para el caso de la forma base abstracta. Supongamos que tiene una
BaseForm
clase y desea que cualquier formulario basado en ella pueda designarse (así seráForm1
). ElTypeDescriptionProvider
presentado por Juan Carlos Díaz tampoco funcionó para mí. Así es como lo hice funcionar, uniéndolo con la solución MiddleClass (por smelch), pero sin la#if DEBUG
compilación condicional y con algunas correcciones:Observe el atributo de la clase BaseForm. A continuación, sólo tiene que declarar los
TypeDescriptionProvider
y las dos clases medias , pero no se preocupe, que son invisibles e irrelevantes para el desarrollador de Form1 . El primero implementa los miembros abstractos (y hace que la clase base no sea abstracta). El segundo está vacío; solo es necesario para que funcione el diseñador de formularios VS. Luego asigna la segunda clase media a laTypeDescriptionProvider
deBaseForm
. Sin compilación condicional.Tenía dos problemas más:
El primer problema (es posible que no lo tenga porque es algo que me persigue en mi proyecto en algunos otros lugares y generalmente produce una excepción "No se puede convertir el tipo X en el tipo X"). Lo resuelto en el
TypeDescriptionProvider
de la comparación de los nombres de los tipos (FullName) en lugar de la comparación de los tipos (ver más abajo).El segundo problema. Realmente no sé por qué los controles del formulario base no se pueden designar en la clase Form1 y sus posiciones se pierden después de cambiar el tamaño, pero lo he solucionado (no es una buena solución; si sabe algo mejor, escriba). Simplemente muevo manualmente los botones de BaseForm (que deberían estar en la esquina inferior derecha) a sus posiciones correctas en un método invocado asincrónicamente desde el evento Load de BaseForm:
BeginInvoke(new Action(CorrectLayout));
Mi clase base solo tiene los botones "Aceptar" y "Cancelar", por lo que el el caso es simple.Y aquí tienes la versión ligeramente modificada de
TypeDescriptionProvider
:¡Y eso es!
¡No tiene que explicar nada a los futuros desarrolladores de formularios basados en su BaseForm y ellos no tienen que hacer ningún truco para diseñar sus formularios! Creo que es la solución más limpia que puede ser (excepto por el reposicionamiento de los controles).
Un consejo más:
Si por alguna razón el diseñador aún se niega a trabajar para usted, siempre puede hacer el simple truco de cambiar
public class Form1 : BaseForm
apublic class Form1 : BaseFormMiddle1
(oBaseFormMiddle2
) en el archivo de código, editarlo en el diseñador de formularios VS y luego volver a cambiarlo. Prefiero este truco a la compilación condicional porque es menos probable que olvide y publique la versión incorrecta .fuente
Tengo un consejo para la solución de Juan Carlos Díaz. Funciona muy bien para mí, pero fue un problema. Cuando inicio VS y entro al diseñador, todo funciona bien. Pero después de ejecutar la solución, deténgala y salga y luego intente ingresar al diseñador, la excepción aparece una y otra vez hasta que reinicia VS. Pero encontré la solución: todo lo que hay que hacer es agregar a continuación a su app.config
fuente
Dado que la clase abstracta
public abstract class BaseForm: Form
da un error y evita el uso del diseñador, vine con el uso de miembros virtuales. Básicamente, en lugar de declarar métodos abstractos, declaré métodos virtuales con el cuerpo mínimo posible. Esto es lo que hice:Como
DataForm
se suponía que era una clase abstracta con el miembro abstractodisplayFields
, "finjo" este comportamiento con miembros virtuales para evitar la abstracción. El diseñador ya no se queja y todo me funciona bien.Es una solución alternativa más legible, pero como no es abstracto, tengo que asegurarme de que todas las clases secundarias de
DataForm
tengan su implementacióndisplayFields
. Por lo tanto, tenga cuidado al utilizar esta técnica.fuente
El Diseñador de Windows Forms está creando una instancia de la clase base de su formulario / control y aplica el resultado de análisis de
InitializeComponent
. Es por eso que puede diseñar el formulario creado por el asistente de proyectos sin siquiera construir el proyecto. Debido a este comportamiento, tampoco puede diseñar un control derivado de una clase abstracta.Puede implementar esos métodos abstractos y lanzar una excepción cuando no se esté ejecutando en el diseñador. El programador que deriva del control debe proporcionar una implementación que no llame a la implementación de su clase base. De lo contrario, el programa fallaría.
fuente
Podría simplemente compilar condicionalmente en la
abstract
palabra clave sin interponer una clase separada:Esto funciona siempre que
BaseForm
no tenga ningún método abstracto (laabstract
palabra clave, por lo tanto, solo evita la instanciación en tiempo de ejecución de la clase).fuente