Personnalisation de la création et du mouvement des éléments

Vous pouvez permettre à un élément d’être déplacé vers un autre élément, soit à partir de la boîte à outils, soit dans une opération de collage ou de déplacement. Vous pouvez lier les éléments déplacés aux éléments cibles à l’aide des relations que vous spécifiez.

Une directive de fusion d’élément (EMD) spécifie ce qui se passe lorsqu’un élément de modèle est fusionné dans un autre élément de modèle. Cela se produit dans les situations suivantes :

  • L’utilisateur fait glisser à partir de la boîte à outils vers le diagramme ou une forme.

  • L’utilisateur crée un élément à l’aide d’un menu Ajouter dans l’explorateur ou d’une forme de compartiment.

  • L’utilisateur déplace un élément d’un couloir vers un autre.

  • L’utilisateur colle un élément.

  • Votre code de programme appelle la directive de fusion d’élément.

Bien que les opérations de création semblent être différentes des opérations de copie, elles fonctionnent en fait de la même façon. Lorsqu’un élément est ajouté, par exemple à partir de la boîte à outils, un prototype de celui-ci est répliqué. Le prototype est fusionné dans le modèle de la même manière que les éléments copiés à partir d’une autre partie du modèle.

La responsabilité d’un EMD est de décider comment un objet ou un groupe d’objets doit être fusionné dans un emplacement particulier dans le modèle. En particulier, il détermine quelles relations doivent être instanciées pour lier le groupe fusionné au modèle. Vous pouvez également le personnaliser pour définir des propriétés et créer des objets supplémentaires.

Diagram showing a before and after look at a tree of elements and their reference relationships when An E M D determines how a new element is added.

Un EMD est généré automatiquement lorsque vous définissez une imbrication de la relation. Cet EMD par défaut crée une instance de la relation lorsque les utilisateurs ajoutent de nouvelles instances enfants au parent. Vous pouvez modifier ces EMD par défaut, par exemple en ajoutant du code personnalisé.

Vous pouvez également ajouter vos propres EMD dans la définition DSL pour permettre aux utilisateurs de faire glisser ou de coller différentes combinaisons de classes fusionnées et de réception.

Définition d’une directive de fusion d’élément

Vous pouvez ajouter des directives de fusion d’éléments aux classes de domaine, aux relations de domaine, aux formes, aux connecteurs et aux diagrammes. Vous pouvez les ajouter ou les trouver dans l’Explorateur DSL sous la classe de domaine de réception. La classe de réception est la classe de domaine de l’élément qui se trouve déjà dans le modèle et sur laquelle l’élément nouveau ou copié sera fusionné.

Screenshot of DSL Explorer showing an E M D being added with ExampleElement selected as the Indexing class and the Applies to subclasses option checked.

La classe d’indexation est la classe de domaine des éléments qui peuvent être fusionnés dans des membres de la classe de réception. Les instances de sous-classes de la classe d’indexation sont également fusionnées par cet EMD, sauf si vous définissez S’applique aux sous-classes sur False.

Il existe deux types de directive de fusion :

  • Une directive Traiter la fusion spécifie les relations par lesquelles le nouvel élément doit être lié à l’arborescence.

  • Une directive Transférer la fusion redirige le nouvel élément vers un autre élément récepteur, généralement un parent.

Vous pouvez ajouter du code personnalisé aux directives de fusion :

  • Définissez Utilise l’acceptation personnalisée pour ajouter votre propre code pour déterminer si une instance particulière de l’élément d’indexation doit être fusionnée dans l’élément cible. Lorsque l’utilisateur fait glisser à partir de la boîte à outils, le pointeur « non valide » indique si votre code interdit la fusion.

    Par exemple, vous pouvez autoriser la fusion uniquement lorsque l’élément de réception est dans un état particulier.

  • Définissez Utilise la fusion personnalisée pour ajouter votre propre code afin de définir les modifications apportées au modèle lors de la fusion.

    Par exemple, vous pouvez définir des propriétés dans l’élément fusionné à l’aide des données de son nouvel emplacement dans le modèle.

Notes

Si vous écrivez du code de fusion personnalisé, il affecte uniquement les fusions effectuées à l’aide de cet EMD. S’il existe d’autres EMD qui fusionnent le même type d’objet, ou s’il existe d’autres codes personnalisés qui créent ces objets sans utiliser d’EMD, ils ne seront pas affectés par votre code de fusion personnalisé.

