方法: 図にモデルを表示する
更新 : 2011 年 3 月
Visual Studio Ultimate に対する拡張機能のプログラム コードにおいて、モデル要素をどのように図に表示するかを制御できます。
このトピックの内容
図にモデルを表示するには
要素を表すシェイプへのアクセス
図形の移動とサイズ変更
図から図形を削除するには
図のオープンと生成
例: 図形を整列させるためのコマンド
図にモデルを表示するには
ユース ケースやアクションのような要素を生成した場合、ユーザーは UML モデル エクスプローラー内でその要素を確認できますが、要素は自動的には図に表示されません。 場合によっては、要素を表示するためのコードを記述する必要があります。 これらの代替手段の概要を次の表に示します。
要素の型 |
例 |
要素を表示するためにコードで行う操作 |
---|---|---|
分類子 |
Class Component Actor Use Case |
関連するシェイプを指定された図に生成します。 各分類子に対して任意の数のシェイプを生成できます。 diagram.Display<modelElementType> (modelElement, parentShape, xPosition , yPosition); 図のトップ レベルの図形に対して、parentShape を null に設定します。 シェイプの中に別のシェイプを表示するには: IShape<IUseCase> usecaseShape = useCaseDiagram.Display (useCase, subsystemShape, subsystemShape.XPosition + 5, subsystemShape.YPosition + 5);
メモ
ILinkedUndo トランザクション内で Display を実行した場合、IShape が返されないことがあります。その場合でも、シェイプは正常に生成されており、IElement.Shapes(). を使用してアクセスできます。
|
分類子の子 |
属性、操作、 パート、ポート |
自動: コードは不要です。 親の一部として表示されます。 |
動作 |
相互作用 (シーケンス)、 アクティビティ |
振る舞いを適切な図にバインドします。 各振る舞いは、一度に最大で 1 つの図にバインドできます。 次に例を示します。 sequenceDiagram.Bind(interaction); activityDiagram.Bind(activity); |
振る舞いの子 |
生存線、メッセージ、アクション、オブジェクト ノード |
自動: コードは不要です。 親が図にバインドされている場合に表示されます。 |
関係 |
関連、汎化、フロー、依存関係 |
自動: コードは不要です。 両方の端部が表示されているすべての図に表示されます。 |
要素を表すシェイプへのアクセス
要素を表すシェイプは、型に属します。
IShape
IShape<<要素の型>>
ここで、<要素の型> は、モデル要素 (たとえば、IClass、IUseCase) です。
anElement.Shapes () |
開いている図の中のこの要素を表すすべての IShapes。 |
anElement.Shapes(aDiagram) |
特定の図のこの要素を表すすべての IShapes。 |
anIShape.GetElement() |
シェイプが表す IElement。 通常、IElement のサブクラスにキャストします。 |
anIShape.Diagram |
シェイプを格納する IDiagram。 |
anIShape.ParentShape |
anIShape を格納するシェイプ。 たとえば、ポート シェイプはコンポーネント シェイプ内に格納されます。 |
anIShape.ChildShapes |
IShape または IDiagram に格納されているシェイプ。 |
anIShape.GetChildShapes<IUseCase>() |
指定された型の要素 (たとえば、IUseCase) を表す IShape または IDiagram に格納されているシェイプ。 |
IShape iShape = ...; IShape<IClass> classShape = iShape.ToIShape<IClass>(); IClass aClass = classShape.Element; |
一般的な IShape を、厳密に型指定された IShape<IElement> にキャストします。 |
IShape<IClassifier> classifierShape; IShape<IUseCase> usecaseShape = classifierShape.ToIShape<IUseCase>(); |
パラメーター化されたシェイプ型のシェイプを別の型にキャストします。 |
図形の移動とサイズ変更
anIShape.Move(x, y, [width], [height]) |
シェイプの移動またはサイズ変更を行います。 |
IDiagram.EnsureVisible( IEnumerable<IShape> shapes, bool zoomToFit = false) |
ウィンドウをアクティブ化して図をスクロールし、指定されたすべての図形を表示します。 すべての図形は、図上にある必要があります。 zoomToFit が true の場合、すべての図形が表示されるよう、必要に応じて図が拡大縮小されます。 |
例については、「Defining an Alignment Command (整列コマンドの定義)」を参照してください。
図から図形を削除するには
特定の型の要素のシェイプを、要素を削除することなく削除できます。
モデル要素 |
シェイプを削除するには |
---|---|
分類子: クラス、インターフェイス、列挙、アクター、ユース ケース、またはコンポーネント |
shape.Delete(); |
振る舞い: 相互作用またはアクティビティ |
図をプロジェクトから削除できます。 IDiagram.FileName を使用し、パスを取得します。 この場合、振る舞いはモデルから削除されません。 |
その他のシェイプ |
その他のシェイプを明示的に図から削除することはできません。 要素がモデルから削除された場合や、親シェイプが図から削除された場合、シェイプは自動的に非表示になります。 |
図のオープンと生成
コマンドまたはジェスチャ拡張機能を使用してユーザーの現在の図にアクセスするには
次のようにインポート プロパティをクラスで宣言します。
[Import]
IDiagramContext Context { get; set; }
メソッドで、図にアクセスします。
IClassDiagram classDiagram =
Context.CurrentDiagram as IClassDiagram;
注意
IDiagram のインスタンス (および IClassDiagram などのサブタイプ) は、処理されるコマンド内でのみ有効です。 コントロールがユーザーに返されている間に保持される変数に、IDiagram オブジェクトを入れておくことはお勧めできません。
詳細については、「方法: モデリング図にメニュー コマンドを定義する」を参照してください。
開いている図の一覧を取得するには
プロジェクト内で現在開かれている図の一覧です。
Context.CurrentDiagram.ModelStore.Diagrams()
プロジェクト内の図にアクセスするには
Visual Studio API を使用し、モデリング プロジェクトおよび図を開いたり、生成したりできます。
EnvDTE.ProjectItem から IDiagramContext へのキャストに注意してください。
using EnvDTE; // Visual Studio API
...
[Import]
public IServiceProvider ServiceProvider { get; set; }
...
// Get Visual Studio API
DTE dte = ServiceProvider.GetService(typeof(DTE)) as DTE;
// Get current Visual Studio project
Project project = dte.ActiveDocument.ProjectItem.ContainingProject;
// Open and process every diagram in the project.
foreach (ProjectItem item in project.ProjectItems)
{
// Cast ProjectItem to IDiagramContext
IDiagramContext context = item as IDiagramContext;
if (context == null)
{
// This is not a diagram file.
continue;
}
// Open the file and give the window the focus.
if (!item.IsOpen)
{
item.Open().Activate();
}
// Get the diagram.
IDiagram diagram = context.CurrentDiagram;
// Deal with specific diagram types.
ISequenceDiagram seqDiagram = diagram as ISequenceDiagram;
if (seqDiagram != null)
{ ... } } }
IDiagram のインスタンスおよびサブタイプは、Visual Studio にコントロールを返した後は有効ではなくなります。
Visual Studio プロジェクトからモデル ストアを取得することもできます。
Project project = ...;
IModelStore modelStore = (project as IModelingProject).Store;
例: 図形を整列させるためのコマンド
次に示すコードでは、図形をきれいに整列させるメニュー コマンドを実装します。 ユーザーは、まず 2 つ以上の形状を垂直方向または水平方向に適切に配置する必要があります。 次に整列コマンドを使用して、形状の中心を整列させます。
コマンドを使用できるようにするには、このコードをメニュー コマンド プロジェクトに追加し、生成された拡張機能をユーザーに配置します。 詳細については、「方法: モデリング図にメニュー コマンドを定義する」を参照してください。
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Presentation;
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml;
using Microsoft.VisualStudio.Modeling.ExtensionEnablement;
namespace AlignCommand
{
// Implements a command to align shapes in a UML class diagram.
// The user first selects shapes that are roughly aligned either vertically or horizontally.
// This command will straighten them up.
// Place this file in a menu command extension project.
// See https://msdn.microsoft.com/library/ee329481.aspx
[Export(typeof(ICommandExtension))]
[ClassDesignerExtension] // TODO: Add other diagram types if needed
class CommandExtension : ICommandExtension
{
/// <summary>
/// See https://msdn.microsoft.com/library/ee329481.aspx
/// </summary>
[Import]
IDiagramContext context { get; set; }
/// <summary>
/// Transaction context.
/// See https://msdn.microsoft.com/library/ee330926.aspx
/// </summary>
[Import]
ILinkedUndoContext linkedUndo { get; set; }
/// <summary>
/// Called when the user selects the command.
/// </summary>
/// <param name="command"></param>
public void Execute(IMenuCommand command)
{
Align(context.CurrentDiagram.SelectedShapes);
}
/// <summary>
/// Called when the user right-clicks on the diagram.
/// Determines whether the command is enabled.
/// </summary>
/// <param name="command"></param>
public void QueryStatus(IMenuCommand command)
{
IEnumerable<IShape> currentSelection = context.CurrentDiagram.SelectedShapes;
// Make it visible if there are shapes selected:
command.Visible = currentSelection.Count() > 0 && !(currentSelection.FirstOrDefault() is IDiagram);
// Make it enabled if there are two or more shapes that are roughly in line:
command.Enabled = currentSelection.Count() > 1
&& (HorizontalAlignCenter(currentSelection) > 0.0
|| VerticalAlignCenter(currentSelection) > 0.0);
}
/// <summary>
/// Title of the menu command.
/// </summary>
public string Text
{
get { return "Align Shapes"; }
}
/// <summary>
/// Find a horizontal line that goes through a list of shapes.
/// </summary>
/// <param name="shapes"></param>
/// <returns></returns>
private static double HorizontalAlignCenter(IEnumerable<IShape> shapes)
{
double Y = -1.0;
double top = 0.0, bottom = shapes.First().Bottom();
foreach (IShape shape in shapes)
{
top = Math.Max(top, shape.Top());
bottom = Math.Min(bottom, shape.Bottom());
}
if (bottom > top) Y = (bottom + top) / 2.0;
return Y;
}
/// <summary>
/// Find a vertical line that goes through a list of shapes.
/// </summary>
/// <param name="shapes"></param>
/// <returns></returns>
private static double VerticalAlignCenter(IEnumerable<IShape> shapes)
{
double X = -1.0;
double left = 0.0, right = shapes.First().Right();
foreach (IShape shape in shapes)
{
left = Math.Max(left, shape.Left());
right = Math.Min(right, shape.Right());
}
if (right > left) X = (right + left) / 2.0;
return X;
}
/// <summary>
/// Line up those shapes that are roughly aligned.
/// </summary>
/// <param name="shapes"></param>
private void Align(IEnumerable<IShape> shapes)
{
if (shapes.Count() > 1)
{
// The shapes must all overlap either horizontally or vertically.
// Find a horizontal line that is covered by all the shapes:
double Y = HorizontalAlignCenter(shapes);
if (Y > 0.0) // Negative if they don't overlap.
{
// Adjust all the shape positions in one transaction:
using (ILinkedUndoTransaction t = linkedUndo.BeginTransaction("align"))
{
foreach (IShape shape in shapes)
{
shape.AlignYCenter(Y);
}
t.Commit();
}
}
else
{
// Find a vertical line that is covered by all the shapes:
double X = VerticalAlignCenter(shapes);
if (X > 0.0) // Negative if they don't overlap.
{
// Adjust all the shape positions in one transaction:
using (ILinkedUndoTransaction t = linkedUndo.BeginTransaction("align"))
{
foreach (IShape shape in shapes)
{
shape.AlignXCenter(X);
}
t.Commit();
}
}
}
}
}
}
/// <summary>
/// Convenience extensions for IShape.
/// </summary>
public static class IShapeExtension
{
public static double Bottom(this IShape shape)
{
return shape.YPosition + shape.Height;
}
public static double Top(this IShape shape)
{
return shape.YPosition;
}
public static double Left(this IShape shape)
{
return shape.XPosition;
}
public static double Right(this IShape shape)
{
return shape.XPosition + shape.Width;
}
public static void AlignYCenter(this IShape shape, double Y)
{
shape.Move(shape.XPosition, Y - shape.YCenter());
}
public static void AlignXCenter(this IShape shape, double X)
{
shape.Move(X - shape.XCenter(), shape.YPosition);
}
/// <summary>
/// We can adjust what bit of the shape we want to be aligned.
/// The default is the center of the shape.
/// </summary>
/// <param name="shape"></param>
/// <returns></returns>
public static double YCenter(this IShape shape)
{
return shape.Height / 2.0;
}
/// <summary>
/// We can adjust what bit of the shape we want to be aligned.
/// The default is the center of the shape.
/// </summary>
/// <param name="shape"></param>
/// <returns></returns>
public static double XCenter(this IShape shape)
{
return shape.Width / 2.0;
}
}
}
参照
その他の技術情報
Sample: Align Shapes on a Diagram menu command ([図] メニュー コマンドで図形を整理する)
Sample: Creating Elements, Shapes and Stereotypes (サンプル: 要素、図形、およびステレオタイプの作成)
履歴の変更
日付 |
履歴 |
理由 |
---|---|---|
2011 年 3 月 |
整列例を追加。 |
カスタマー フィードバック |