Personnalisation des outils et de la boîte à outils

Vous devez définir les éléments de boîte à outils pour les éléments que les utilisateurs sont autorisés à ajouter à leurs modèles. Il existe deux types d'outils : les outils d'élément et les outils de connexion. Dans le concepteur généré, un utilisateur peut sélectionner un outil d'élément pour déplacer des formes vers le diagramme et sélectionner un outil de connexion pour tracer des liens entre les formes. En général, les outils d'élément permettent aux utilisateurs d'ajouter des instances de classes de domaine à leurs modèles et les outils de connexion d'ajouter des instances de relations de domaine.

Définition de la boîte à outils

Dans l'Explorateur DSL, développez le nœud Éditeur et les nœuds sous-jacents. Généralement, vous voyez une arborescence qui se présente ainsi :

Editor
     Toolbox Tabs
        MyDsl          //a tab
           Tools
               ExampleElement      // an element tool
               ExampleRelationship // a connection tool

Dans cette partie de l'Explorateur DSL, vous pouvez :

  • Créer des onglets. Les onglets définissent les en-têtes de section de la boîte à outils.

  • Créer des outils.

  • Copier et coller des outils.

  • Monter ou descendre des outils dans la liste.

  • Supprimer des onglets et des outils.

Important

Pour ajouter ou coller des éléments dans un Explorateur DSL, cliquez avec le bouton droit sur le grand-parent du nouveau nœud. Par exemple, pour ajouter un outil, faites un clic droit sur l’onglet, et non sur le nœud Outils. Pour ajouter un onglet, faites un clic droit sur le nœud Éditeur.

La propriété Icône de boîte à outils de chaque outil fait référence à un fichier bitmap 16 x 16. Ces fichiers sont généralement conservés dans le dossier Dsl\Resources.

La propriété Classe d'un outil d'élément fait référence à une classe de domaine concrète. Par défaut, l'outil crée les instances de cette classe. Cependant, vous pouvez écrire le code pour que l'outil crée des groupes d'éléments ou des éléments de différents types.

La propriété Générateur de connexions d'un outil de connexion fait référence à un générateur de connexions qui définit les types d'éléments auxquels l'outil peut se connecter, ainsi que les relations qu'il crée entre les types. Les générateurs de connexions sont définis comme nœuds dans l'Explorateur DSL. Les générateurs de connexions sont créés automatiquement lorsque vous définissez les relations de domaine, mais vous pouvez écrire le code qui permet de les personnaliser.

Pour ajouter un outil à la boîte à outils

  1. Vous créez généralement un outil d'élément après avoir créé une classe de formes et l'avoir mappée sur une classe de domaines.

    Vous créez généralement un outil de connecteur après avoir créé une classe de connecteurs et l'avoir mappée sur une relation de référence.

  2. Dans l'Explorateur DSL, développez le nœud Éditeur et le nœud Onglets de la boîte à outils.

    Faites un clic droit sur un nœud d'onglet de la boîte à outils, puis cliquez sur Ajouter un nouvel outil d'élément ou sur Ajouter un nouvel outil de connexion.

  3. Définissez la propriété Icône de boîte à outils pour faire référence à un fichier bitmap 16 x 16.

    Si vous voulez définir une nouvelle icône, créez un fichier bitmap dans l'Explorateur de solutions dans le dossier Dsl\Resources. Le fichier doit avoir les valeurs de propriété suivantes : Action de génération = Contenu ; Copier dans le répertoire de sortie = Ne pas copier.

  4. Pour un outil d’élément : définir la propriété Classe de l’outil pour faire référence à une classe de domaine concrète mappée à une forme.

    Pour un outil de connecteur : définir la propriété Générateur de connexions de l’outil sur l’un des éléments proposés dans la liste déroulante. Les générateurs de connexions sont automatiquement créés lorsque vous mappez un connecteur sur une relation de domaine. Si vous avez créé récemment un connecteur, vous devez normalement sélectionner le générateur de connexions associé.

  5. Pour tester le DSL, appuyez sur F5 ou CTRL+F5, et dans l'instance expérimentale de Visual Studio, ouvrez un exemple de fichier modèle. Le nouvel outil doit apparaître sur la boîte à outils. Faites-le glisser vers le diagramme pour vérifier qu'un nouvel élément a été créé.

    Si l'outil n'apparaît pas, arrêtez l'instance expérimentale de Visual Studio. Dans le menu Démarrer de Windows, tapez réinitialiser Visual Studio, puis exécutez la commande Réinitialiser l’instance expérimentale Microsoft Visual Studio correspondant à votre version de Visual Studio. Dans le menu Générer, cliquez sur Régénérer la solution. Puis, testez à nouveau le DSL.

