Bewise

Nous développons... votre avance

Nouveautés du support design-time .NET 2.0

SLF
26/06/2006 - Yann Faure
Télécharger la version Word
Télécharger les sources

Application à la construction d’un contrôle Wizard

Le Framework .NET au-delà du runtime fournit un support du mode design-time pour la création de vos applications. La version 2.0 enrichit ce support en améliorant ou ajoutant un certain nombre de fonctionnalités.

Afin de voir par l’exemple ces fonctionnalités, nous allons développer un contrôle nommé Wizard permettant de créer des assistants pour Windows Forms à la manière du contrôle Wizard d’ASP.NET 2.0.

Le design-time : rappels importants

Avant de rentrer dans les nouveautés du design-time .NET 2.0, un petit rappel des grands principes en les appliquant à la création de notre contrôle Wizard.

Le fonctionnement du design-time pour les Windows Forms consiste à créer un type (le designer qui hérite généralement de ControlDesigner) associé au contrôle via l’attribut Designer afin d’enrichir celui-ci de fonctionnalités en mode design-time.

Dans notre exemple, c’est le contrôle Wizard qui va contenir toutes les propriétés, événements et méthodes utiles à la gestion de la logique runtime de l’assistant et le designer WizardDesigner qui va gérer les spécificités de l’exécution en mode design-time.

La déclaration du contrôle et association au designer s’effectue ainsi :

<Designer(GetType(Bewise.Windows.Forms.Design.WizardDesigner))> _ <DefaultProperty("WizardSteps")> _ <DefaultEvent("ActiveStepChanged")> _ <ToolboxBitmap(GetType(Bewise.Windows.Forms.Wizard), "WizardIcon.bmp")> _ Pulic Class Wizard Inherits UserControl ...

clip_image002

Dans cette classe nous trouvons l’équivalent du contrôle Wizard d’ASP.NET 2.0 adapté au monde Windows Forms ; voici les propriétés dont nous parlerons dans cet article :

· Une propriété WizardSteps de type WizardStepCollection qui va s’occuper de stocker les différentes étapes de l’assistant. Afin de gérer correctement la sauvegarde des données contenues dans la collection en design il est nécessaire d’y associer l’attribut <DesignerSerializationVisibility(DesignerSerializationVisibility.Content)>.

· Une propriété DisplayTopBar permettant d’afficher ou pas le bandeau du haut

· Une propriété SeparatorStyle permettant d’afficher des styles de bordures différentes

· Une propriété Image permettant de gérer l’image à afficher dans le bandeau

La déclaration du designer va ensuite se réaliser ainsi :

Pulic Class WizardDesigner Inherits ControlDesigner ...

clip_image004

Dans notre cas, il est nécessaire afin que le développeur ait déjà deux étapes créées lorsqu’il va ajouter notre contrôle sur le formulaire et aucunes s’il instancie le contrôle Wizard par code, bien entendu. Ceci est possible grâce à la redéfinition d’InitializeNewComponent dans la classe WizardDesigner qui sera appelé par l’environnement de design (par exemple Visual Studio 2005).

Lorsque le designer modifie les propriétés du contrôle (accessible grâce à la propriété Control du designer), il est important de notifier l’environnement de design qu’un contrôle a été ajouté/modifié/supprimé. Pour la création et la suppression, c’est au HostDesigner que la responsabilité incombe via CreateComponent et RemoveComponent. Pour la modification, il faut aussi que le HostDesigner en soit notifié : c’est à dire utiliser le service IComponentChangeService auquel est abonné le HostDesigner. Pour simplifier cela, on peut passer par la classe PropertyDescriptor et notamment la méthode SetValue qui réalise cela pour nous.

' Création d'une WizardStep newWizardStep = DirectCast(host.CreateComponent(GetType(WizardStep)), WizardStep) descriptor = TypeDescriptor.GetProperties(GetType(WizardStep)).Item("Title") descriptor.SetValue(newWizardStep, "Step 1") ' Ajout à la collection _wizardControl.WizardSteps.Add(newWizardStep)

Comme plusieurs opérations sont effectuées, une encapsulation dans une transaction est nécessaire à des fins de performance et de gestion de l’undo. C’est encore un service du HostDesigner.

