J'ai quelques conseils pour les personnes qui disent que le TypeDescriptionProvider
par Juan Carlos Diaz ne fonctionne pas et n'aiment pas non plus la compilation conditionnelle:
Tout d'abord, vous devrez peut-être redémarrer Visual Studio pour que les modifications de votre code fonctionnent dans le concepteur de formulaires (je devais, la reconstruction simple ne fonctionnait pas - ou pas à chaque fois).
Je présenterai ma solution de ce problème pour le cas de la forme de base abstraite. Disons que vous avez une BaseForm
classe et que vous souhaitez que tous les formulaires basés sur celle-ci soient modifiables (ce sera le cas Form1
). Le TypeDescriptionProvider
tel que présenté par Juan Carlos Diaz n'a pas fonctionné pour moi aussi. Voici comment je l'ai fait fonctionner, en le joignant à la solution MiddleClass (par smelch), mais sans la#if DEBUG
compilation conditionnelle et avec quelques corrections:
[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
}
}
Notez l'attribut sur la classe BaseForm. Ensuite, il vous suffit de déclarer la classe moyenneTypeDescriptionProvider
et les deux classes moyennes , mais ne vous inquiétez pas, elles sont invisibles et non pertinentes pour le développeur de Form1 . Le premier implémente les membres abstraits (et rend la classe de base non abstraite). Le second est vide - il est juste nécessaire au concepteur de formulaires VS de fonctionner. Ensuite, vous affectez la deuxième classe moyenne au TypeDescriptionProvider
de BaseForm
. Aucune compilation conditionnelle.
J'avais encore deux problèmes:
- Problème 1: après avoir modifié Form1 dans le concepteur (ou un code), l'erreur a été renvoyée (en essayant de l'ouvrir à nouveau dans le concepteur).
- Problème 2: les contrôles de BaseForm ont été placés de manière incorrecte lorsque la taille de Form1 a été modifiée dans le concepteur et le formulaire a été fermé et rouvert dans le concepteur de formulaires.
Le premier problème (vous ne l'avez peut-être pas car c'est quelque chose qui me hante dans mon projet à quelques autres endroits et produit généralement une exception "Impossible de convertir le type X en type X"). Je l'ai résolu dans le TypeDescriptionProvider
en comparant les noms de type (FullName) au lieu de comparer les types (voir ci-dessous).
Le deuxième problème. Je ne sais pas vraiment pourquoi les contrôles du formulaire de base ne sont pas concevables dans la classe Form1 et leurs positions sont perdues après le redimensionnement, mais je l'ai contourné (ce n'est pas une bonne solution - si vous en savez mieux, veuillez écrire). Je déplace simplement manuellement les boutons de BaseForm (qui devraient être dans le coin inférieur droit) à leurs positions correctes dans une méthode appelée de manière asynchrone à partir de l'événement Load de BaseForm: BeginInvoke(new Action(CorrectLayout));
Ma classe de base n'a que les boutons "OK" et "Annuler", donc le le cas est 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
{
}
Et ici vous avez la version légèrement modifiée 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);
}
}
Et c'est tout!
Vous n'avez rien à expliquer aux futurs développeurs de formulaires basés sur votre BaseForm et ils n'ont pas à faire d'astuces pour concevoir leurs formulaires! Je pense que c'est la solution la plus propre possible (sauf pour le repositionnement des commandes).
Encore un conseil:
Si, pour une raison quelconque, le concepteur refuse toujours de travailler pour vous, vous pouvez toujours faire la simple astuce de changer le public class Form1 : BaseForm
to public class Form1 : BaseFormMiddle1
(ou BaseFormMiddle2
) dans le fichier de code, de le modifier dans le concepteur de formulaire VS, puis de le modifier à nouveau. Je préfère cette astuce à la compilation conditionnelle car il est moins probable d'oublier et de publier la mauvaise version .