JE SAVAIS qu'il devait y avoir un moyen de le faire (et j'ai trouvé un moyen de le faire proprement). La solution de Sheng est exactement ce que j'ai proposé comme solution de contournement temporaire, mais après qu'un ami ait souligné que la Form
classe a finalement hérité d'une abstract
classe, nous DEVRAIENT être en mesure de le faire. S'ils peuvent le faire, nous pouvons le faire.
Nous sommes passés de ce code au problème
Form1 : Form
Problème
public class Form1 : BaseForm
...
public abstract class BaseForm : Form
C'est là qu'intervient la question initiale. Comme dit précédemment, un ami a souligné qu'il System.Windows.Forms.Form
implémente une classe de base abstraite. Nous avons pu trouver ...
Preuve d'une meilleure solution
À partir de là, nous savions qu'il était possible pour le concepteur de montrer une classe qui implémentait une classe abstraite de base, il ne pouvait tout simplement pas afficher une classe de concepteur qui implémentait immédiatement une classe abstraite de base. Il devait y en avoir au maximum 5 entre les deux, mais nous avons testé 1 couche d'abstraction et avons initialement proposé cette solution.
Solution initiale
public class Form1 : MiddleClass
...
public class MiddleClass : BaseForm
...
public abstract class BaseForm : Form
...
Cela fonctionne réellement et le concepteur le rend bien, le problème est résolu ... sauf que vous avez un niveau supplémentaire d'héritage dans votre application de production qui n'était nécessaire qu'en raison d'une insuffisance dans le concepteur de winforms!
Ce n'est pas une solution sûre à 100%, mais c'est plutôt bon. Fondamentalement, vous utilisez #if DEBUG
pour trouver la solution raffinée.
Solution raffinée
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
...
Cela n'utilise que la solution décrite dans «solution initiale», si elle est en mode débogage. L'idée est que vous ne publierez jamais le mode production via une version de débogage et que vous concevrez toujours en mode débogage.
Le concepteur s'exécutera toujours sur le code créé dans le mode actuel, vous ne pouvez donc pas utiliser le concepteur en mode version. Cependant, tant que vous concevez en mode débogage et publiez le code intégré en mode version, vous êtes prêt à partir.
La seule solution infaillible serait de pouvoir tester le mode conception via une directive de préprocesseur.
@smelch, Il existe une meilleure solution, sans avoir à créer un contrôle intermédiaire, même pour le débogage.
Ce que nous voulons
Tout d'abord, définissons la classe finale et la classe abstraite de base.
Maintenant, tout ce dont nous avons besoin est un fournisseur de description .
Enfin, nous appliquons simplement un attribut TypeDescriptionProvider au contrôle Abastract.
Et c'est tout. Aucun contrôle intermédiaire requis.
Et la classe de fournisseur peut être appliquée à autant de bases abstraites que nous le souhaitons dans la même solution.
* EDIT * Les éléments suivants sont également nécessaires dans l'app.config
Merci @ user3057544 pour la suggestion.
la source
TypeDescriptionProvider
@Smelch, merci pour la réponse utile, car je rencontrais le même problème récemment.
Voici une modification mineure de votre publication pour éviter les avertissements de compilation (en mettant la classe de base dans la
#if DEBUG
directive pré-processeur):la source
J'ai eu un problème similaire mais j'ai trouvé un moyen de refactoriser les choses pour utiliser une interface à la place d'une classe de base abstraite:
Cela peut ne pas être applicable à toutes les situations, mais lorsque cela est possible, il en résulte une solution plus propre que la compilation conditionnelle.
la source
UserControl
propriété à l'interface et l'ai référencée chaque fois que j'avais besoin d'y accéder directement. Dans mes implémentations d'interface, j'étends UserControl et définit laUserControl
propriété surthis
J'utilise la solution dans cette réponse à une autre question, qui lie cet article . L'article recommande d'utiliser une
TypeDescriptionProvider
implémentation personnalisée et concrète de la classe abstraite. Le concepteur demandera au fournisseur personnalisé quels types utiliser, et votre code peut renvoyer la classe concrète afin que le concepteur soit satisfait pendant que vous avez un contrôle complet sur la façon dont la classe abstraite apparaît en tant que classe concrète.Mise à jour: j'ai inclus un exemple de code documenté dans ma réponse à cette autre question. Le code fonctionne, mais parfois, je dois passer par un cycle de nettoyage / construction comme indiqué dans ma réponse pour le faire fonctionner.
la source
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 casForm1
). LeTypeDescriptionProvider
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:Notez l'attribut sur la classe BaseForm. Ensuite, il vous suffit de déclarer la classe moyenne
TypeDescriptionProvider
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 auTypeDescriptionProvider
deBaseForm
. Aucune compilation conditionnelle.J'avais encore deux problèmes:
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.Et ici vous avez la version légèrement modifiée de
TypeDescriptionProvider
: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
topublic class Form1 : BaseFormMiddle1
(ouBaseFormMiddle2
) 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 .la source
J'ai une astuce pour la solution Juan Carlos Diaz. Cela fonctionne très bien pour moi, mais cela pose un problème. Quand je lance VS et entre dans le concepteur, tout fonctionne bien. Mais après avoir exécuté la solution, arrêtez-la et quittez-la, puis essayez d'entrer dans le concepteur, l'exception apparaît encore et encore jusqu'au redémarrage de VS. Mais j'ai trouvé la solution - tout à faire est d'ajouter ci-dessous à votre app.config
la source
Puisque la classe abstraite
public abstract class BaseForm: Form
donne une erreur et évite l'utilisation du concepteur, je suis venu avec l'utilisation de membres virtuels. Fondamentalement, au lieu de déclarer des méthodes abstraites, j'ai déclaré des méthodes virtuelles avec le moins de corps possible. Voici ce que j'ai fait:Puisqu'il
DataForm
était censé être une classe abstraite avec le membre abstraitdisplayFields
, je "simule" ce comportement avec des membres virtuels pour éviter l'abstraction. Le designer ne se plaint plus et tout fonctionne bien pour moi.C'est une solution de contournement plus lisible, mais comme elle n'est pas abstraite, je dois m'assurer que toutes les classes enfants de
DataForm
ont leur implémentation dedisplayFields
. Soyez donc prudent lorsque vous utilisez cette technique.la source
Le Concepteur Windows Forms crée une instance de la classe de base de votre formulaire / contrôle et applique le résultat d'analyse de
InitializeComponent
. C'est pourquoi vous pouvez concevoir le formulaire créé par l'assistant de projet sans même créer le projet. En raison de ce comportement, vous ne pouvez pas non plus concevoir un contrôle dérivé d'une classe abstraite.Vous pouvez implémenter ces méthodes abstraites et lever une exception lorsqu'elle ne s'exécute pas dans le concepteur. Le programmeur qui dérive du contrôle doit fournir une implémentation qui n'appelle pas votre implémentation de classe de base. Sinon, le programme planterait.
la source
Vous pouvez simplement compiler conditionnellement le
abstract
mot - clé sans interposer une classe distincte:Cela fonctionne à condition de
BaseForm
ne pas avoir de méthodes abstraites (leabstract
mot - clé empêche donc uniquement l'instanciation d'exécution de la classe).la source