Développer un contrôle utilisateur (UserControl) en C#

LEBRUN Thomas

Cet article vous apprendra comment créer et manipuler un contrôle utilisateur en C#.

N'hésitez pas à commenter cet article ! Commentez ♪

- Titre : Développer un contrôle utilisateur (UserControl) en C#

- Auteur : LEBRUN Thomas

- Parution : 26 janvier 2006

- Mise à jour : 26 janvier 2006

- Licence : Copyright ® 2006 LEBRUN Thomas. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- II. Création du UserControl dans Visual Studio

- II-A. Création du UserControl

- II-B. Utilisation des attributs

- II-B-1. Attribut Category

- II-B-2. Attribut Description

- II-B-3. Attribut Browsable

- II-B-4. Attribut DesignerSerializationVisibility

- II-C. Associer une image à votre UserControl

- III. Utilisation du UserControl

- IV. Un peu plus loin

- IV-A. Les événements

- IV-B. Les SmartTags

- IV-C. Les DesignerVerbs

- V. Conclusions

- VI. Téléchargements

I - Introduction

Le Framework .NET comporte un nombre important de contrôles utilisables dans vos applications. Cependant, il peut arriver que vous ayez besoin d'utiliser un contrôle qui est en fait, une combinaison de plusieurs contrôles de base. Pour cela, vous pouvez utiliser des contrôles utilisateurs.
Les Contrôles Utilisateurs (UserControls) sont des contrôles Dotnet que vous avez développés et que vous voulez réutiliser dans vos applications. On peut donc les assimiler à des contrôles personnalisés
Dans cet article, je vous propose une vue d'ensemble de ce qu'ils sont exactement ainsi que de leurs fonctionnalités diverses.

Tous les exemples de cet article ont été réalisés avec Visual Studio 2005 Professionnal Edition, donc la version 2.0 du Framework .NET.

II - Création du UserControl dans Visual Studio

II-A - Création du UserControl

Pour créer un UserControl, dans Visual Studio, vous avez deux possibilités :

- faites un clic droit sur le nom de votre projet, choisissez « Add New Item » puis sélectionnez « User Control » ;

- ajoutez, à votre solution, un nouveau projet de type « Windows Control Library ».

Voici ce que cela donne en image :

Et :

Vous vous retrouvez alors avec une fenêtre de ce type dans Visual Studio :

En fait, il s'agit d'une fenêtre Windows classique, mais sur laquelle il n'y a pas de bordures !
Sur ce formulaire, vous allez pouvoir utiliser toutes les capacités de Visual Studio, et en particulier le glisser/déposer de composants, pour créer votre composant personnalisé.

Tout au long de cet article, nous allons développer un contrôle d'authentification entièrement personnalisable : je vous laisse donc le soin d'architecturer l'interface graphique de celui-ci selon vos besoins et/ou idées, le tout étant d'avoir, si possible, quelque chose ressemblant à ceci :

Et voilà !
La première partie, création du contrôle, étant maintenant terminée, nous allons donc voir comment l'utiliser dans nos applications.

Une chose importante à savoir : bien que ce contrôle soit développé en C#, vous pouvez tout à fait l'utiliser avec n'importe quel autre langage Dotnet (VB.NET, J#, etc.).

II-B - Utilisation des attributs

Si vous voulez pouvoir modifier certaines propriétés de vos UserControls dans la fenêtre « Properties » (Propriétés) de Visual Studio, vous devez utiliser les attributs.
Voici une liste, non exhaustive, des attributs qu'il vous est possible d'utiliser :

- Category ;

- Description ;

- Browsable ;

- DefaultValue ;

- DefaultEvent.

Je vais détailler ici un peu plus certains de ces attributs.

II-B-1 - Attribut Category

Cet attribut (Category)vous permet de définir dans quelle catégorie vous voulez que votre propriété soit placée. En effet, par défaut, la catégorie utilisée est « Misc », mais vous pouvez tout à fait modifier cela :

Paramétrage de l'attribut Category

[Category("Configuration")]
public String Title
{
get
{
return this.gbLoginUserControl.Text;
}
set
{
this.gbLoginUserControl.Text = value;
}
}

II-B-2 - Attribut Description

