Partager via


Comment : modifier des diagrammes de séquence à l'aide de l'API UML

Une interaction est une séquence de messages entre un ensemble de lignes de vie. Une interaction s'affiche dans un diagramme de séquences.

Pour obtenir des informations complètes sur l'API, consultez Microsoft.VisualStudio.Uml.Interactions.

Pour une introduction plus générale à l'écriture de commandes et de gestionnaires de mouvements pour les diagrammes UML, consultez Comment : définir une commande de menu sur un diagramme de modélisation.

Code Basic

Importations d'espaces de noms

Vous devez inclure les instructions using suivantes :

using Microsoft.VisualStudio.Uml.Classes;
   // for basic UML types such as IPackage
using Microsoft.VisualStudio.Uml.Interactions;
   // for interaction types
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml;
   // to create elements and use additional functions

Si vous créez des commandes de menu et des gestionnaires de mouvements, vous aurez également besoin de :

using System.ComponentModel.Composition; 
   // for Import and Export
using Microsoft.VisualStudio.Modeling.ExtensionEnablement;
   // for ICommandExtension
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Presentation;
   // for diagrams and context

Pour plus d'informations, consultez Comment : définir une commande de menu sur un diagramme de modélisation.

Obtention du contexte

Si vous modifiez une interaction dans le cadre d'un gestionnaire de commandes ou de mouvements dans un diagramme de séquence, vous pouvez obtenir une référence au contexte. Par exemple :

    [SequenceDesignerExtension]
    [Export(typeof(ICommandExtension))]  
    public class MySequenceDiagramCommand : ICommandExtension
    {
        [Import]
        public IDiagramContext Context { get; set; }
        public void QueryStatus (IMenuCommand command)
        {
          ISequenceDiagram sequenceDiagram = 
              Context.CurrentDiagram as ISequenceDiagram;
             ...

Diagrammes de séquences UML et générés

Il existe deux genres de diagrammes de séquences : ceux créés manuellement dans un projet de modélisation UML et ceux générés à partir du code de programme. Vous pouvez utiliser la propriété UmlMode pour découvrir quel genre de diagramme de séquence vous utilisez.

Par exemple, pour créer une commande de menu visible uniquement sur les diagrammes de séquence UML, la méthode QueryStatus() peut inclure l'instruction suivante :

    command.Enabled = command.Visible = 
          sequenceDiagram != null && sequenceDiagram.UmlMode;

Les lignes de vie, les messages et les autres éléments sont similaires dans un diagramme de séquence généré et dans un diagramme de séquence UML. Dans un modèle UML, le magasin de modèles a une racine, qui correspond au modèle qui détient tous les autres éléments, mais une interaction générée existe dans son propre magasin de modèles, qui a une racine null :

    IModel rootModel = sequenceDiagram.ModelStore.Root;
    // !sequenceDiagram.UmlMode == (rootModel == null)

Pour créer et afficher une interaction

Créez l'interaction en tant qu'enfant d'un package ou d'un modèle.

Par exemple, si vous développez une commande qui peut être exécutée sur un diagramme de séquence vide, vous devez toujours commencer par vérifier si l'interaction existe.

public void Execute (IMenuCommand command)
{
    ISequenceDiagram sequenceDiagram = 
         Context.CurrentDiagram as ISequenceDiagram;
    if (sequenceDiagram == null) return;
    // Get the diagram's interaction:
    IInteraction interaction = sequenceDiagram.Interaction;
    // A new sequence diagram might have no interaction:
    if (interaction == null)
    {
       // Get the home package or model of the diagram:
       IPackage parentPackage = sequenceDiagram.GetObject<IPackage>();
       interaction = parentPackage.CreateInteraction();
       // Display the interaction on the sequence diagram:
       sequenceDiagram.Bind(interaction);
    } 

Mise à jour d'une interaction et de sa disposition

Lorsque vous mettez à jour une interaction, terminez toujours l'opération en mettant à jour sa disposition à l'aide de l'une des méthodes suivantes :

  • ISequenceDiagram.UpdateShapePositions() ajuste les positions des formes qui ont été insérées récemment ou déplacées, ainsi que celles de leurs formes avoisinantes.

  • ISequenceDiagram.Layout([SequenceDiagramLayoutKinds]) redessine le diagramme complet. Vous pouvez utiliser ce paramètre pour spécifier le repositionnement des lignes de vie, des messages ou des deux à la fois.

Ceci est particulièrement important lorsque vous insérez de nouveaux éléments ou déplacez des éléments existants. Ils ne seront pas dans des positions correctes sur le diagramme tant que vous n'aurez pas exécuté l'une de ces opérations. Vous devez seulement appeler l'une de ces opérations une fois, à la fin d'une série de modifications.

Pour éviter de déconcerter l'utilisateur qui effectue une action d'annulation après votre commande, utilisez un ILinkedUndoTransaction pour délimiter vos modifications et les opérations Layout() ou UpdateShapePositions() finales. Par exemple :

using (ILinkedUndoTransaction transaction = LinkedUndoContext.BeginTransaction("create loop"))
{
  Interaction.CreateCombinedFragment(InteractionOperatorKind.Loop, messages);
  Diagram.UpdateShapePositions();
  transaction.Commit();
}

Pour utiliser un ILinkedUndoTransaction, vous devez effectuer la déclaration suivante dans votre classe :

[Import] ILinkedUndoContext LinkedUndoContext { get; set; }

Pour plus d'informations, consultez Comment : lier des mises à jour de modèles à l'aide de transactions.

Génération d'une interaction

Pour créer des lignes de vie

ILifeline lifeline = interaction.CreateLifeline();

Une ligne de vie représente un élément connectable, autrement dit, une instance de type. Par exemple, si l'interaction est utilisée pour montrer comment un composant délègue des messages entrants à ses parties internes, les lignes de vie peuvent représenter les ports et les parties du composant :

foreach (IConnectableElement part in 
            component.Parts
           .Concat<IConnectableElement>(component.OwnedPorts))
{
   ILifeline lifeline = interaction.CreateLifeline();
   lifeline.Represents = part;
}

Par ailleurs, si l'interaction affiche un ensemble arbitraire d'objets, vous pouvez créer une propriété ou un autre IConnectableElement dans l'interaction elle-même :

ILifeline lifeline = interaction.CreateLifeline();
IProperty property1 = interaction.CreateProperty();
property1.Type = model.CreateInterface();
property1.Type.Name = "Type 1";
lifeline.Represents = property1;

Comme alternative supplémentaire, vous pouvez définir le nom et le type d'une ligne de vie sans la lier à un élément connectable :

ILifeline lifeline = interaction.CreateLifeline();
lifeline.Name = "c1";
lifeline.SetInstanceType("Customer");
System.Diagnostics.Debug.Assert(
           lifeline.GetDisplayName() == "c1:Customer"  );

Pour créer des messages

Pour créer un message, vous devez identifier des points d'insertion sur les lignes de vie source et cible. Par exemple :

interaction.CreateMessage( sourceInsertionPoint, 
                           targetInsertionPoint, 
                           MessageKind.Complete, 
                           MessageSort.ASynchCall)

Pour créer un message qui dispose d'une source et d'une cible indéfinies :

interaction.CreateLostFoundMessage(MessageKind.Found, insertionPoint);

Il existe plusieurs messages que vous pouvez utiliser pour identifier des points d'insertion au niveau de tous les points clés d'une ligne de vie :

Méthode sur ILifeline

Utilisez-la pour procéder à une insertion au niveau de ce point.

FindInsertionPointAtTop()

Partie supérieure de la ligne de vie.

FindInsertionPointAtBottom()

Partie inférieure de la ligne de vie.

FindInsertionPointAfterMessage

(IMessage previous)

Point situé immédiatement après le message spécifié.

FindInsertionPointAfterExecutionSpecification

(IExecutionSpecification previous)

Le point peut se situer sur la ligne de vie ou sur un bloc de spécification d'exécution parent.

FindInsertionPointAfterInteractionUse

(IInteractionUse previous)

Point suivant une utilisation d'interaction.

FindInsertionPointAfterCombinedFragment

(ICombinedFragment previous)

Point suivant un fragment combiné.

FindInsertionPoint(IExecutionSpecification block)

Partie supérieure d'un bloc d'exécution.

FindInsertionPoint(IInteractionOperand fragment)

Partie supérieure d'un opérande d'un fragment combiné.

Lorsque vous créez des messages, prenez soin d'éviter de définir un message qui traverserait un autre message.

Pour créer des fragments combinés et des utilisations d'interaction

Vous pouvez créer des fragments combinés et des utilisations d'interaction en spécifiant un point d'insertion sur chaque ligne de vie qui doit être couverte par l'élément. Prenez soin d'éviter de spécifier un ensemble de points qui traverserait un message ou un fragment existant.

Interaction.CreateCombinedFragment(InteractionOperatorKind.Loop, 
  Interaction.Lifelines.Select(lifeline => lifeline.FindInsertionPointAtTop()));
Interaction.CreateInteractionUse(
  Interaction.Lifelines.Select(lifeline => lifeline.FindInsertionPointAtTop()));

Vous pouvez également créer un fragment combiné qui couvre un ensemble de messages existant. Tous les messages doivent prendre leur source dans la même ligne de vie ou le même bloc d'exécution.

ICombinedFragment cf = Interaction.CreateCombinedFragment(
  InteractionOperatorKind.Loop,
  Interaction.Lifelines.First().GetAllOutgoingMessages());

Un fragment combiné est toujours créé avec un opérande unique. Pour créer un nouvel opérande, vous devez spécifier l'opérande existant que vous souhaitez insérer avant ou après, et si vous souhaitez insérer après lui ou avant lui :

// Create an additional operand before the first
cf.CreateInteractionOperand(cf.Operands.First(), false);
// Create an additional operand after the last:
cf.CreateInteractionOperand(cf.Operands.Last(), true);

Dépannage

Les formes apparaîtront dans des positions incorrectes si les modifications ne sont pas complétées par une opération UpdateShapePositions() ou Layout().

La plupart des autres problèmes sont provoqués par des points d'insertion mal alignés, qui obligent de nouveaux messages ou fragments à en traverser d'autres. Les symptômes éventuels sont la non-exécution de toute modification ou la levée d'une exception. Il est possible que l'exception ne soit pas levée tant que l'opération UpdateShapePositions() ou Layout() ne sera pas exécutée.

Voir aussi

Référence

Microsoft.VisualStudio.Uml.Interactions

Autres ressources

Extension de modèles et de diagrammes UML

Comment : définir une commande de menu sur un diagramme de modélisation

Comment : définir un élément de boîte à outils de modélisation personnalisé

Comment : définir des contraintes de validation pour les modèles UML

Programmation à l'aide de l'API UML