110 votes

Constructeur 'UserControl' avec paramètres en C#

Vous pouvez dire que je suis fou, mais je suis le genre de personne qui aime les constructeurs avec des paramètres (si nécessaire), par opposition à un constructeur sans paramètres suivi d'une définition des propriétés. Mon raisonnement est le suivant : si les propriétés sont nécessaires pour construire l'objet, elles doivent être placées dans le constructeur. J'en tire deux avantages :

  1. Je sais que lorsqu'un objet est construit (sans erreur/exception), mon objet est bon.
  2. Cela permet d'éviter d'oublier de définir une certaine propriété.

Cet état d'esprit commence à me faire souffrir en ce qui concerne le développement des formulaires et des contrôles d'utilisation. Imaginez ceci UserControl :

public partial class MyUserControl : UserControl
{
  public MyUserControl(int parm1, string parm2)
  {
    // We'll do something with the parms, I promise
    InitializeComponent();
  }
}

Au moment de la conception, si je laisse tomber cette UserControl sur un formulaire, j'obtiens un Exception :

Échec de la création du composant 'MyUserControl' ...
System.MissingMethodException - Aucun constructeur sans paramètre n'a été défini pour cet objet.

Il me semble que le seul moyen de contourner ce problème était d'ajouter le constructeur par défaut (à moins que quelqu'un d'autre ne connaisse un autre moyen).

public partial class MyUserControl : UserControl
{
  public MyUserControl()
  {
    InitializeComponent();
  }

  public MyUserControl(int parm1, string parm2)
  {
    // We'll do something with the parms, I promise
    InitializeComponent();
  }
}

Le but de ne pas inclure le constructeur sans paramètre était d'éviter de l'utiliser. Et je ne peux même pas utiliser le DesignMode pour faire quelque chose comme :

public partial class MyUserControl : UserControl
{
  public MyUserControl()
  {
    if (this.DesignMode)
    {
      InitializeComponent();
      return;
    }

    throw new Exception("Use constructor with parameters");
  }
}

Cela ne fonctionne pas non plus :

if (LicenseManager.UsageMode == LicenseUsageMode.Designtime)

Bien, on avance...

J'ai mon constructeur sans paramètre, je peux le déposer sur le formulaire, et le formulaire InitializeComponent ressemblera à ceci :

private void InitializeComponent()
{
  this.myControl1 = new MyControl();

  // blah, blah
}

Et croyez-moi, parce que je l'ai fait (oui, en ignorant les commentaires générés par Visual Studio), j'ai essayé de m'amuser et j'ai passé des paramètres à InitializeComponent afin que je puisse les passer au constructeur de MyControl .

Ce qui m'amène à ceci :

public MyForm()
{
  InitializeComponent(); // Constructed once with no parameters

  // Constructed a second time, what I really want
  this.myControl1 = new MyControl(anInt, aString);  
}

Pour moi, utiliser un UserControl avec des paramètres au constructeur, je dois ajouter un deuxième constructeur dont je n'ai pas besoin ? Et instancier deux fois le contrôle ?

J'ai l'impression que je dois faire quelque chose de mal. Vous avez des idées ? Opinions ? Une assurance (si possible) ?

71voto

Greg D Points 24218

Les décisions de conception prises concernant le fonctionnement de Windows Forms excluent plus ou moins les .ctors paramétrés pour les composants Windows Forms. Vous pouvez les utiliser, mais si vous le faites, vous sortez des mécanismes généralement approuvés. Windows Forms préfère plutôt l'initialisation des valeurs via les propriétés. Il s'agit d'une technique de conception valable, même si elle n'est pas largement utilisée.

