方法: UML API を使用してシーケンス図を編集する

相互作用とは、生存線のセットの間におけるメッセージのシーケンスです。 相互作用は、シーケンス図に表示されます。

API の詳細については、「Microsoft.VisualStudio.Uml.Interactions」を参照してください。

UML 図のコマンドやジェスチャ ハンドラーを記述する方法の概略については、「方法: モデリング図にメニュー コマンドを定義する」を参照してください。

基本コード

名前空間インポート

次の using ステートメントを含める必要があります。

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

メニュー コマンドとジェスチャ ハンドラーを生成する場合は、次のステートメントも必要です。

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

詳細については、「方法: モデリング図にメニュー コマンドを定義する」を参照してください。

コンテキストの取得

シーケンス図のコマンドまたはジェスチャ ハンドラーの一部として相互作用を編集する場合は、コンテキストの参照を次のように取得できます。 次に例を示します。

    [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;
             ...

生成される UML シーケンス図と手動で生成する UML シーケンス図

シーケンス図には、UML モデリング プロジェクトで手動で生成するシーケンス図と、プログラム コードから生成されるシーケンス図の 2 種類があります。 操作しているシーケンス図の種類を判断するには、UmlMode プロパティを使用します。

たとえば、UML シーケンス図でのみ表示できるメニュー コマンドを生成する必要がある場合、QueryStatus() メソッドに次のステートメントを含めることができます。

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

生成されたシーケンス図で、生存線、メッセージ、その他の要素は、UML シーケンス図と同じように表示されます。 UML モデルでは、モデル ストアにルートがあります。このルートは、その他すべての要素を所有するモデルです。ただし、生成された相互作用は、null ルートを持つ独自のモデル ストアに存在します。

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

相互作用を生成して表示するには

相互作用はパッケージまたはモデルの子として生成します。

たとえば、空のシーケンス図で実行されるコマンドを開発する場合、必ず相互作用が存在するかどうかを確認するところから始める必要があります。

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);
    } 

相互作用とそのレイアウトの更新

相互作用を更新する場合、必ず次のいずれかのメソッドを使用してレイアウトを更新し、操作を終了する必要があります。

  • ISequenceDiagram.UpdateShapePositions() は、最近挿入または移動された図形、およびその周辺の図形の位置を調整します。

  • ISequenceDiagram.Layout([SequenceDiagramLayoutKinds]) は、図全体を再描画します。 パラメーターを使用し、生存線、メッセージ、またはその両方を再配置するように指定できます。

これは特に、新規要素を挿入する場合、または既存の要素を移動する場合に重要です。 これらのいずれかの操作を実行しなければ、図上で正しく配置されません。 必要な処理は、一連の変更の最後に、これらの操作のいずれかを呼び出すことだけです。

コマンドを実行した後に、元に戻す操作を実行したユーザーが混乱しないよう、ILinkedUndoTransaction を使用し、変更と最終的な Layout() または UpdateShapePositions() 操作を囲みます。 次に例を示します。

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

ILinkedUndoTransaction を使用するには、クラス内で次の宣言を行う必要があります。

[Import] ILinkedUndoContext LinkedUndoContext { get; set; }

詳細については、「方法: トランザクションを使用してモデルの更新をリンクする」を参照してください。

相互作用の構築

生存線を生成するには

ILifeline lifeline = interaction.CreateLifeline();

生存線は、接続可能要素、つまり型のインスタンスを表します。 たとえば、コンポーネントが受信メッセージをどのように内部パートに渡すかを相互作用を使用して示す場合、生存線はポートとコンポーネントのパートを表すことができます。

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

また、相互作用で任意のオブジェクトのセットを表す場合、相互作用自体にプロパティまたはその他の IConnectableElement を生成できます。

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

さらに、接続可能要素にリンクせずに、生存線の名前と型を設定することもできます。

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

メッセージを生成するには

メッセージを生成するには、ソースの生存線およびターゲットの生存線での挿入ポイントを特定する必要があります。 次に例を示します。

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

ソースまたはターゲットが定義されていないメッセージを生成するには

interaction.CreateLostFoundMessage(MessageKind.Found, insertionPoint);

生存線のすべてのキー ポイントで挿入ポイントを特定するために使用できるメッセージを次の表に示します。

ILifeline のメソッド

挿入ポイント

FindInsertionPointAtTop()

生存線の上部

FindInsertionPointAtBottom()

生存線の下部

FindInsertionPointAfterMessage

(IMessage previous)

指定されたメッセージの直後のポイント

FindInsertionPointAfterExecutionSpecification

(IExecutionSpecification previous)

生存線上または親の実行指定ブロック上のポイント

FindInsertionPointAfterInteractionUse

(IInteractionUse previous)

相互作用使用に続くポイント

FindInsertionPointAfterCombinedFragment

(ICombinedFragment previous)

結合フラグメントに続くポイント

FindInsertionPoint(IExecutionSpecification block)

実行ブロックの上部

FindInsertionPoint(IInteractionOperand fragment)

結合フラグメントのオペランドの上部

メッセージを生成する場合は、別のメッセージと重複するメッセージを定義しないように注意してください。

結合フラグメントおよび相互作用使用を生成するには

要素の範囲として含まれる各生存線に挿入位置を指定すると、結合フラグメントおよび相互作用使用を生成することができます。 既存のメッセージやフラグメントと重複するポイントを指定しないように注意してください。

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

また、既存のメッセージ セットを処理する結合フラグメントも生成することができます。 メッセージは、すべて同じ生存線または実行ブロックから供給される必要があります。

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

結合フラグメントは、常に単一のオペランドで生成されます。 新しいオペランドを生成するには、前か後に挿入する既存のオペランドを指定すると共に、それを後に挿入するか、前に挿入するかを指定する必要があります。

// 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);

トラブルシューティング

変更が UpdateShapePositions() または Layout() 操作で完了していなければ、図形の位置は誤って表示されます。

その他のほとんどの問題は、挿入位置がずれているために、新規メッセージまたはフラグメントが他のメッセージやフラグメントと重複することによって発生します。 この場合、変更が実行されない、または例外がスローされるという症状が見られます。 例外は、UpdateShapePositions() または Layout() 操作が実行されるまではスローされないことがあります。

参照

参照

Microsoft.VisualStudio.Uml.Interactions

その他の技術情報

UML モデルと図の拡張

方法: モデリング図にメニュー コマンドを定義する

方法: カスタム モデリング ツールボックス項目を定義する

方法: UML モデルの検証制約を定義する

UML API を使用したプログラミング