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
). El TypeDescriptionProvider
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:
[TypeDescriptionProvider(typeof(AbstractControlDescriptionProvider<BaseForm, BaseFormMiddle2>))] // BaseFormMiddle2 explained below
public abstract class BaseForm : Form
{
public BaseForm()
{
InitializeComponent();
}
public abstract void SomeAbstractMethod();
}
public class Form1 : BaseForm // Form1 is the form to be designed. As you see it's clean and you do NOTHING special here (only the the normal abstract method(s) implementation!). The developer of such form(s) doesn't have to know anything about the abstract base form problem. He just writes his form as usual.
{
public Form1()
{
InitializeComponent();
}
public override void SomeAbstractMethod()
{
// implementation of BaseForm's abstract method
}
}
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 la TypeDescriptionProvider
de BaseForm
. Sin compilación condicional.
Tenía dos problemas más:
- Problema 1: después de cambiar Form1 en el diseñador (o en algún código), estaba dando el error nuevamente (al intentar abrirlo en el diseñador nuevamente).
- Problema 2: los controles de BaseForm se colocaron incorrectamente cuando se cambió el tamaño de Form1 en el diseñador y el formulario se cerró y se volvió a abrir en el diseñador de formularios.
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.
class BaseFormMiddle1 : BaseForm
{
protected BaseFormMiddle1()
{
}
public override void SomeAbstractMethod()
{
throw new NotImplementedException(); // this method will never be called in design mode anyway
}
}
class BaseFormMiddle2 : BaseFormMiddle1 // empty class, just to make the VS designer working
{
}
Y aquí tienes la versión ligeramente modificada de TypeDescriptionProvider
:
public class AbstractControlDescriptionProvider<TAbstract, TBase> : TypeDescriptionProvider
{
public AbstractControlDescriptionProvider()
: base(TypeDescriptor.GetProvider(typeof(TAbstract)))
{
}
public override Type GetReflectionType(Type objectType, object instance)
{
if (objectType.FullName == typeof(TAbstract).FullName) // corrected condition here (original condition was incorrectly giving false in my case sometimes)
return typeof(TBase);
return base.GetReflectionType(objectType, instance);
}
public override object CreateInstance(IServiceProvider provider, Type objectType, Type[] argTypes, object[] args)
{
if (objectType.FullName == typeof(TAbstract).FullName) // corrected condition here (original condition was incorrectly giving false in my case sometimes)
objectType = typeof(TBase);
return base.CreateInstance(provider, objectType, argTypes, args);
}
}
¡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
a public class Form1 : BaseFormMiddle1
(o BaseFormMiddle2
) 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 .