方法: モデリング図にメニュー コマンドを定義する
Visual Studio Ultimate では、UML 図のショートカット メニューに追加のメニュー項目を定義できます。 図上の任意の要素のショートカット メニューに対して、メニュー コマンドを表示して有効にするかどうかを制御できます。また、ユーザーがメニュー項目を選択したときに実行されるコードを記述できます。 これらの拡張機能を VSIX (Visual Studio Integration Extension) にパッケージ化し、他の Visual Studio ユーザーに配布できます。
要件
Visual Studio SDK (Visual Studio ギャラリーから入手可能できます)。
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.12.0
モデル イベント ハンドラーを定義する。
モデルに対する一連の変更をカプセル化する。 詳細については、「方法: トランザクションを使用してモデルの更新をリンクする」を参照してください。
Microsoft.VisualStudio.Modeling.Sdk.Diagrams.12.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 の名前を設定します。
[Install Targets] (インストールの対象) タブで、Visual Studio Ultimate および Premium を対象として設定します。
[アセット] タブで、[新規作成] をクリックし、ダイアログ ボックスで次のように設定します。
[種類] = MEF コンポーネント
[ソース] = 現在のソリューション内のプロジェクト
[プロジェクト] = Your class library project
メニュー コマンドの実装
メニュー コマンド クラスは、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 12.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 プロジェクトのショートカット メニューを開き、[エクスプローラーでフォルダーを開く] をクリックします。
bin\*\YourProject.vsix ファイルを見つけます。
拡張機能をインストールする対象のコンピューターに .vsix ファイルをコピーします。 自分のコンピューターでも別のコンピューターでもかまいません。
インストール先のコンピューターには、source.extension.vsixmanifest に指定した Visual Studio のいずれかのエディションがインストールされている必要があります。
ターゲット コンピューターで .vsix ファイルを開きます。たとえば、ダブルクリックして開きます。
Visual Studio 拡張機能インストーラーが起動され、拡張機能がインストールされます。
Visual Studio を起動または再起動します。
拡張機能をアンインストールするには
[ツール] メニューの [拡張機能マネージャー] をクリックします。
[インストール済みの拡張機能] を展開します。
拡張機能を選択し、[アンインストール] をクリックします。
拡張機能の障害が原因で読み込みが失敗し、エラー ウィンドウにレポートが生成されることがまれにありますが、それは拡張機能マネージャーには表示されません。 その場合は、以下の場所からファイルを削除して、拡張機能を削除します。
%LocalAppData%\Local\Microsoft\VisualStudio\12.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"; }
}
}
}
参照
概念
その他の技術情報
Sample: Command to Align Shapes on a UML Diagram (サンプル: UML 図で図形を整理するコマンド)