Si vous souhaitez vous assurer qu’un nouvel élément ou une nouvelle relation est toujours traité par votre code personnalisé, envisagez de définir AddRule sur la relation d’incorporation et DeleteRule sur la classe de domaine de l’élément. Pour plus d’informations, consultez Propagation de modifications dans le modèle par des règles.

Exemple : Définition d’un EMD sans code personnalisé

L’exemple suivant permet aux utilisateurs de créer un élément et un connecteur en même temps en faisant glisser à partir de la boîte à outils vers une forme existante. L’exemple ajoute un EMD à la définition DSL. Avant cette modification, les utilisateurs peuvent faire glisser des outils sur le diagramme, mais pas sur des formes existantes.

Les utilisateurs peuvent également coller des éléments sur d’autres éléments.

Pour permettre aux utilisateurs de créer un élément et un connecteur en même temps

  1. Créez un DSL à l’aide du modèle de solution de Langage minimal.

    Lorsque vous exécutez ce DSL, il vous permet de créer des formes et des connecteurs entre les formes. Vous ne pouvez pas faire glisser une nouvelle forme ExampleElement de la boîte à outils vers une forme existante.

  2. Pour permettre aux utilisateurs de fusionner des éléments sur des formes ExampleElement, créez un EMD dans la classe de domaine ExampleElement :

    1. Dans l’Explorateur DSL, développez Classes de domaine. Faites un clic droit sur ExampleElement, puis cliquez sur Ajouter une nouvelle directive de fusion d’élément.

    2. Assurez-vous que la fenêtre Détails de DSL est ouverte, afin que vous puissiez voir les détails du nouvel EMD. (Menu : Affichage, Autres Fenêtres, Détails DSL.)

  3. Définissez la classe d’indexation dans la fenêtre Détails DSL pour définir la classe d’éléments pouvant être fusionnée sur des objets ExampleElement.

    Pour cet exemple, sélectionnez ExampleElements afin que l’utilisateur puisse faire glisser de nouveaux éléments sur des éléments existants.

    Notez que la classe d’indexation devient le nom de l’EMD dans l’Explorateur DSL.

  4. Sous Traiter la fusion en créant des liens, ajoutez deux chemins :

    • Un chemin lie le nouvel élément au modèle parent. L’expression de chemin d’accès que vous devez entrer navigue à partir de l’élément existant, jusqu’à l’imbrication de la relation au modèle parent. Enfin, il spécifie le rôle dans le nouveau lien auquel le nouvel élément sera affecté. Le chemin d’accès est le suivant :

      ExampleModelHasElements.ExampleModel/!ExampleModel/.Elements

    • L’autre chemin lie le nouvel élément à l’élément existant. L’expression de chemin spécifie la relation de référence et le rôle auquel le nouvel élément sera affecté. Ce chemin d’accès est le suivant :

      ExampleElementReferencesTargets.Sources

      Vous pouvez utiliser l’outil de navigation de chemin pour créer chaque chemin d’accès :

      1. Sous Traiter la fusion en créant des liens sur des chemins d’accès, cliquez sur <Ajouter un chemin>.

      2. Cliquez sur la flèche déroulante à droite de l’élément de liste. Une arborescence s’affiche.

      3. Développez les nœuds de l’arborescence pour former le chemin d’accès que vous souhaitez spécifier.

  5. Testez le DSL :

    1. Pour générer et exécuter la solution, appuyez sur F5.

      La reconstruction prend plus de temps que d’habitude, car le code généré est mis à jour à partir de modèles de texte pour se conformer à la nouvelle définition DSL.

    2. Lorsque l’instance expérimentale de Visual Studio a démarré, ouvrez un fichier de modèle de votre DSL. Créez des exemples d’éléments.

    3. Faites glisser à partir de l’outil Exemple d’élément sur une forme existante.

      Une nouvelle forme s’affiche. Elle est liée à la forme existante avec un connecteur.

    4. Copiez une forme existante. Sélectionnez une autre forme et collez-la.

      Une copie de la première forme est créée. Elle a un nouveau nom et est liée à la deuxième forme avec un connecteur.