Personnalisation des outils d'élément

Par défaut, l'outil crée une seule instance de la classe spécifiée, mais vous pouvez modifier ce comportement de deux façons :

  • Définissez Directives de fusion d'éléments sur d'autres classes, en leur permettant d'accepter de nouvelles instances de cette classe et de créer des liens supplémentaires lorsque le nouvel élément est créé. Par exemple, vous pouvez autoriser l'utilisateur à déposer un Commentaire sur un autre élément et à créer ainsi un lien de référence entre les deux éléments.

    Ces personnalisations affectent aussi ce qui se passe quand l'utilisateur colle ou fait glisser un élément.

    Pour plus d’informations, consultez Personnalisation de la création et du mouvement des éléments.

  • Écrivez le code pour personnaliser l'outil de telle sorte qu'il puisse créer des groupes d'éléments. L'outil est initialisé par des méthodes de ToolboxHelper.cs que vous pouvez remplacer. Pour plus d'informations, consultez Création de groupes d'éléments à partir d'un outil.

Création de groupes d'éléments à partir d'un outil

Chaque outil d'élément contient un prototype des éléments qu'il doit créer. Par défaut, chaque outil d'élément crée un seul élément, mais il est également possible de créer un groupe d'objets liés avec un seul outil. Pour ce faire, vous initialisez l'outil avec un ElementGroupPrototype qui contient les éléments liés.

L'exemple suivant est extrait d'un DSL dans lequel se trouve un type Transistor. Chaque Transistor possède trois Terminaux nommés. L'outil d'élément pour Transistors stocke un prototype contenant quatre éléments de modèle et trois liens de relation. Lorsque l'utilisateur déplace l'outil sur le diagramme, le prototype est instancié et lié à la racine du modèle.

Ce code remplace une méthode définie dans Dsl\GeneratedCode\ToolboxHelper.cs.

Pour plus d’informations sur la personnalisation du modèle à l’aide du code de programme, consultez Navigation et mise à jour d’un modèle dans le code du programme.

using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;

  public partial class CircuitsToolboxHelper
  {
    /// <summary>
    /// Toolbox initialization, called for each element tool on the toolbox.
    /// This version deals with each Component subtype separately.
    /// </summary>
    /// <param name="store"></param>
    /// <param name="domainClassId">Identifies the domain class this tool should instantiate.</param>
    /// <returns>prototype of the object or group of objects to be created by tool</returns>
    protected override ElementGroupPrototype CreateElementToolPrototype(Store store, Guid domainClassId)
    {
        if (domainClassId == Transistor.DomainClassId)
        {
            Transistor transistor = new Transistor(store);

            transistor.Base = new ComponentTerminal(store);
            transistor.Collector = new ComponentTerminal(store);
            transistor.Emitter = new ComponentTerminal(store);

            transistor.Base.Name = "base";
            transistor.Collector.Name = "collector";
            transistor.Emitter.Name = "emitter";

            // Create an ElementGroup for the Toolbox.
            ElementGroup elementGroup = new ElementGroup(store.DefaultPartition);
            elementGroup.AddGraph(transistor, true);
            // AddGraph includes the embedded parts

            return elementGroup.CreatePrototype();
        }
        else
        {
            return base.CreateElementToolPrototype(store, domainClassId);
}  }    }

Personnalisation des outils de connexion

Généralement, vous créez un outil d'élément lorsque vous créez une classe de connecteurs. Une autre solution consiste à surcharger un outil en autorisant les types des deux extrémités à déterminer le type de la relation. Par exemple, vous pouvez définir un outil de connexion capable de créer à la fois des relations de type Personne-Personne et des relations de type Personne-Ville.

Les outils de connexion invoquent les générateurs de connexions. Utilisez les générateurs de connexions pour spécifier de quelle façon les utilisateurs peuvent lier les éléments dans le concepteur généré. Les générateurs de connexions spécifient les éléments qui peuvent être liés et le type de lien créé entre eux.

Lorsque vous créez une relation de référence entre classes de domaine, un générateur de connexions est automatiquement créé. Vous pouvez utiliser ce générateur de connexions lors du mappage d'un outil de connexion. Pour plus d’informations sur la création d’outils de connexion, consultez Configuration de la boîte à outils.

Vous pouvez modifier le générateur de connexions par défaut de telle sorte qu'il puisse traiter un éventail différent de types source et cible, et créer différents types de relation.