L'attribut Description vous permet d'afficher, dans l'explorateur de propriétés, une petite description relative à la propriété (ou l'événement) sélectionnée. Cela peut s'avérer très pratique si vous avez besoin de donner des informations, à l'utilisateur.

Paramétrage de l'attribut Description

[Description("Le texte à afficher pour le nom d'utilisateur")]
public String LoginText
{
get
{
return this.lblLogin.Text;
}
set
{
this.lblLogin.Text = value;
}
}

II-B-3 - Attribut Browsable

Le dernier attribut dont je voulais vous parler est l'attribut Browsable. Celui-ci vous permet d'indiquer si, dans l'explorateur de propriétés, la propriété sélectionnée peut être modifiée. Dans le cas contraire, elle apparait comme grisée dans l'explorateur.
À noter que cet attribut prend en paramètre un booléen, et non pas une chaîne de caractère comme pour les autres attributs.

Paramétrage de l'attribut Browsable

[Browsable(true)]
public String PasswordText
{
get
{
return this.lblPassword.Text;
}
set
{
this.lblPassword.Text = value;
}
}

Voici le résultat que vous pouvez, par exemple, obtenir après avoir utilisé plusieurs de ces attributs :

Code avec tous les attributs réunis

[Category("Configuration"), Browsable(true), Description("Le titre que vous voulez afficher")]
public String Title
{
get
{
return this.gbLoginUserControl.Text;
}
set
{
this.gbLoginUserControl.Text = value;
}
}

[Category("Configuration"), Browsable(true), Description("Le texte à afficher pour le nom d'utilisateur")]
public String LoginText
{
get
{
return this.lblLogin.Text;
}
set
{
this.lblLogin.Text = value;
}
}

[Category("Configuration"), Browsable(true), Description("Le texte à afficher pour le mot de passe")]
public String PasswordText
{
get
{
return this.lblPassword.Text;
}
set
{
this.lblPassword.Text = value;
}
}

Ce qui donne :

Bien sûr, je ne vous l'ai pas dit avant, mais vous pouvez tout à fait combiner les attributs !

II-B-4 - Attribut DesignerSerializationVisibility

Il est possible que vous soyez amené à développer un contrôle utilisateur qui utilise une collection comme propriété, jusque là, pas de problème.
Cependant, lorsque vous utilisez votre contrôle sur un formulaire Windows, et que vous ajoutez des éléments à cette collection, rien ne change dans le Designer (et rien n'apparait dans le code d'initialisation du formulaire).
Pour que votre contrôle garde les valeurs qui ont été saisies dans le Designer, vous devrez utiliser l'attribut DesignerSerializationVisibility sur votre propriété :

Attribut DesignerSerializationVisibility

List<String> list = new List<String>();