Notez les points suivants de cette procédure :

  • En créant des directives de fusion d’éléments, vous pouvez autoriser n’importe quelle classe d’élément à accepter tout autre élément. L’EMD est créé dans la classe de domaine de réception, et la classe de domaine acceptée est spécifiée dans le champ de classe d’index.

  • En définissant des chemins d’accès, vous pouvez spécifier les liens à utiliser pour connecter le nouvel élément au modèle existant.

    Les liens que vous spécifiez doivent inclure une imbrication de la relation.

  • L’EMD affecte à la fois la création à partir de la boîte à outils et les opérations de collage.

    Si vous écrivez du code personnalisé qui crée de nouveaux éléments, vous pouvez appeler explicitement l’EMD à l’aide de la méthode ElementOperations.Merge. Cela garantit que votre code lie de nouveaux éléments au modèle de la même façon que d’autres opérations. Pour plus d’informations, consultez Personnalisation du comportement de copie.

Exemple : Ajout d’un code d’acceptation personnalisé à un EMD

En ajoutant du code personnalisé à un EMD, vous pouvez définir un comportement de fusion plus complexe. Cet exemple simple empêche l’utilisateur d’ajouter plus d’un nombre fixe d’éléments au diagramme. L’exemple modifie l’EMD par défaut qui accompagne une relation d’incorporation.

Pour écrire du code d’acceptation personnalisé afin de restreindre ce que l’utilisateur peut ajouter

  1. Créez un DSL à l’aide du modèle de solution Langage minimal. Ouvrez le diagramme de définition DSL.

  2. Dans l’Explorateur DSL, développez Classes de domaine, ExampleModel, Directives de fusion d’éléments. Sélectionnez la directive de fusion d’élément nommée ExampleElement.

    Cet EMD contrôle la façon dont l’utilisateur peut créer de nouveaux objets ExampleElement dans le modèle, par exemple en faisant glisser à partir de la boîte à outils.

  3. Dans la fenêtre Détails DSL, sélectionnez Utilise l’acceptation personnalisée.

  4. Régénérez la solution. Cela prendra plus de temps que d’habitude, car le code généré sera mis à jour à partir du modèle.

    Une erreur de build est signalée, semblable à : « Company.ElementMergeSample.ExampleElement ne contient pas de définition pour CanMergeExampleElement... »

    Vous devez implémenter la méthode CanMergeExampleElement.

  5. Créez un fichier de code dans le projet Dsl. Remplacez son contenu par le code suivant et remplacez l’espace de noms par l’espace de noms de votre projet.

    using Microsoft.VisualStudio.Modeling;
    
    namespace Company.ElementMergeSample // EDIT.
    {
      partial class ExampleModel
      {
        /// <summary>
        /// Called whenever an ExampleElement is to be merged into this ExampleModel.
        /// This happens when the user pastes an ExampleElement
        /// or drags from the toolbox.
        /// Determines whether the merge is allowed.
        /// </summary>
        /// <param name="rootElement">The root element in the merging EGP.</param>
        /// <param name="elementGroupPrototype">The EGP that the user wants to merge.</param>
        /// <returns>True if the merge is allowed</returns>
        private bool CanMergeExampleElement(ProtoElementBase rootElement, ElementGroupPrototype elementGroupPrototype)
        {
          // Allow no more than 4 elements to be added:
          return this.Elements.Count < 4;
        }
      }
    }
    

    Cet exemple simple limite le nombre d’éléments pouvant être fusionnés dans le modèle parent. Pour des conditions plus intéressantes, la méthode peut inspecter une propriété ou un lien de l’objet récepteur. Elle peut également inspecter les propriétés des éléments de fusion, qui sont transportés dans un ElementGroupPrototype. Pour plus d’informations sur ElementGroupPrototypes, consultez Personnalisation du comportement de copie. Pour plus d’informations sur l’écriture de code qui lit un modèle, consultez Navigation et mise à jour d’un modèle dans le code du programme.

  6. Testez le DSL :

    1. Appuyez sur F5 pour reconstruire la solution. Lorsque l’instance expérimentale de Visual Studio s’ouvre, ouvrez une instance de votre DSL.

    2. Créez de nouveaux éléments de plusieurs façons :

      • Faites glisser l’outil Exemple d’élément vers le diagramme.

      • Dans l’Explorateur d’exemples de modèles, faites un clic droit sur le nœud racine, puis cliquez sur Ajouter un nouvel exemple d’élément.

      • Copiez et collez un élément dans le diagramme.

    3. Vérifiez que vous ne pouvez pas utiliser l’une de ces façons pour ajouter plus de quatre éléments au modèle. Cela est dû au fait qu’elles utilisent toutes la directive de fusion d’élément.