Using transaction As DesignerTransaction = host.CreateTransaction() ... transaction.Commit() End Using

Enfin, il faut gérer les différentes étapes pour design ; c’est à dire permettre au développeur de cliquer sur les boutons « suivant » et « précédent » afin qu’il puisse glisser des contrôles sur chacune des étapes.

Premier soucis, par défaut, les événements en design-time (clavier, souris etc.) ne sont pas transmis au contrôle. Là encore, le designer peut être le bon allié en utilisant la méthode GetHitTest qui retournera vrai lorsque la souris sera au-dessus des boutons.

Protected Overrides Function GetHitTest(ByVal point As Point) As Boolean

Deuxième soucis, nous avons définis les différentes étapes comme des WizardStep (héritant de Panel). Ceux-ci ne sont pas aptes à accepter en design des contrôles puisque ces Panel sont internes au UserControl. Pour cela, deux solutions :

· Soit les activer en mode design via la méthode EnableDesignMode du designer parent (WizardDesigner). Cette solution implique d’exposer une propriété présentant notre WizardStep or ici ce n’est pas une simple propriété mais une collection.

· Soit créer un designer spécifique au WizardStep : le WizardStepDesigner qui héritera non pas de ControlDesigner classiquement mais de ParentControlDesigner qui permet justement d’accepter l’imbrication de contrôles en design. C’est la solution adoptée :

<Designer(GetType(Bewise.Windows.Forms.Design.WizardStepDesigner))> _ <ToolboxItem(False)> _ <DefaultProperty("Title")> _ <DesignTimeVisible(False)> _ Public Class WizardStep Inherits Panel ... Friend Class WizardStepDesigner Inherits ParentControlDesigner ...

Le troisième et dernier souci consiste à interdire les actions associées au redimensionnement du WizardStep : il doit tout le temps remplir l’espace du milieu. Pour cela, le designer proposer de redéfinir les méthodes PreFilterProperties et PreFilterEvents pour indiquer à l’environnement de développement de ne pas permettre la lecture/modification des propriétés et événements.

Le design-time 2.0 : nouveautés importantes

La version 2.0 du Framework .NET apporte des avancées ergonomiques pour le développeur qui sont exploitées par Visual Studio 2005 : les listes d’actions contextuelles, l’aide au positionnement et l’ornement.

Support des listes d’actions : les smart tags

Une des nouvelles fonctionnalités de Visual Studio 2005 est de proposer des smart tags permettant d’accéder rapidement à des fonctionnalités courantes du contrôle (ou plus généralement du composant).

La DataGridView propose par exemple ce smart tag :

clip_image006

Les actions présentées sont une extension des verbes que vous connaissiez en .NET 1.x. Comme auparavant, il faut créer un designer associé à votre contrôle mais ensuite il ne faut pas redéfinir la propriété Verbs mais la propriété ActionLists. Si vous avez déjà des verbes définis (et pas d’ActionLists), une ActionLists sera automatiquement créée et inclura les verbes.

Private _actionLists As DesignerActionListCollection Public Overrides ReadOnly Property ActionLists() As DesignerActionListCollection Get If _actionLists Is Nothing Then _actionLists = New DesignerActionListCollection() _actionLists.Add(New WizardActionList(Me.Component)) End If Return _actionLists End Get End Property

Cette propriété retourne une collection de listes d’actions (DesignerActionListCollection). Pour ce faire, il faut créer une classe dérivée d’ActionList pour définir une liste personnalisée d’actions.

Public Class WizardActionList Inherits DesignerActionList

Dans notre cas, il serait par exemple utile de proposer : la sélection d’une étape, l’appel à la gestion des étapes, le choix du style de bordure, l’affichage ou non du bandeau, le référencement d’une image et la modification du docking.

clip_image008

Pour ce faire, la propriété GetSortedActionItems est à redéfinir et doit renvoyer la liste des DesginerActionItem qui sont les actions appelables dans le smart tag.

Ces DesignerAction peuvent être en standard :

· Des entêtes pour catégoriser d’autres actions (classe DesignerActionHeaderItem).

· Des actions de modification de propriété (classe DesignerActionPropertyItem). Cette action va tout simplement modifier une propriété (référencée dans le constructeur). Attention, cette propriété n’est pas une propriété du contrôle (ou plus généralement du composant) mais une propriété de la classe dérivée de DesignerActionList. La propriété Component de cette classe permet de récupérer le composant (dans notre cas le contrôle Wizard) associé.

