方法: モデリング図にメニュー コマンドを定義する
Visual Studio Ultimate では、UML 図のショートカット メニューのメニュー項目を定義できます。ユーザーがメニュー項目を選択すると、メニュー コマンドが図上の要素のショートカット メニューで、に実行するコードを記述できます表示して有効にするかどうかを制御します。Visual Studio Integration Extension にこれらの拡張機能 (VSIX) にパッケージ化し、他の Visual Studio のユーザーに配布できます。
要件
から派生した [Visual Studio ギャラリー]できる Visual Studio SDK。
から派生した Code Gallery の Visual Studio の Visualization and Modeling SDKできる Visual Studio の Visualization and Modeling SDK。
メニュー コマンドの定義
UML デザイナーのメニュー コマンドを作成するには、コマンドの振る舞いを定義するクラスを作成し、そのクラスを Visual Studio Integration Extension (VSIX) に埋め込む必要があります。VSIX は、コマンドをインストールできるコンテナーとして機能します。メニュー コマンドを定義する方法は 2 つあります。
**プロジェクト テンプレートを使用してメニュー コマンドを独自の VSIX に作成する。**これはより簡単な方法です。メニュー コマンドの他の種類の拡張機能 (検証拡張機能、カスタム ツールボックス項目、ジェスチャ ハンドラーなど) と組み合わせない場合は、この方法を使用します。
**メニュー コマンドと VSIX プロジェクトを個別に作成する。**複数の種類の拡張機能を同じ VSIX に組み合わせる場合は、この方法を使用します。たとえば、メニュー コマンドが特定の制約に従うモデルを必要とする場合は、そのモデルを検証メソッドとして同じ VSIX に埋め込むことができます。
メニュー コマンドを独自の VSIX に作成するには
[新しいプロジェクト] ダイアログ ボックスの [モデリング プロジェクト] で、[コマンド拡張機能] をクリックします。
新しいプロジェクトで .cs ファイルを開き、CommandExtension クラスを変更してコマンドを実装します。
詳細については、「メニュー コマンドの実装」を参照してください。
新しいクラスを定義して、このプロジェクトに追加のコマンドを追加できます。
F5 キーを押してメニュー コマンドをテストします。詳細については、「メニュー コマンドの実行」を参照してください。
別のコンピューターにメニュー コマンドをインストールします。これを行うには、プロジェクトでビルドした bin\*\*.vsix ファイルを別のコンピューターにコピーします。詳細については、「Installing the Menu Command (メニュー コマンドのインストール)」を参照してください。
代替制御が:を次に示します。
別のクラス ライブラリ (DLL) のメニュー コマンドを作成するには、
新しい Visual Studio のソリューションまたは既存のソリューションにクラス ライブラリ プロジェクトを作成します。
[ファイル] メニューで、[新規]、[プロジェクト] クリックします。
[インストールされているテンプレート]、を選択します Visual C# またはの下 [Visual Basic]。中央の列で、**[クラス ライブラリ]**を選択します。
新しいソリューションを作成するか、既に開いている VSIX ソリューションにコンポーネントを追加するかを [ソリューション] に指定します。
プロジェクトの名前と場所を設定し、[OK] をクリックします。
以下の参照をプロジェクトに追加します。
参照
実行できる操作
System.ComponentModel.Composition
MEF (Managed Extensibility Framework) を使用してコンポーネントを定義する。
Microsoft.VisualStudio.Uml.Interfaces
モデル要素のプロパティを読み取り、変更する。
Microsoft.VisualStudio.ArchitectureTools.Extensibility
モデル要素を生成する、図のシェイプを変更する。
Microsoft.VisualStudio.Modeling.Sdk.11.0
モデル イベント ハンドラーを定義する。
モデルに対する一連の変更をカプセル化する。詳細については、「方法: トランザクションを使用してモデルの更新をリンクする」を参照してください。
Microsoft.VisualStudio.Modeling.Sdk.Diagrams.11.0
(必須ではない)
ジェスチャ ハンドラーの追加の図要素にアクセスする。
Microsoft.VisualStudio.ArchitectureTools.Extensibility.Layer
レイヤー図上のコマンドにのみ必要。詳細については、「レイヤー図の拡張」を参照してください。
レイヤー図上のコマンドを定義する。
プロジェクトにクラス ファイルを追加し、その内容を次のコードに設定します。
[!メモ]
名前空間、クラス名、および Text から返される値を必要に応じて変更してください。
複数のコマンドを定義する場合は、クラス名] メニューにアルファベット順に表示されます。
using System.ComponentModel.Composition; using System.Linq; using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Presentation; using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml; using Microsoft.VisualStudio.Modeling.ExtensionEnablement; using Microsoft.VisualStudio.Uml.AuxiliaryConstructs; using Microsoft.VisualStudio.Uml.Classes; // ADD other UML namespaces if required namespace UMLmenu1 // CHANGE { // DELETE any of these attributes if the command // should not appear in some types of diagram. [ClassDesignerExtension] [ActivityDesignerExtension] [ComponentDesignerExtension] [SequenceDesignerExtension] [UseCaseDesignerExtension] // [LayerDesignerExtension] // All menu commands must export ICommandExtension: [Export (typeof(ICommandExtension))] // CHANGE class name – determines order of appearance on menu: public class Menu1 : ICommandExtension { [Import] public IDiagramContext DiagramContext { get; set; } public void QueryStatus(IMenuCommand command) { // Set command.Visible or command.Enabled to false // to disable the menu command. command.Visible = command.Enabled = true; } public string Text { get { return "MENU COMMAND LABEL"; } } public void Execute(IMenuCommand command) { // A selection of starting points: 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 (IElement element in modelStore.AllInstances<IClass>()) { } } } }
メソッドに記述する内容の詳細については、「メニュー コマンドの実装」を参照してください。
VSIX プロジェクトにメニュー コマンドを追加する必要があります。VSIX プロジェクトは、コマンドをインストールするためのコンテナーとして機能します。必要に応じて、同じ VSIX に他のコンポーネントを追加できます。
VSIX にメニュー コマンドを追加するには、
メニュー コマンドを独自の VSIX と共に作成した場合、この手順は必要ありません。
ソリューションに既存の VSIX プロジェクトが存在しない場合は、新しく作成します。
ソリューションのショートカット メニューの **[ソリューション エクスプローラー]では、追加、[新しいプロジェクト]**を選択します。
**[インストールされているテンプレート]**の下に、Visual C# か **[Visual Basic]**を展開し、を **[機能拡張]を選択します。中央の列で、[VSIX プロジェクト]**を選択します。
VSIX プロジェクトのショートカット メニューの [ソリューション エクスプローラーで、[スタートアップ プロジェクトに設定]、を選択します。
source.extension.vsixmanifest を開きます。
[メタデータ] のタブで、VSIX の名前を設定します。
[対象のインストール] のタブで、ターゲットとして Visual Studio Premium および Ultimate を設定します。
[アセット] のタブで、ダイアログ ボックスの 新規を、選択した場合:
[種類] = MEF コンポーネント
[ソース] = [現在のソリューション内のプロジェクト]
[プロジェクト] = 、クラス ライブラリ プロジェクト
メニュー コマンドの実装
メニュー コマンド クラスは、ICommandExtension に必要なメソッドを実装します。
string Text { get; } |
メニュー項目のラベルを返します。 |
void QueryStatus(IMenuCommand command); |
ユーザーが図上で右クリックすると呼び出されます。 このメソッドによってモデルが変更されることはありません。 DiagramContext.CurrentDiagram.SelectedShapes は、コマンドを表示して有効にするかどうかを指定するために使用します。 設定できる値:
|
void Execute (IMenuCommand command); |
ユーザーがメニュー項目をクリックすると呼び出されます (メニュー項目が表示され、有効な場合)。
|
コードのモデルにアクセスする
メニュー コマンド クラスに、次の宣言を含めます。
[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 (IElement element in modelStore.AllInstances<IUseCase>()) {...}
モデルをナビゲートおよび更新する
UML モデルの要素は、すべて API を通じて使用できます。現在の選択項目から、またはモデルのルートから、その他すべての要素にアクセスすることができます。詳細については、「方法: UML モデル内を移動する」および「UML API を使用したプログラミング」を参照してください。
シーケンス図を扱う場合は、「方法: UML API を使用してシーケンス図を編集する」も参照してください。
この API を使用すると、要素のプロパティを変更し、要素と関係を削除し、新規要素および関係を生成することもできます。
既定では、Execute メソッドに対する変更は、別のトランザクションで実行されます。ユーザーは、それぞれの変更を個別に元に戻すことができます。変更を単一のトランザクションとしてグループ化する場合は、「方法: トランザクションを使用してモデルの更新をリンクする」の説明に従って、ILinkedUndoTransaction を使用します。
更新の際の UI スレッドの使用
バックグラウンド スレッドからモデルの更新を行うと便利な場合があります。たとえば、コマンドを使用して低速なリソースからデータを読み込む場合は、その処理をバックグラウンド スレッドで実行できます。これにより、ユーザーは実行中の変更を確認し、必要に応じて操作を取り消すことができます。
ただし、モデル ストアがスレッド セーフでないことに注意してください。更新を行うには、必ずユーザー インターフェイス (UI) スレッドを使用する必要があります。可能な場合は、バックグラウンド操作の実行中にユーザーが編集を行えないようにしてください。例については、「方法: バックグラウンド スレッドから UML モデルを更新する」を参照してください。
メニュー コマンドの実行
テストを行う場合は、コマンドをデバッグ モードで実行します。
メニュー コマンドをテストするには
[デバッグ] のメニューの **[F5]**を、またはを選択します **[デバッグの開始]**を押します。
Visual Studio の実験用のインスタンスが開始します。
トラブルシューティング: 新しい Visual Studio が起動しない場合:
複数のプロジェクトがある場合は、VSIX プロジェクトがソリューションのスタートアップ プロジェクトとして設定されていることを確認してください。
起動時またはプロジェクトのショートカット メニューのソリューション エクスプローラーで、[プロパティ]、を選択します。プロジェクトのプロパティ エディターで、[デバッグ] のタブをクリックします。[外部プログラムの開始] フィールドの文字列が Visual Studio の完全なパス名であることを確認してください。通常は次のようになります。
C:\Program Files\Microsoft Visual Studio 11.0\Common7\IDE\devenv.exe
実験用の Visual Studio で、モデリング プロジェクトを開くか、または生成し、モデリング図を開くか、または生成します。メニュー コマンド クラスの属性に表示されているいずれかの種類に含まれる図を使用してください。
図のショートカット メニューを開きます。コマンドがメニューに表示されます。
トラブルシューティング: コマンドがメニューに表示されない場合は、次の点について確認してください。
メニュー コマンド プロジェクトは、VSIX プロジェクトの source.extensions.manifest の [アセット] のタブに MEF コンポーネントとして表示されます。
Import 属性と Export 属性のパラメーターが有効である。
QueryStatus メソッドで、command.Enabled フィールドまたは Visible フィールドが false に設定されない。
使用するモデル図の種類 (UML クラス、シーケンスなど) が、メニュー コマンド クラスの属性 ([ClassDesignerExtension]、[SequenceDesignerExtension] など) の 1 つとして表示される。
拡張機能のインストールとアンインストール
Visual Studio 拡張機能は、自分のコンピューターと他のコンピューターの両方にインストールできます。
拡張機能をインストールするには
自分のコンピューターで、VSIX プロジェクトによってビルドされた .vsix ファイルを見つけます。
VSIX プロジェクトのショートカット メニューの **[ソリューション エクスプローラー]では、[Windows エクスプローラーでフォルダーを開く]**を選択します。
bin\*\<プロジェクト>.vsix ファイルを見つけます。
拡張機能をインストールする対象のコンピューターに .vsix ファイルをコピーします。自分のコンピューターでも別のコンピューターでもかまいません。
インストール先のコンピューターには、source.extension.vsixmanifest に指定した Visual Studio のいずれかのエディションがインストールされている必要があります。
ターゲット コンピューターでをダブルクリックして、たとえば .vsix ファイルを開きます。
Visual Studio 拡張機能インストーラーが起動され、拡張機能がインストールされます。
Visual Studio を起動または再起動します。
拡張機能をアンインストールするには
[ツール] で、メニューの **[拡張機能マネージャー]**を選択します。
[インストール済みの拡張機能] を展開します。
拡張機能を選択し、**[アンインストール]**を選択します。
拡張機能の障害が原因で読み込みが失敗し、エラー ウィンドウにレポートが生成されることがまれにありますが、それは拡張機能マネージャーには表示されません。その場合は、以下の場所からファイルを削除して、拡張機能を削除します。
%LocalAppData%\Local\Microsoft\VisualStudio\11.0\Extensions
例
クラス図の 2 つの要素の名前を入れ替えるメニュー コマンドのコードを次の例に示します。このコードは、Visual Studio 拡張機能プロジェクト内でビルドし、前のセクションで説明した手順に従ってインストールする必要があります。
using System.Collections.Generic; // for IEnumerable
using System.ComponentModel.Composition;
// for [Import], [Export]
using System.Linq; // for IEnumerable extensions
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Presentation;
// for IDiagramContext
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml;
// for designer extension attributes
using Microsoft.VisualStudio.Modeling.Diagrams;
// for ShapeElement
using Microsoft.VisualStudio.Modeling.ExtensionEnablement;
// for IGestureExtension, ICommandExtension, ILinkedUndoContext
using Microsoft.VisualStudio.Uml.Classes;
// for class diagrams, packages
/// <summary>
/// Extension to swap names of classes in a class diagram.
/// </summary>
namespace SwapClassNames
{
// Declare the class as an MEF component:
[Export(typeof(ICommandExtension))]
[ClassDesignerExtension]
// Add more ExportMetadata attributes to make
// the command appear on diagrams of other types.
public class NameSwapper : ICommandExtension
{
// MEF required interfaces:
[Import]
public IDiagramContext Context { get; set; }
[Import]
public ILinkedUndoContext LinkedUndoContext { get; set; }
/// <summary>
/// Swap the names of the currently selected elements.
/// </summary>
/// <param name="command"></param>
public void Execute(IMenuCommand command)
{
// Get selected shapes that are IClassifiers -
// IClasses, IInterfaces, IEnumerators.
var selectedShapes = Context.CurrentDiagram
.GetSelectedShapes<IClassifier>();
if (selectedShapes.Count() < 2) return;
// Get model elements displayed by shapes.
IClassifier firstElement = selectedShapes.First().Element;
IClassifier lastElement = selectedShapes.Last().Element;
// Do the swap in a transaction so that user
// cannot undo one change without the other.
using (ILinkedUndoTransaction transaction =
LinkedUndoContext.BeginTransaction("Swap names"))
{
string firstName = firstElement.Name;
firstElement.Name = lastElement.Name;
lastElement.Name = firstName;
transaction.Commit();
}
}
/// <summary>
/// Called by Visual Studio to determine whether
/// menu item should be visible and enabled.
/// </summary>
public void QueryStatus(IMenuCommand command)
{
int selectedClassifiers = Context.CurrentDiagram
.GetSelectedShapes<IClassifier>().Count();
command.Visible = selectedClassifiers > 0;
command.Enabled = selectedClassifiers == 2;
}
/// <summary>
/// Name of the menu command.
/// </summary>
public string Text
{
get { return "Swap Names"; }
}
}
}