Exemple : Ajout d’un code de fusion personnalisé à un EMD

Dans le code de fusion personnalisé, vous pouvez définir ce qui se passe lorsque l’utilisateur fait glisser un outil ou colle sur un élément. Il existe deux façons de définir une fusion personnalisée :

  1. Définissez Utilise la fusion personnalisée et fournissez le code requis. Votre code remplace le code de fusion généré. Utilisez cette option si vous souhaitez redéfinir complètement ce que fait la fusion.

  2. Remplacez la méthode MergeRelate et éventuellement la méthode MergeDisconnect. Pour ce faire, vous devez définir la propriété Generates Double Derived de la classe de domaine. Votre code peut appeler le code de fusion généré dans la classe de base. Utilisez cette option si vous souhaitez réaliser des opérations supplémentaires une fois la fusion effectuée.

    Ces approches affectent uniquement les fusions effectuées à l’aide de cet EMD. Si vous souhaitez affecter toutes les façons dont l’élément fusionné peut être créé, une alternative consiste à définir AddRule sur la relation d’incorporation et DeleteRule sur la classe de domaine fusionnée. Pour plus d’informations, consultez Propagation de modifications dans le modèle par des règles.

Pour remplacer MergeRelate

  1. Dans la définition DSL, vérifiez que vous avez défini l’EMD auquel vous souhaitez ajouter du code. Si vous le souhaitez, vous pouvez ajouter des chemins d’accès et définir du code d’acceptation personnalisé comme décrit dans les sections précédentes.

  2. Dans le diagramme DslDefinition, sélectionnez la classe de réception de la fusion. En règle générale, il s’agit de la classe à la fin source d’une imbrication de la relation.

    Par exemple, dans un DSL généré à partir de la solution de langage minimal, sélectionnez ExampleModel.

  3. Dans la fenêtre Propriétés, définissez Generates Double Derived sur true.

  4. Régénérez la solution.

  5. Inspectez le contenu de Dsl\Generated Files\DomainClasses.cs. Recherchez les méthodes nommées MergeRelate et examinez leur contenu. Cela vous aidera à écrire vos propres versions.

  6. Dans un nouveau fichier de code, écrivez une classe partielle pour la classe de réception et remplacez la méthode MergeRelate. N’oubliez pas d’appeler la méthode de base. Par exemple :

    partial class ExampleModel
    {
      /// <summary>
      /// Called when the user drags or pastes an ExampleElement onto the diagram.
      /// Sets the time of day as the name.
      /// </summary>
      /// <param name="sourceElement">Element to be added</param>
      /// <param name="elementGroup">Elements to be merged</param>
      protected override void MergeRelate(ModelElement sourceElement, ElementGroup elementGroup)
      {
        // Connect the element according to the EMD:
        base.MergeRelate(sourceElement, elementGroup);
    
        // Custom actions:
        ExampleElement mergingElement = sourceElement as ExampleElement;
        if (mergingElement != null)
        {
          mergingElement.Name = DateTime.Now.ToLongTimeString();
        }
      }
    }
    

Pour écrire du code de fusion personnalisé

  1. Dans Dsl\Generated Code\DomainClasses.cs, inspectez les méthodes nommées MergeRelate. Ces méthodes créent des liens entre un nouvel élément et le modèle existant.

    Inspectez également les méthodes nommées MergeDisconnect. Ces méthodes dissocient un élément du modèle lorsqu’il doit être supprimé.

  2. Dans l’Explorateur DSL, sélectionnez ou créez la directive de fusion d’éléments que vous souhaitez personnaliser. Dans la fenêtre Détails DSL, définissez Utilise la fusion personnalisée.

    Lorsque vous définissez cette option, les options Traiter la fusion et Transférer la fusion sont ignorées. Votre code est utilisé à la place.

  3. Régénérez la solution. Cela prendra plus de temps que d’habitude, car les fichiers de code générés seront mis à jour à partir du modèle.

    Des messages d’erreur s’affichent. Double-cliquez sur les messages d’erreur pour afficher les instructions dans le code généré. Ces instructions vous demandent de fournir deux méthodes, MergeRelateYourDomainClass et MergeDisconnectYourDomainClass

  4. Écrivez les méthodes dans une définition de classe partielle dans un fichier de code distinct. Les exemples que vous avez inspectés précédemment doivent suggérer ce dont vous avez besoin.

    Le code de fusion personnalisé n’affecte pas le code qui crée directement des objets et des relations, et n’affecte pas d’autres EMD. Pour vous assurer que vos modifications supplémentaires sont implémentées indépendamment de la façon dont l’élément est créé, envisagez d’écrire un AddRule et un DeleteRule à la place. Pour plus d’informations, consultez Propagation de modifications dans le modèle par des règles.