[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
public DisplayList
{
get
{
return list;
}
}

Vous trouverez plus d'informations sur cet attribut à cette adresse.

II-C - Associer une image à votre UserControl

Vous avez la possibilité d'associer une image à votre UserControl, afin de le personnaliser encore plus et de le reconnaitre plus facilement dans la boite à outils.
Pour cela, commencez par inclure votre image à votre projet (clic droit sur le projet, puis sélectionnez « Add » => « New Item ») et, dans les propriétés, positionner « Build Action » sur « Embedded Ressource ». Cela aura pour effet d'incorporer votre image dans la DLL de votre UserControl.
Ensuite, il ne vous reste plus qu'à ajouter un attribut à votre contrôle : l'attribut ToolboxBitmap.

L'attribut ToolboxBitmap permet d'associer une image à votre contrôle, image qui sera visible dans la boite à outils. Voyons comment cela se passe :

Association d'une image au contrôle

[ToolboxBitmap(typeof(LoginuserControl), "favicon.ico")]
public partial class LoginuserControl : UserControl
{
public LoginuserControl()
{
InitializeComponent();
}
}

Le constructeur de ToolboxBitmap prend jusqu'à deux paramètres :

- le type qui définit l'assemblage dans lequel rechercher l'image du contrôle ;

- le nom de l'image.

Le résultat est assez parlant de lui-même :

III - Utilisation du UserControl

Je vous l'ai dit un peu plus haut dans cet article, un UserControl, ce n'est qu'un simple contrôle personnalisé. Par conséquent, comme tous les contrôles que vous utilisez, il se trouve également dans votre boite à outils.
S'il n'y apparait pas, vous devrez peut-être l'y ajouter vous-même. Pour cela, faites un clic droit sur votre boite à outils, puis choisissez « Choose Items ». Dans la boite de dialogue qui apparait, cliquez sur « Browse » puis allez chercher la DLL correspondant à votre UserControl :

Il ne vous reste plus qu'à valider pour voir ensuite votre contrôle apparaitre dans la boite à outils :

faites maintenant un glisser/déposer de votre contrôle sur un formulaire : Visual Studio ajoutera automatiquement les dépendances nécessaires et placera le contrôle là où vous l'avez indiqué !

IV - Un peu plus loin

Vous vous dîtes surement que pour le moment, c'est pas mal, mais vous trouvez ça léger, je me trompe ?
Rassurez-vous, jusqu'à maintenant, nous avons les principales bases de développement d'un contrôle personnalisé en C#. Nous allons maintenant étudier des concepts plus poussés qui vous permettront d'améliorer votre contrôle.

IV-A - Les événements

Comme tout contrôle .NET, votre UserControl possède des événements prédéfinis (Load, Paint, etc.), jusque là, rien de bien nouveau.
Mais dans le cadre de notre article, nous avons développé un UserControl avec 2 boutons : un bouton de validation et un bouton d'annulation. Or, lorsque vous faites un glisser/déposer de votre contrôle sur un formulaire Windows, vous vous rendez compte que vous ne pouvez pas sélectionner un des deux boutons pour changer la valeur de son événement Click.
Pour remédier à cela, vous devez créer un (ou plusieurs) événement personnalisé, ce que nous allons voir tout de suite.

Pour que votre UserControl puisse utiliser votre événement, vous devez avoir au moins deux choses :

- un délégué pour l'événement ;

- le UserControl qui contient la déclaration de l'événement.

Vous pouvez également utiliser une classe qui dérive de System.EventArgs, qui sera utilisée pour contenir les données relatives à l'événement (état, etc.).
Voyons cela par l'exemple : On commence par déclarer les délégués :

Déclaration des délégués

// Déclaration des délégués
public delegate void ValidButtonClickHandler(object sender, EventArgs e);
public delegate void CancelButtonClickHandler(object sender, EventArgs e);

Maintenant, déclarez les événements qui utilisent ces délégués :

Déclaration des événements

// Déclaration des événements qui utilisent ces délégués
[Category("Configuration"), Browsable(true), Description("Événement associé au bouton de validation")]
public event ValidButtonClickHandler BoutonValidClick;

[Category("Configuration"), Browsable(true), Description("Événement associé au bouton d'annulation")]
public event CancelButtonClickHandler BoutonCancelClick;

On crée ensuite les méthodes qui vont utiliser ces événements :

Méthodes

protected virtual void OnValidButtonClick(EventArgs e)
{
if (BoutonValidClick != null)
{
BoutonValidClick(this, e);
}
}

protected virtual void OnCancelButtonClick(EventArgs e)
{
if (BoutonCancelClick != null)
{
BoutonCancelClick(this, e);
}
}

Et voilà, vous avez (presque) fini !
En effet, à partir de maintenant, tout ce qu'il vous reste à faire, c'est d'appeler vos méthodes dans l'événement Click (ou autre) de vos boutons :

Événement Click des boutons

// On lance nos méthodes
private void btValid_Click(object sender, EventArgs e)
{
OnValidButtonClick(e);
}

private void btCancel_Click(object sender, EventArgs e)
{
OnCancelButtonClick(e);
}

Le résultat est directement visible lorsque vous utilisez votre UserControl sur un formulaire Windows :

IV-B - Les SmartTags

Les SmartTags sont une des nouveautés disponibles avec Visual Studio 2005 et le Framework .NET 2.0. Ils représentent une sorte de menu contextuel pour vos contrôles.
Et bien, sachez que vous avez la possibilité de créer vos propres SmartTags pour votre contrôle.

Pour commencer, créer une classe UserControlActionList qui héritera de System.ComponentModel.Design.DesignerActionList : cette classe contiendra la liste des propriétés qui devront être ajoutées au SmartTag :

Classe UserControlActionList

public class UserControlActionList : System.ComponentModel.Design.DesignerActionList
{
public UserControlActionList(IComponent component) : base(component)
{ }

// Liste des propriétés à ajouter au SmartTag
public String Title
{
get
{
return ((LoginuserControl)this.Component).Title;
}
set
{
PropertyDescriptor property = TypeDescriptor.GetProperties(this.Component)["Title"];
property.SetValue(this.Component, value);
}
}

public String LoginText
{
get
{
return ((LoginuserControl)this.Component).LoginText;
}
set
{
PropertyDescriptor property = TypeDescriptor.GetProperties(this.Component)["LoginText"];
property.SetValue(this.Component, value);
}
}

public String PasswordText
{
get
{
return ((LoginuserControl)this.Component).PasswordText;
}
set
{
PropertyDescriptor property = TypeDescriptor.GetProperties(this.Component)["PasswordText"];
property.SetValue(this.Component, value);
}
}

Dans cette classe, redéfinissons la méthode GetSortedActionItems qui nous permet de récupérer les actions définies par l'utilisateur.

Redéfinition de GetSortedActionItems

public override System.ComponentModel.Design.DesignerActionItemCollection GetSortedActionItems()
{
System.ComponentModel.Design.DesignerActionItemCollection items = new System.ComponentModel.Design.DesignerActionItemCollection();

items.Add(new System.ComponentModel.Design.DesignerActionHeaderItem("Paramétrage"));

items.Add(new System.ComponentModel.Design.DesignerActionPropertyItem("Title", "Définissez le titre du groupe de contrôles: ",
"Titre", "Titre du groupe de contrôle"));
items.Add(new System.ComponentModel.Design.DesignerActionPropertyItem("LoginText", "Définissez le texte du champ Login: ",
"LoginText", "Texte du champ Login"));
items.Add(new System.ComponentModel.Design.DesignerActionPropertyItem("PasswordText", "Définissez le texte du champ Password: ",
"PasswordText", "Texte du champ Password"));
items.Add(new System.ComponentModel.Design.DesignerActionPropertyItem("LoginValue", "Définissez la valeur du champ Login: ",
"LoginValue", "Valeur du champ Login"));
items.Add(new System.ComponentModel.Design.DesignerActionPropertyItem("PasswordValue", "Définissez la valeur du champ Password: ",
"PasswordValue", "Valeur du champ Password"));
items.Add(new System.ComponentModel.Design.DesignerActionPropertyItem("PasswordChar",
"Définissez le caractère utilisé pour masquer le mot de passe: ", "PasswordChar", "Valeur du caractère pour le Password"));
items.Add(new System.ComponentModel.Design.DesignerActionPropertyItem("TexteBoutonValid",
"Définissez le texte du bouton de validation: ", "TexteBoutonValid", "Texte du bouton de Validation"));
items.Add(new System.ComponentModel.Design.DesignerActionPropertyItem("TexteBoutonCancel",
"Définissez le texte du bouton d'annulation: ", "TexteBoutonCancel", "Texte du bouton d'Annulation"));

return items;
}

À noter que cette méthode vous renvoie une DesignerActionItemCollection (une collection de DesignerActionItem ) et que vous avez à votre disposition, les DesignerActionItem suivants :

- DesignerActionHeaderItem : permet d'afficher un nom de groupe de propriétés ;

- DesignerActionMethodItem : permet d'appeler une méthode de l'ActionList ;

- DesignerActionPropertyItem : permet d'afficher/éditer une propriété ;

- DesignerActionTextItem : permet d'afficher une ligne de texte.

Une fois que vous avez fait cela, il ne vous reste plus qu'à créer le SmartTag. Cela se fait en créant une classe (par exemple DesignerUserControl), qui hérite de System.Windows.Forms.Design.ControlDesigner.
Dans cette classe, nous allons redéfinir une propriété, ActionLists, qui renvoie une System.ComponentModel.Design.DesignerActionListCollection et qui nous permet de récupérer la liste de tous les éléments qui seront affichés dans le SmartTag.

Redéfinition de ActionLists

public class DesignerUserControl : System.Windows.Forms.Design.ControlDesigner
{
// On définit la liste des actions du SmartTag
public override System.ComponentModel.Design.DesignerActionListCollection ActionLists
{
get
{
System.ComponentModel.Design.DesignerActionListCollection collectionAction = new
System.ComponentModel.Design.DesignerActionListCollection();
UserControlActionList designerActionList = new UserControlActionList(this.Control);
collectionAction.Add(designerActionList);

return collectionAction;
}
}
}

Et voilà, vous avez terminé la création de votre SmartTag. Pour l'utiliser, c'est très simple : il vous suffit d'ajouter l'attribut Designer à votre UserControl :

Association du SmartTag au UserControl

[Designer(typeof(DesignerUserControl))]
public partial class LoginuserControl : UserControl
{
public LoginuserControl()
{
InitializeComponent();
}
}

Et observez le résultat lorsque vous utilisez votre contrôle sur un formulaire Windows :

Avant de passer à la suite, voici quelques conseils pour la création de SmartTag :

- ajouter l'attribut [Docking(DockingBehavior.Ask)] à votre UserControl vous permet d'afficher, dans le SmartTag, la commande « Dock in parent container » ;

- vous devez rajouter une référence au namespace System.Design ;

- vous devez ajouter les using correspondants à System.Windows.Forms.Design et System.ComponentModel.Design.

Enfin, voici un lien vers un autre article traitant des SmartTags : https://odelmotte.developpez.com/tutoriels/dotnet/controlesavances/?page=SmartTag (par Olivier Delmotte)

IV-C - Les DesignerVerbs

Les DesignerVerbs sont des liens cliquables qui apparaissent dans l'explorateur de propriétés de Visual Studio, dans le menu contextuel du contrôle et dans le SmartTags.
Comme pour les SmartTags, pour créer un DesignerVerb, il faut créer une classe qui héritera de ControlDesigner. Là encore, nous allons surcharger une propriété, Verbs, qui renvoie une DesignerVerbCollection, et qui est utilisée pour avoir la liste de tous les DesignerVerb.

Création d'un DesignerVerb

public class DesignerUserControl : System.Windows.Forms.Design.ControlDesigner
{
public override System.ComponentModel.Design.DesignerVerbCollection Verbs
{
get
{
// On définit une collection de DesignerVerbs
DesignerVerbCollection collectionVerbs = new DesignerVerbCollection();

// Ensuite, un à un, on ajoute des DesignerVerb à cette collection
collectionVerbs.Add(new DesignerVerb("Afficher SmartTag",
new EventHandler(this.EventHandlerDisplaySmartTagVerbs)));
collectionVerbs.Add(new DesignerVerb("Visiter le site Web de l'auteur",
new EventHandler(this.EventHandlerVisitWebsiteVerbs)));

return collectionVerbs;
}
}
}

Comme vous pouvez le voir, la collection DesignerVerbCollection n'est qu'un ensemble de DesignerVerb. Le constructeur de la classe DesignerVerb prend en paramètre deux choses :

- le texte du DesignerVerb, qui sera affiché dans le PropertyGrid, le menu contextuel et le SmartTag ;

- l'événement qui est lié au DesignerVerb, et qui surviendra lorsque l'on cliquera dessus.

Dans mon cas, les événements sont très simples :

Événements liés aux DesignerVerb

private void EventHandlerDisplaySmartTagVerbs(object sender, EventArgs e)
{
MessageBox.Show("Vous avez cliqué sur Afficher SmartTag");
}

private void EventHandlerVisitWebsiteVerbs(object sender, EventArgs e)
{
System.Diagnostics.Process.Start("http://morpheus.developpez.com");
}

Mais libre à vous de faire des choses plus complexes ;)

Enfin, pensez aussi à marquer votre contrôle avec l'attribut Designer :

Association des DesignerVerbs au UserControl

[Designer(typeof(DesignerUserControl))]
public partial class LoginuserControl : UserControl
{
public LoginuserControl()
{
InitializeComponent();
}
}

Voici le résultat en image :

Si votre DesignerVerb n'apparait pas dans le PropertyGrid, mais bien dans le menu contextuel et/ou le SmartTag, faites un clic droit sur le PropertyGrid et assurez-vous que « Commands » est coché.

V - Conclusions

Grâce à cet article, vous avez pu vous rendre compte que développer un UserControl (Contrôle Utilisateur) s'avère extrêmement simple et rapide.
Vous avez également appris comment le personnaliser le plus possible, pour faire en sorte qu'il soit vraiment adapté à vos besoins, et le plus simple d'utilisation possible.

VI - Téléchargements

Les sources du contrôle d'authentification : Sources