Cela présente cependant quelques avantages.

  1. Facilité d'utilisation pour les clients. Le code client n'a pas besoin de rechercher un tas de données, il peut immédiatement créer quelque chose et le voir avec des résultats raisonnables (même s'ils sont inintéressants).
  2. Facilité d'utilisation pour le concepteur. Le code du concepteur est plus clair et plus facile à analyser en général.
  3. Décourage les dépendances de données inhabituelles au sein d'un même composant. (Bien que même Microsoft ait raté ce coup avec le programme SplitContainer )

Il y a beaucoup d'aide dans les formulaires pour travailler correctement avec le concepteur dans cette technique également. Des choses comme DefaultValueAttribute , DesignerSerializationVisibilityAttribute y BrowsableAttribute vous donnent la possibilité de fournir une expérience client riche avec un minimum d'efforts.

(Ce n'est pas le seul compromis qui a été fait pour l'expérience client dans les formulaires Windows. Les composants de la classe de base abstraite peuvent aussi devenir poilus).

Je vous suggère de vous en tenir à un constructeur sans paramètre et de travailler dans le respect des principes de conception des formulaires Windows. S'il y a des conditions préalables réelles que votre UserControl doivent être appliquées, encapsulez-les dans une autre classe, puis affectez une instance de cette classe à votre contrôle via une propriété. Cela permettra également une meilleure séparation des préoccupations.

36voto

Kevin Kibler Points 5471

Il existe deux paradigmes concurrents pour la conception des classes :

  1. Utilisez des constructeurs sans paramètres et définissez ensuite un certain nombre de propriétés.
  2. Utiliser les constructeurs paramétrés pour définir les propriétés dans le constructeur.

El Concepteur de formulaires Windows de Visual Studio vous oblige à fournir un constructeur sans paramètre sur les contrôles afin de fonctionner correctement. En fait, il ne requiert un constructeur sans paramètre que pour instancier les contrôles, mais pas pour les concevoir (le concepteur analysera la méthode InitializeComponent lors de la conception d'un contrôle). Cela signifie que vous pouvez utiliser le concepteur pour concevoir un formulaire ou un contrôle utilisateur sans constructeur sans paramètre, mais que vous ne pouvez pas concevoir un autre contrôle pour utiliser ce contrôle car le concepteur ne parviendra pas à l'instancier.

Si vous n'avez pas l'intention d'instancier vos contrôles de manière programmatique (c'est-à-dire de construire votre interface utilisateur "à la main"), ne vous souciez pas de créer des constructeurs paramétrés, puisqu'ils ne seront pas utilisés. Même si vous avez l'intention d'instancier vos contrôles de manière programmatique, vous pouvez fournir un constructeur sans paramètre afin qu'ils puissent toujours être utilisés dans le concepteur si nécessaire.

Quel que soit le paradigme que vous utilisez, c'est aussi généralement une bonne idée de placer un long code d'initialisation dans le fichier OnLoad() d'autant plus que le DesignMode fonctionnera au moment du chargement, mais ne fonctionnera pas dans le constructeur.

10voto

Antony Koch Points 964

Je recommande

public partial class MyUserControl : UserControl
{
    private int _parm1;
    private string _parm2;

    private MyUserControl()
    {
        InitializeComponent();
    }

    public MyUserControl(int parm1, string parm2) : this()
    {
        _parm1 = parm1;
        _parm2 = parm2;
    }
}

De cette façon, le constructeur de base est toujours appelé en premier et toutes les références aux composants sont valides.

Vous pouvez ensuite surcharger le ctor public si nécessaire, en vous assurant que le contrôle est toujours instancié avec les valeurs correctes.

De toute façon, vous vous assurez que le ctor sans paramètre n'est jamais appelé.

Je ne l'ai pas testé, donc si elle tombe, je m'en excuse !

0 votes

Si ma mémoire est bonne, à cause du constructeur privé "sans paramètre", je ne pouvais toujours pas l'utiliser en mode conception. Mais, +1 quand même.

5voto

Reed Copsey Points 315315

Il s'agit malheureusement d'un problème de conception qui se produira fréquemment, et pas seulement dans l'espace de contrôle.

Il y a souvent des situations où vous avez besoin d'un constructeur sans paramètre, même si un constructeur sans paramètre n'est pas idéal. Par exemple, de nombreux types de valeurs seraient mieux sans constructeur sans paramètre, mais il est impossible d'en créer un qui fonctionne de cette façon.

Dans ces situations, vous devez simplement concevoir le contrôle/composant de la meilleure façon possible. L'utilisation de paramètres par défaut raisonnables (et de préférence les plus courants) peut être d'une grande aide, car vous pouvez au moins (avec un peu de chance) initialiser le composant avec une bonne valeur.

Essayez également de concevoir le composant de manière à pouvoir modifier ces propriétés après la génération du composant. Avec les composants Windows Forms, c'est généralement le cas, puisque vous pouvez faire à peu près n'importe quoi jusqu'au moment du chargement en toute sécurité.

Encore une fois, je suis d'accord - ce n'est pas idéal, mais c'est juste quelque chose avec lequel nous devons vivre et travailler.

4voto

Fredrik Mörk Points 85694

En bref, le concepteur est le genre de personne qui aime les constructeurs sans paramètre. Donc, à ma connaissance, si vous voulez vraiment utiliser des constructeurs avec paramètres, vous êtes probablement obligé de contourner le problème d'une manière ou d'une autre.

1 votes

"Le concepteur est le genre de gars" ... bon truc (drôle) ! Oui, il semble que je vais devoir utiliser des solutions de rechange. Je voulais juste m'assurer qu'il n'y avait pas une façon astucieuse de faire les choses, ou qu'il y avait une "règle connue" telle que : "Vous ne faites jamais ce que je faisais avec mon UserControl"

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X