Redirection d’une opération de fusion

Une directive de transfert de fusion redirige la cible d’une opération de fusion. En règle générale, la nouvelle cible est le parent d’incorporation de la cible initiale.

Par exemple, dans un DSL créé avec le modèle de diagramme de composants, les ports sont incorporés dans les composants. Les ports sont affichés sous forme de petites formes sur le bord d’une forme de composant. L’utilisateur crée des ports en faisant glisser l’outil Port sur une forme composant. Mais parfois, l’utilisateur fait glisser par erreur l’outil Port sur un port existant, au lieu du composant, et l’opération échoue. Il s’agit d’une erreur courante lorsqu’il existe plusieurs ports existants. Pour aider l’utilisateur à éviter ce problème, vous pouvez autoriser les ports à être déplacés vers un port existant, mais l’action est redirigée vers le composant parent. L’opération fonctionne comme si l’élément cible était le composant.

Vous pouvez créer une directive de transfert de fusion dans la solution Modèle de composant. Si vous compilez et exécutez la solution d’origine, vous pouvez voir que les utilisateurs peuvent faire glisser n’importe quel nombre d’éléments Port d’entrée ou Port de sortie de la Boîte à outils vers un élément Composant. Toutefois, ils ne peuvent pas faire glisser un port vers un port existant. Le pointeur Indisponible les avertit que ce déplacement n’est pas activé. Toutefois, vous pouvez créer une directive de transfert de fusion afin qu’un port involontairement supprimé sur un Port d’entrée existant soit transféré à l’élément Composant.

Pour créer une directive de transfert de fusion

  1. Créez une solution Outils de langage spécifique à un domaine à l’aide du modèle de solution Composant.

  2. Affichez l’Explorateur DSL en ouvrant DslDefinition.dsl.

  3. Dans l’Explorateur DSL, développez Classes de domaine.

  4. La classe de domaine abstraite ComponentPort est la classe de base d’InPort et d’OutPort. Faites un clic droit sur ComponentPort, puis cliquez sur Ajouter une nouvelle directive de fusion d’élément.

    Un nouveau nœud Directive de fusion d’élément apparaît sous le nœud Directives de fusion d’élément.

  5. Sélectionnez le nœud Directive de fusion d’éléments et ouvrez la fenêtre Détails DSL.

  6. Dans la liste Classe d’indexation, sélectionnez ComponentPort.

  7. Sélectionnez Transférer la fusion vers une autre classe de domaine.

  8. Dans la liste de sélection du chemin d’accès, développez ComponentPort, ComponentHasPorts, puis sélectionnez Composant.

    Le nouveau chemin doit ressembler à celui-ci :

    ComponentHasPorts.Component/!Component

  9. Enregistrez la solution, puis transformez les modèles en cliquant sur le bouton le plus à droite de la barre d’outils Explorateur de solutions.

  10. Créez et exécutez la solution. Une seconde instance de Visual Studio apparaît.

  11. Dans l’Explorateur de solutions, ouvrez Sample.mydsl. Le diagramme et la Boîte à outils ComponentLanguage s’affichent.

  12. Faites glisser un Port d’entrée de la Boîte à outils vers un autre Port d’entrée. Ensuite, faites glisser un OutputPort vers un InputPort, puis vers un autre OutputPort.

    Vous ne devez pas voir le pointeur Indisponible, et vous devez être en mesure de supprimer le nouveau Port d’entrée sur le port d’entrée existant. Sélectionnez le nouveau Port d’entrée et faites-le glisser vers un autre point sur le Composant.