· Des actions d’invocation de méthodes (classe DesignerActionMethodItem). Notez que dans ce cas vous avez la possibilité de rendre cette action visible au même titre qu’un verbe classique en modifiant la propriété IncludeAsDesignerVerb (accessible aussi dans le constructeur).

· Des actions d’invocation de méthodes (classe DesignerActionTextItem)

· Des actions d’invocation de méthodes (classe DesignerActionTextItem)

Cela se traduit dans notre cas par :

Public Overrides Function GetSortedActionItems() As DesignerActionItemCollection ' Déclarations Dim _designerActionItems As New DesignerActionItemCollection ' Wizard steps actions _designerActionItems.Add(New DesignerActionHeaderItem("Wizard steps")) _designerActionItems.Add(New DesignerActionPropertyItem("ActiveStepIndex", _ "Step", "Wizard steps")) _designerActionItems.Add(New DesignerActionMethodItem(Me, _ "startWizardCollectionEditor", "Add/Remove steps...", "Wizard steps", _ "Allow to add/remove WizardSteps", True)) ... Return _designerActionItems End Function

Remarquez, que le mode d’édition associé à la propriété sera le même que celui utilisé dans la fenêtre de propriété (si vous avez par exemple à saisir une valeur énumérée, une liste déroulante sera utilisée dans la fenêtre de propriété et dans le smart tag). C’est le cas pour ici pour la propriété SeparatorStyle. Pour la sélection de l’étape active, on va utiliser la propriété ActiveStepIndex mais en utilisant un TypeConverter différent nommé ActiveStepIndexConverter permettant non pas de saisir un index mais de sélectionner l’étape dans une liste. Il est déclaré ainsi :

Friend Class ActiveStepIndexConverter Inherits TypeConverter ...

Support de l’aide au positionnement : les snap lines

L’alignement et l’espacement aidés sont une des autres fonctionnalités bien utiles à offrir au développeur. En effet, lors du déplacement d’un contrôle des lignes d’alignement et d’espacement apparaissent sur lesquelles est attiré le contrôle : les SnapLine.

clip_image010

En standard, le ControlDesigner va supporter cette fonctionnalité et proposer des lignes pour votre contrôle. Cependant, vous avez la possibilité de redéfinir la propriété SnapLines du designer pour les modifier.

Dans notre exemple, nous souhaiterions ajouter des SnapLine afin que le développeur puisse aligner aussi avec les boutons du Wizard.

clip_image012

Pour cela, il suffit d’ajouter aux SnapLines de base d’autres SnapLine personnalisées :

Public Overrides ReadOnly Property SnapLines() As System.Collections.IList Get Dim snapLinelist As New ArrayList snapLinelist.AddRange(MyBase.SnapLines) snapLinelist.Add(New SnapLine(SnapLineType.Top, ...)) snapLinelist.Add(New SnapLine(SnapLineType.Bottom, ...)) ... Return snapLinelist End Get End Property

Notez, qu’il est possible de définir 7 types de SnapLine :

· Left, Right, Top et Bottom pour gérer les alignements sur les côtés

· Baseline pour gérer l’alignement par rapport au contenu interne du contrôle (le texte d’un bouton par exemple).

· Vertical et Horizontal pour gérer l’espacement

Enfin, sachez qu’il est possible pour chacune des SnapLine d’indiquer un niveau de priorité d’apparition de la SnapLine et de désactiver l’usage des SnapLine en redéfinissant la propriété ParticipatesWithSnapLines.

Support de l’ornement graphique : Adorner, Glyph et Behavior

Les smart tags sont des aides bien utiles pour le développeur afin d’accéder rapidement au paramétrage des fonctions courantes du contrôle. Il est cependant parfois souhaitable de pouvoir agir directement sur l’interface du contrôle. C’est la technique des Adorner qui sont tout simplement des sortes de calques graphiques empilés sur lesquels il est possible de dessiner des éléments (nommés Glyph) qui permettront d’enrichir le design en ayant un comportement spécifique (nommé Behavior).

clip_image013

