Share via


方法: モデリング図にドロップおよびダブルクリック ハンドラーを定義する

Visual Studio Ultimate では、ユーザーが UML 図の項目をダブルクリックまたはドラッグしたときに実行されるコマンドを定義できます。 これらの拡張機能を Visual Studio Integration Extension (VSIX) にパッケージ化し、他の Visual Studio Ultimate ユーザーに配布できます。

図の種類およびドラッグする要素の種類に対応する組み込みの動作が既に用意されている場合は、この動作を追加またはオーバーライドできない可能性があります。

要件

ジェスチャ ハンドラーの作成

UML デザイナーのジェスチャ ハンドラーを定義するには、ジェスチャ ハンドラーの振る舞いを定義するクラスを生成し、そのクラスを Visual Studio Integration Extension (VSIX) に埋め込む必要があります。 VSIX は、ハンドラーをインストールできるコンテナーとして機能します。 ジェスチャ ハンドラーを定義する方法は 2 つあります。

  • プロジェクト テンプレートを使用してジェスチャ ハンドラーを独自の VSIX に生成する。 これはより簡単な方法です。 ハンドラーの他の種類の拡張機能 (検証拡張機能、カスタム ツールボックス項目、メニュー コマンドなど) と組み合わせない場合は、この方法を使用します。

  • ジェスチャ ハンドラーと VSIX プロジェクトを個別に生成する。 複数の種類の拡張機能を同じ VSIX に組み合わせる場合は、この方法を使用します。 たとえば、ジェスチャ ハンドラーが特定の制約に従うモデルを必要とする場合は、そのモデルを検証メソッドとして同じ VSIX に埋め込むことができます。

ジェスチャ ハンドラーを独自の VSIX に生成するには

  1. [新しいプロジェクト] ダイアログ ボックスの [モデリング プロジェクト] で、[ジェスチャ拡張機能] をクリックします。

  2. 新しいプロジェクトで .cs ファイルを開き、GestureExtension クラスを変更してジェスチャ ハンドラーを実装します。

    詳細については、「ジェスチャ ハンドラーの実装」を参照してください。

  3. F5 キーを押してジェスチャ ハンドラーをテストします。 詳細については、「ジェスチャ ハンドラーの実行」を参照してください。

  4. 別のコンピューターにジェスチャ ハンドラーをインストールします。これを行うには、プロジェクトでビルドした bin\*\*.vsix ファイルを別のコンピューターにコピーします。 詳細については、「Installing the Gesture Handler (ジェスチャ ハンドラーのインストール)」を参照してください。