Vous pouvez aussi écrire le code personnalisé des générateurs de connexions pour spécifier les classes source et cible de la collection, définir le type de connexion à établir et déclencher d'autres actions associées à la création d'une connexion.

Structure des générateurs de connexions

Les générateurs de connexions contiennent une ou plusieurs directives de connexion de liens, qui spécifient la relation de domaine et les éléments source et cible. Par exemple, dans le modèle de solution Flux de tâches, vous pouvez voir CommentReferencesSubjectsBuilder dans l'Explorateur DSL. Ce générateur de connexions contient une directive de connexion de lien nommée CommentReferencesSubjects, qui est mappée à la relation de domaine CommentReferencesSubjects. Cette directive de connexion de liens contient une directive de rôle source qui pointe vers la classe de domaine Comment et une directive de rôle cible qui pointe vers la classe de domaine FlowElement.

Utilisation de générateurs de connexion pour limiter les rôles source et cible

Vous pouvez utiliser les générateurs de connexion pour restreindre l'occurrence de certaines classes dans le rôle source ou le rôle cible d'une relation de domaine donnée. Par exemple, il se peut que vous ayez une classe de domaine de base avec une relation de domaine vers une autre classe de domaine, mais que vous ne vouliez pas que toutes les classes dérivées de la classe de base aient les mêmes rôles dans cette relation. Dans la solution Flux de tâches, il existe quatre classes de domaine concrètes (StartPoint, EndPoint, MergeBranchet Synchronization) qui héritent directement de la classe de domaine abstraite FlowElement, et deux classes de domaine concrètes (Task et ObjectInState) qui héritent indirectement de celle-ci. Il existe également une relation de référence Flow qui prend les classes de domaine FlowElement dans leur rôle source et leur rôle cible. Toutefois, une instance d’une classe de domaine EndPoint ne doit pas être la source d’une instance d’une relation Flow, et une instance d’une classe StartPoint ne doit pas être la cible d’une instance d’une relation Flow . Le générateur de connexions FlowBuilder a une directive de connexion de lien nommée Flow qui spécifie les classes de domaine qui peuvent jouer le rôle source (Task, MergeBranch, StartPointet Synchronisation) et qui peuvent jouer le rôle cible(MergeBranch, Endpointet Synchronisation).

Vous pouvez ajouter plusieurs directives de connexion de liens à un générateur de connexions. Cela peut vous aider à masquer certaines des complexités du modèle de domaine aux utilisateurs et à empêcher la Boîte à outils de s’encombrer. Vous pouvez ajouter les directives de connexion de liens de différentes relations de domaine à un seul générateur de connexions. Cependant, vous devez regrouper les relations de domaine quand elles exécutent à peu près la même fonction.

Dans la solution Flux de tâches, l’outil de connexion Flow est utilisé pour créer des instances des relations de domaine Flow et ObjectFlow. Le générateur de connexions FlowBuilder possède, en plus de la directive de connexion de liens Flow décrite précédemment, deux directives de connexion de liens nommées ObjectFlow. Ces directives spécifient qu’une instance d’une relation ObjectFlow peut être créée entre les instances de la classe de domaine ObjectInState, ou d’une instance d’un ObjectInState à une instance d’une Task, mais pas entre deux instances d’une Task, ou d’une instance d’une Task à une instance d’un ObjectInState. Cependant, l'instance d'une relation Flow peut être tracée entre deux instances Task. Si vous compilez et exécutez la solution Flux de tâches, vous pouvez voir que le dessin d’un Flow à partir d’une instance d’un objet ObjectInState à une instance d’une Task crée une instance d’un ObjectFlow, mais dessiner une Flow entre deux instances d’une Task crée une instance d’un Flow.

Code personnalisé pour générateurs de connexions

Les quatre cases à cocher de l'interface utilisateur définissent différents types de personnalisation de générateurs de connexions :

  • la case à cocher Acceptation personnalisée d'une directive de rôle source ou cible

  • la case à cocher Connexion personnalisée d'une directive de rôle source ou cible

  • la case à cocher Utilise la connexion personnalisée d'une directive de connexion

  • la propriété Est personnalisé du générateur de connexions

    Vous devez écrire un certain code de programme pour procéder à ces personnalisations. Pour savoir quel code vous devez fournir, cochez l'une de ces cases, cliquez sur Transformer tous les modèles, puis générez votre solution. Il en résultera un rapport d'erreurs. Double-cliquez sur le rapport d'erreurs pour afficher un commentaire expliquant quel code vous devez ajouter.

Notes