Le smart tag est un bon exemple de l’utilisation des adorners en rendant accessible via le Glyph clip_image014 les différentes actions exposées par le designer.

Il serait par exemple utile de pouvoir masquer ou afficher l’intitulé de l’étape de l’assistant (ce qui correspond à la propriété DisplayTopBar du contrôle) graphiquement.

La première étape va consister à créer un adorner qui gérera notre Glyph. Ceci est réalisé par l’intermédiaire du BehaviorService qui est un des nouveaux services design-time disponibles :

_behaviorService = TryCast(Me.GetService(GetType(BehaviorService)), BehaviorService) If _behaviorService IsNot Nothing Then _adorner = New Adorner() _behaviorService.Adorners.Add(_adorner) ...

Une fois cet Adorner créé, il faut ajouter un Glyph et un Behavior personnalisés :

Dim behavior As New ExpansionBehavior _adorner.Glyphs.Add(New ExpansionGlyph(..., behavior))

La classe ExpansionGlyph va donc se charger de la représentation graphique, c’est à dire afficher une image de réduction et d’agrandissement qui sera cliquable (à la manière du ToolStripContainer) :

clip_image016

clip_image018

La classe doit hériter de ControlBodyGlyph. Le dessin du Glyph est effectué dans la redéfinition de la méthode Paint. Notez que les coordonnées utilisées doivent être relative à l’Adorner et non au formulaire ; le BehaviorService dispose de méthodes permettant de faire cette translation (notamment ControlToAdornerWindow et ControlRectInAdornerWindow).

De plus, la propriété Bounds est redéfinie pour indiquer quelle zone est utilisée par le Glyph et la méthode GetHitTest est redéfinie pour retourner le curseur en fonction du point courant de la souris.

Enfin, il est utile d’intercepter le changement de sélection en design et le changement des propriétés de positionnement pour rafraîchir le positionnement du Glyph. Ceci est réalisé en interceptant les événements des services ISelectionService et IComponentChangeService.

La classe ExpansionBehavior se charge quant à elle de la logique. Son rôle est d’intercepter l’événement de clic sur le Glyph pour changer l’état. La classe doit hériter de Behavior et redéfinir la méthode OnMouseUp (le support du glisser/déposer est aussi fourni).

Conclusion

Les capacités du design-time dans le Framework .NET 2.0 sont importantes, vous l’avez vu. Elles permettent au développeur de rapidement personnaliser ses contrôles ou composants. Nous n’avons cependant pas vu toutes ses capacités. On pourrait encore évoquer la création d’une gestion personnalisé de l’agencement graphique d’un contrôle (via le LayoutEngine), du contrôle de l’undo (via l’UndoEngine), de la substitution d’instances via les TypeDescriptor etc.

Mais, ce qui est surtout important de noter, c’est que cette infrastructure est propre au Framework .NET et non à Visual Studio qui pour sa part ne fait qu’en tirer parti élégamment. Cela vous laisse libre de créer vos propres environnements de développement spécialisés.

> Tous les articles

Commentaires

aucun commentaire
Page 1/1
   
Connexion
  • Accueil
  • Plan du site
  • Contact
Bewise TV, Blog technique, Webcasts...

Votre carrière et nous

  • Nos offres
  • Votre candidature
Ignorer les liens de navigation > Accueil > Nos Métiers > Solutions Langages et Framework > Détail Article
Ignorer les liens de navigation
Nous
Nos Métiers
Vous Former
Nos Evénements
Nos Références
Nos Activités
Nos Certifications
Nos Chiffres
Le Groupe
Nos Partenaires
On Parle de Nous
Votre Carrière et Nous
Nous Contacter
Défiler vers le haut
Défiler vers le bas
Administration, Système et Communication
Architecture, Méthodes, Industrialisation
Décisionnel et Gestion des Données
Nouvelles Interfaces Utilisateurs
Portail et Travail Collaboratif
Solutions Langages et Framework
Solutions Web Avancées
Défiler vers le haut
Défiler vers le bas
Nos cours
Le Planning
Offres promotionnelles
Défiler vers le haut
Défiler vers le bas
TechDays'12
TechLunch
Windows 8 Camp
Défiler vers le haut
Défiler vers le bas
  • Infos légales
  • Lettre du Regional Director
  • Revue de presse