ジェスチャ ハンドラー用のクラス ライブラリ (DLL) プロジェクトを作成するには

  1. 新規の Visual Studio ソリューションまたは既存のソリューションにクラス ライブラリ プロジェクトを作成します。

    1. [ファイル] メニューの [新規作成] をポイントし、[プロジェクト] をクリックします。

    2. [インストールされたテンプレート][Visual C#] または [Visual Basic] をクリックし、中央の列で、[クラス ライブラリ] をクリックします。

    3. 新しいソリューションを作成するか、既に開いている VSIX ソリューションにコンポーネントを追加するかを [ソリューション] に指定します。

    4. プロジェクトの名前と場所を設定し、[OK] をクリックします。

  2. 以下の参照をプロジェクトに追加します。

    Microsoft.VisualStudio.Modeling.Sdk.10.0

    Microsoft.VisualStudio.Modeling.Sdk.Diagrams.10.0

    Microsoft.VisualStudio.ArchitectureTools.Extensibility

    Microsoft.VisualStudio.Uml.Interfaces

    System.ComponentModel.Composition

    System.Windows.Forms

    Microsoft.VisualStudio.ArchitectureTools.Extensibility.Layer – レイヤー図を拡張する場合にのみ必要になります。 詳細については、「レイヤー図に関する拡張機能の作成」を参照してください。

  3. プロジェクトにクラス ファイルを追加し、その内容を次のコードに設定します。

    注意

    名前空間およびクラスの名前は必要に応じて変更してください。

    using System.ComponentModel.Composition;
    using System.Linq;
    using System.Collections.Generic;
    using Microsoft.VisualStudio.Modeling.Diagrams;
    using Microsoft.VisualStudio.Modeling.Diagrams.ExtensionEnablement;
    using Microsoft.VisualStudio.Modeling.ExtensionEnablement;
    using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml;
    using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Presentation;
    using Microsoft.VisualStudio.Uml.AuxiliaryConstructs;
    using Microsoft.VisualStudio.Modeling;
    using Microsoft.VisualStudio.Uml.Classes;
    // ADD other UML namespaces if required
    
    namespace MyGestureHandler // CHANGE
    {
      // DELETE any of these attributes if the handler
      // should not work with some types of diagram.
      [ClassDesignerExtension]
      [ActivityDesignerExtension]
      [ComponentDesignerExtension]
      [SequenceDesignerExtension]
      [UseCaseDesignerExtension]
      // [LayerDesignerExtension]
    
      // Gesture handlers must export IGestureExtension:
      [Export(typeof(IGestureExtension))]
      // CHANGE class name
      public class MyGesture1 : IGestureExtension
      {
        [Import]
        public IDiagramContext DiagramContext { get; set; }
    
        /// <summary>
        /// Called when the user double-clicks on the diagram
        /// </summary>
        /// <param name="targetElement"></param>
        /// <param name="diagramPointEventArgs"></param>
        public void OnDoubleClick(ShapeElement targetElement, DiagramPointEventArgs diagramPointEventArgs)
        {
          // CHANGE THIS CODE FOR YOUR APPLICATION.
    
          // Get the target shape, if any. Null if the target is the diagram.
          IShape targetIShape = targetElement.CreateIShape();
    
          // Do something...
        }
    
        /// <summary>
        /// Called repeatedly when the user drags from anywhere on the screen.
        /// Return value should indicate whether a drop here is allowed.
        /// </summary>
        /// <param name="targetMergeElement">References the element to be dropped on.</param>
        /// <param name="diagramDragEventArgs">References the element to be dropped.</param>
        /// <returns></returns>
        public bool CanDragDrop(ShapeElement targetMergeElement, DiagramDragEventArgs diagramDragEventArgs)
        {
          // CHANGE THIS CODE FOR YOUR APPLICATION.
    
          // Get the target element, if any. Null if the target is the diagram.
          IShape targetIShape = targetMergeElement.CreateIShape();
    
          // This example allows drag of any UML elements.
          return GetModelElementsFromDragEvent(diagramDragEventArgs).Count() > 0;
        }
    
    
        /// <summary>
        /// Execute the action to be performed on the drop.
        /// </summary>
        /// <param name="targetDropElement"></param>
        /// <param name="diagramDragEventArgs"></param>
        public void OnDragDrop(ShapeElement targetDropElement, DiagramDragEventArgs diagramDragEventArgs)
        {
          // CHANGE THIS CODE FOR YOUR APPLICATION.
        }
    
        /// <summary>
        /// Retrieves UML IElements from drag arguments.
        /// Works for drags from UML diagrams.
        /// </summary>
        private IEnumerable<IElement> GetModelElementsFromDragEvent
                (DiagramDragEventArgs dragEvent)
        {
          //ElementGroupPrototype is the container for
          //dragged and copied elements and toolbox items.
          ElementGroupPrototype prototype =
             dragEvent.Data.
             GetData(typeof(ElementGroupPrototype))
                  as ElementGroupPrototype;
          // Locate the originals in the implementation store.
          IElementDirectory implementationDirectory =
             dragEvent.DiagramClientView.Diagram.Store.ElementDirectory;
    
          return prototype.ProtoElements.Select(
            prototypeElement =>
            {
              ModelElement element = implementationDirectory
                .FindElement(prototypeElement.ElementId);
              ShapeElement shapeElement = element as ShapeElement;
              if (shapeElement != null)
              {
                // Dragged from a diagram.
                return shapeElement.ModelElement as IElement;
              }
              else
              {
                // Dragged from UML Model Explorer.
                return element as IElement;
              }
            });
        }
    
      }
    }
    

    メソッドに記述する内容の詳細については、「ジェスチャ ハンドラーの実装」を参照してください。

VSIX プロジェクトにメニュー コマンドを追加する必要があります。VSIX プロジェクトは、コマンドをインストールするためのコンテナーとして機能します。 必要に応じて、同じ VSIX に他のコンポーネントを追加できます。

VSIX プロジェクトにジェスチャ ハンドラーを個別に追加するには

  1. メニュー コマンドを独自の VSIX と共に作成した場合、この手順は必要ありません。

  2. ソリューションに既存の VSIX プロジェクトが存在しない場合は、新しく作成します。

    1. ソリューション エクスプローラーでソリューションを右クリックし、[追加] をポイントして [新しいプロジェクト] をクリックします。

    2. [インストールされたテンプレート][Visual C#] または [Visual Basic] を展開し、[機能拡張] をクリックします。 中央の列で、[VSIX プロジェクト] をクリックします。

  3. VSIX プロジェクトをソリューションのスタートアップ プロジェクトとして設定します。

    • ソリューション エクスプローラーで、VSIX プロジェクトを右クリックし、[スタートアップ プロジェクトに設定] をクリックします。
  4. source.extension.vsixmanifest[コンテンツ] で、ジェスチャ ハンドラーのクラス ライブラリ プロジェクトを MEF コンポーネントとして追加します。

    1. source.extension.vsixmanifest を開きます。

    2. [コンテンツの追加] をクリックします。

    3. [コンテンツ タイプの選択] で、[MEF コンポーネント] を選択します。

    4. [ソースの選択] で、[プロジェクト] をクリックし、ジェスチャ ハンドラーのクラス ライブラリ プロジェクトの名前を選択します。

  5. [エディションの選択] をクリックし、拡張機能を実行する Visual Studio のエディションを選択します。

  6. VSIX の名前と説明のフィールドを設定します。 ファイルを保存します。

ジェスチャ ハンドラーの実行

テストを行う場合は、ジェスチャ ハンドラーをデバッグ モードで実行します。

ジェスチャ ハンドラーをテストするには

  1. F5 キーを押すか、または [デバッグ] メニューの [デバッグ開始] をクリックします。

    Visual Studio の実験用のインスタンスが開始します。

    トラブルシューティング: 新しい Visual Studio が起動しない場合:

    • 複数のプロジェクトがある場合は、VSIX プロジェクトがソリューションのスタートアップ プロジェクトとして設定されていることを確認してください。

    • ソリューション エクスプローラーでスタートアップまたはプロジェクトのみを右クリックし、[プロパティ] をクリックします。 プロジェクトのプロパティ エディターで、[デバッグ] タブをクリックします。 [外部プログラムの開始] フィールドの文字列が Visual Studio の完全なパス名であることを確認してください。通常は次のようになります。

      C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\devenv.exe

  2. 実験用の Visual Studio で、モデリング プロジェクトを開くか、または生成し、モデリング図を開くか、または生成します。 ジェスチャ ハンドラー クラスの属性に表示されているいずれかの種類に含まれる図を使用してください。

  3. 図上の任意の場所をダブルクリックします。 ダブルクリック ハンドラーが呼び出されます。

  4. UML エクスプローラーから図上へ要素をドラッグします。 ドラッグ ハンドラーが呼び出されます。

トラブルシューティング: ジェスチャ ハンドラーが動作しない場合は、次の点について確認してください。

  • ジェスチャ ハンドラー プロジェクトは、VSIX プロジェクトの source.extensions.manifest[コンテンツ] の一覧に MEF コンポーネントとして表示される。

  • すべての Import 属性と Export 属性のパラメーターが有効である。

  • CanDragDrop メソッドが false を返さない。

  • 使用するモデル図の種類 (UML クラス、シーケンスなど) が、ジェスチャ ハンドラー クラスの属性 ([ClassDesignerExtension]、[SequenceDesignerExtension] など) の 1 つとして表示される。

  • この種類のターゲットおよびドロップ対象の要素に対して組み込みの機能が定義されていない。

ジェスチャ ハンドラーの実装

ジェスチャ ハンドラーのメソッド

ジェスチャ ハンドラー クラスは、IGestureExtension を実装およびエクスポートします。 定義する必要のあるメソッドを次に示します。

bool CanDragDrop (ShapeElement target, DiagramDragEventArgs dragEvent)

dragEvent で参照されるソース要素をこのターゲット上にドロップすることを許可する場合は true を返します。

このメソッドによってモデルが変更されることはありません。 このメソッドは、マウスを動かしたときの矢印の状態を判断するために使用されるので、すばやい処理が必要です。

void OnDragDrop (ShapeElement target, DiagramDragEventArgs dragEvent)

dragEvent で参照されるソース オブジェクトに基づいてモデルを更新し、ターゲットを更新します。

ドラッグ後にマウスのボタンを離すと呼び出されます。

void OnDoubleClick (ShapeElement target, DiagramPointEventArgs pointEvent)

target は、ユーザーがダブルクリックした図形です。

UML だけでなく、ファイル、.NET クラス ビュー内のノード、アーキテクチャ エクスプローラーのノードなどのさまざまな項目を受け取ることができるハンドラーを生成できます。 シリアル化形式の項目をデコードする OnDragDrop メソッドを記述すると、ユーザーはこれらの項目を UML 図にドラッグできます。 デコード メソッドは、項目の種類によって異なります。

メソッドのパラメーターは次のとおりです。

  • ShapeElement target. ユーザーが項目をドラッグした図形または図。

    ShapeElement は、UML モデリング ツールの基礎にある実装のクラスです。 UML モデルと図が不整合な状態になる可能性を低くするには、このクラスのメソッドを直接使用しないことをお勧めします。 代わりに、要素を IShape にラップし、「方法: 図にモデルを表示する」で説明するメソッドを使用します。

    • IShape を取得するには

      IShape targetIShape = target.CreateIShape(target);
      
    • ドラッグまたはダブルクリック操作が対象とするモデル要素を取得するには

      IElement target = targetIShape.Element;
      

      これを要素のより具体的な種類にキャストできます。

    • UML モデルを格納する UML モデル ストアを取得するには

      IModelStore modelStore = 
        targetIShape.Element.GetModelStore(); 
      
    • ホストおよびサービス プロバイダーへのアクセスを取得するには

      target.Store.GetService(typeof(EnvDTE.DTE)) as EnvDTE.DTE
      
  • DiagramDragEventArgs eventArgs. このパラメーターは、ドラッグ操作の、シリアル化形式のソース オブジェクトを保持します。

    System.Windows.Forms.IDataObject data = eventArgs.Data;  
    

    Visual Studio のさまざまな部分から、または Windows デスクトップから、さまざまな種類の要素を図にドラッグすることができます。 IDataObject では、多種多様な要素がさまざまな方法でエンコードされています。 情報を抽出する方法については、適切なオブジェクトの種類に関するドキュメントを参照してください。

    ソース オブジェクトが、UML モデル エクスプローラーまたは別の UML 図からドラッグされた UML 要素の場合は、「方法: IDataObject から UML モデル要素を取得する」を参照してください。

メソッドのコードの記述

モデルを読み取って更新するコードの記述の詳細については、「UML API を使用したプログラミング」を参照してください。

ドラッグ操作でのモデル情報へのアクセスについては、「方法: IDataObject から UML モデル要素を取得する」を参照してください。

シーケンス図を扱う場合は、「方法: UML API を使用してシーケンス図を編集する」も参照してください。

メソッドのパラメーターに加え、現在の図およびモデルにアクセスするためのクラスの、インポート済みプロパティを宣言することもできます。

[Import] public IDiagramContext DiagramContext { get; set; }

IDiagramContext の宣言により、図、現在の選択項目、およびモデルにアクセスするメソッドにコードを記述することができます。

IDiagram diagram = this.DiagramContext.CurrentDiagram;
foreach (IShape<IElement> shape in diagram.GetSelectedShapes<IElement>)
{ IElement element = shape.Element; ... }
IModelStore modelStore = diagram.ModelStore;
IModel model = modelStore.Root;
foreach (IDiagram diagram in modelStore.Diagrams) {...}
foreach (IElement element in modelStore.AllInstances<IUseCase>) {...}

詳細については、「方法: UML モデル内を移動する」を参照してください。

拡張機能のインストールとアンインストール

Visual Studio 拡張機能は、自分のコンピューターと他のコンピューターの両方にインストールできます。

拡張機能をインストールするには

  1. 自分のコンピューターで、VSIX プロジェクトによってビルドされた .vsix ファイルを見つけます。

    1. ソリューション エクスプローラーで、VSIX プロジェクトを右クリックし、[エクスプローラーでフォルダーを開く] をクリックします。

    2. bin\*\<プロジェクト>.vsix ファイルを見つけます。

  2. 拡張機能をインストールする対象のコンピューターに .vsix ファイルをコピーします。 自分のコンピューターでも別のコンピューターでもかまいません。

    インストール先のコンピューターには、source.extension.vsixmanifest に指定した Visual Studio のいずれかのエディションがインストールされている必要があります。

  3. インストール先のコンピューター上で、.vsix ファイルをダブルクリックします。

    Visual Studio 拡張機能インストーラーが起動され、拡張機能がインストールされます。

  4. Visual Studio を起動または再起動します。

拡張機能をアンインストールするには

  1. [ツール] メニューの [拡張機能マネージャー] をクリックします。

  2. [インストール済みの拡張機能] を展開します。

  3. 拡張機能を選択し、[アンインストール] をクリックします。

拡張機能の障害が原因で読み込みが失敗し、エラー ウィンドウにレポートが生成されることがまれにありますが、それは拡張機能マネージャーには表示されません。 その場合は、以下の場所からファイルを削除して、拡張機能を削除します。

%LocalAppData%\Local\Microsoft\VisualStudio\10.0\Extensions

コンポーネント図からドラッグされたコンポーネントのパートおよびポートに基づいて、シーケンス図に生存線を生成する方法を次の例に示します。

テストするには、F5 キーを押します。 Visual Studio の実験用インスタンスが開きます。 このインスタンスで UML モデルを開き、コンポーネント図にコンポーネントを生成します。 このコンポーネントに、インターフェイスと内部コンポーネントのパートを追加します。 インターフェイスおよびパートを選択します。 次に、インターフェイスおよびパートをシーケンス図にドラッグします (コンポーネント図からシーケンス図のタブまでドラッグし、続いてシーケンス図にドラッグします)。 生存線は、インターフェイスとパートのそれぞれに表示されます。

相互作用をシーケンス図にバインディングする方法の詳細については、「方法: UML API を使用してシーケンス図を編集する」を参照してください。

using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;
using Microsoft.VisualStudio.Modeling.Diagrams.ExtensionEnablement;
using Microsoft.VisualStudio.Modeling.ExtensionEnablement;
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml;
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Presentation;
using Microsoft.VisualStudio.Uml.AuxiliaryConstructs;
using Microsoft.VisualStudio.Uml.Classes;
using Microsoft.VisualStudio.Uml.Interactions;
using Microsoft.VisualStudio.Uml.CompositeStructures;
using Microsoft.VisualStudio.Uml.Components;

/// <summary>
/// Creates lifelines from component ports and parts.
/// </summary>
[Export(typeof(IGestureExtension))]
[SequenceDesignerExtension]
public class CreateLifelinesFromComponentParts : IGestureExtension
{
  [Import]
  public IDiagramContext Context { get; set; }

  /// <summary>
  /// Called by the modeling framework when
  /// the user drops something on a target.
  /// </summary>
  /// <param name="target">The target shape or diagram </param>
  /// <param name="dragEvent">The item being dragged</param>
  public void OnDragDrop(ShapeElement target,
           DiagramDragEventArgs dragEvent)
  {
    ISequenceDiagram diagram = Context.CurrentDiagram
            as ISequenceDiagram;
    IInteraction interaction = diagram.Interaction;
    if (interaction == null)
    {
      // Sequence diagram is empty: create an interaction.
      interaction = diagram.ModelStore.Root.CreateInteraction();
      interaction.Name = Context.CurrentDiagram.Name;
      diagram.Bind(interaction);
    }
    foreach (IConnectableElement connectable in
       GetConnectablesFromDrag(dragEvent))
    {
      ILifeline lifeline = interaction.CreateLifeline();
      lifeline.Represents = connectable;
      lifeline.Name = connectable.Name;
    }
  }

  /// <summary>
  /// Called by the modeling framework to determine whether
  /// the user can drop something on a target.
  /// Must not change anything.
  /// </summary>
  /// <param name="target">The target shape or diagram</param>
  /// <param name="dragEvent">The item being dragged</param>
  /// <returns>true if this item can be dropped on this target</returns>
  public bool CanDragDrop(ShapeElement target,
           DiagramDragEventArgs dragEvent)
  {
    IEnumerable<IConnectableElement> connectables = GetConnectablesFromDrag(dragEvent);
    return connectables.Count() > 0;
  }

  ///<summary>
  /// Get dragged parts and ports of an IComponent.
  ///</summary>
  private IEnumerable<IConnectableElement>
    GetConnectablesFromDrag(DiagramDragEventArgs dragEvent)
  {
    foreach (IElement element in
      GetModelElementsFromDragEvent(dragEvent))
    {
      IConnectableElement part = element as IConnectableElement;
      if (part != null)
      {
        yield return part;
      }
    }
  }

  /// <summary>
  /// Retrieves UML IElements from drag arguments.
  /// Works for drags from UML diagrams.
  /// </summary>
  private IEnumerable<IElement> GetModelElementsFromDragEvent
          (DiagramDragEventArgs dragEvent)
  {
    //ElementGroupPrototype is the container for
    //dragged and copied elements and toolbox items.
    ElementGroupPrototype prototype =
       dragEvent.Data.
       GetData(typeof(ElementGroupPrototype))
            as ElementGroupPrototype;
    // Locate the originals in the implementation store.
    IElementDirectory implementationDirectory =
       dragEvent.DiagramClientView.Diagram.Store.ElementDirectory;

    return prototype.ProtoElements.Select(
      prototypeElement =>
      {
        ModelElement element = implementationDirectory
          .FindElement(prototypeElement.ElementId);
        ShapeElement shapeElement = element as ShapeElement;
        if (shapeElement != null)
        {
          // Dragged from a diagram.
          return shapeElement.ModelElement as IElement;
        }
        else
        {
          // Dragged from UML Model Explorer.
          return element as IElement;
        }
      });
  }

  public void OnDoubleClick(ShapeElement targetElement, DiagramPointEventArgs diagramPointEventArgs)
  {
  }
}

GetModelElementsFromDragEvent() のコードについては、「方法: IDataObject から UML モデル要素を取得する」を参照してください。

参照

その他の技術情報

方法: モデリング拡張機能を定義およびインストールする

UML モデルと図の拡張

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

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

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