Pour ajouter un code personnalisé, créez une définition de classe partielle dans un fichier de code distinct des fichiers de code des dossiers GeneratedCode. Pour éviter de perdre votre travail, vous ne devez pas modifier les fichiers de code générés. Pour plus d’informations, consultez Remplacement et extension des classes générées.

Création du code de connexion personnalisé

Dans chaque directive de connexion de liens, l'onglet Directives de rôle source définit de quels types vous pouvez effectuer un déplacement. De même, l'onglet Directives de rôle cible définit les types vers lesquels vous pouvez effectuer un déplacement. Pour chaque type, vous pouvez spécifier s’il faut autoriser la connexion (pour cette directive de connexion de liens) en définissant l’indicateur Acceptation personnalisée puis en fournissant le code supplémentaire.

Vous pouvez également personnaliser ce qui se produit quand la connexion est établie. Par exemple, vous pouvez personnaliser uniquement le cas où le déplacement se produit vers ou depuis une classe particulière, tous les cas commandés par une directive de connexion de liens ou la totalité du générateur de connexions FlowBuilder. Pour chacune de ces options, vous pouvez définir des indicateurs personnalisés au niveau approprié. Lorsque vous transformez tous les modèles et essayez de générer la solution, les messages d'erreur vous dirigent vers les commentaires qui se trouvent dans le code généré. Ces commentaires identifient ce que vous devez fournir.

Dans l'exemple Diagramme de composants, le générateur de connexions de la relation de domaine Connexion est personnalisé pour limiter les connexions qui peuvent être établies entre les ports. L'illustration suivante montre que vous ne pouvez établir des connexions qu'à partir des éléments OutPort vers les éléments InPort, mais que vous pouvez imbriquer des composants les uns à l'intérieur des autres.

Connexion entrante vers un OutPort à partir d'un composant imbriqué

Connection Builder

Par conséquent, il se peut que vous vouliez spécifier qu'une connexion puisse aller d'un composant imbriqué vers un OutPort. Pour spécifier une telle connexion, définissez Utilise l'acceptation personnalisée du type InPort comme rôle source et le type OutPort comme rôle cible de la fenêtre Détails DSL, comme indiqué dans les illustrations suivantes :

Directive de connexion de liens dans l'Explorateur DSL

Connection builder image

Directive de connexion de liens dans la fenêtre Détails DSL

Link connect directive in DSL Details window

Vous devez ensuite fournir les méthodes de la classe ConnectionBuilder :

  public partial class ConnectionBuilder
  {
    /// <summary>
    /// OK if this component has children
    /// </summary>
    private static bool CanAcceptInPortAsSource(InPort candidate)
    {
       return candidate.Component.Children.Count > 0;
    }

    /// <summary>
    /// Only if source is on parent of target.
    /// </summary>
    private static bool CanAcceptInPortAndInPortAsSourceAndTarget                (InPort sourceInPort, InPort targetInPort)
    {
      return sourceInPort.Component == targetInPort.Component.Parent;
    }
// And similar for OutPorts...

Pour plus d’informations sur la personnalisation du modèle à l’aide du code de programme, consultez Navigation et mise à jour d’un modèle dans le code du programme.

Vous pouvez utiliser un code similaire pour empêcher, par exemple, les utilisateurs de créer des boucles avec des liens parent-enfant. Ces restrictions sont considérées comme des contraintes « dures » car les utilisateurs ne peuvent les enfreindre à aucun moment. Vous pouvez aussi créer des contraintes de validation « douces » que les utilisateurs peuvent ignorer temporairement en créant des configurations non valides qu'ils ne peuvent pas sauvegarder.

Meilleure pratique de définition des générateurs de connexions

Vous devez définir un générateur de connexions pour créer différents types de relation uniquement s'ils sont conceptuellement associés. Dans l'exemple du flux de tâches, vous utilisez le même générateur pour créer des flux entre les tâches, ainsi qu'entre les tâches et les objets. Cependant, l'utilisation du même générateur pour créer des relations entre commentaires et tâches serait source de confusion.

Si vous définissez un générateur de connexions pour plusieurs types de relation, vous devez vous assurer qu'il ne puisse pas correspondre à plus d'un type de la même paire d'objets source et cible. Sinon, les résultats seront imprévisibles.

Vous pouvez utiliser le code personnalisé pour appliquer les contraintes « dures », mais vous devez prendre en compte le fait que les utilisateurs puissent ou non établir temporairement des connexions non valides. S'ils le peuvent, vous pouvez modifier les contraintes de telle sorte que les connexions ne soient pas validées tant que les utilisateurs n'ont pas enregistré les modifications.