방법: 다이어그램에 모델 표시
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()를 사용하여 액세스할 수 있습니다. |
분류자의 자식 |
특성, 작업 파트, 포트 |
자동 - 코드가 필요하지 않습니다. 부모의 일부로 표시됩니다. |
동작 |
상호 작용(시퀀스), 작업 |
적절한 다이어그램에 동작을 바인딩합니다. 각 동작은 한 번에 최대 하나의 다이어그램에만 바인딩될 수 있습니다. 예를 들면 다음과 같습니다. sequenceDiagram.Bind(interaction); activityDiagram.Bind(activity); |
동작의 자식 |
수명선, 메시지, 동작, 개체 노드 |
자동 - 코드가 필요하지 않습니다. 부모가 다이어그램에 바인딩되어 있는 경우 표시됩니다. |
관계 |
연결, 일반화, 흐름, 종속성 |
자동 - 코드가 필요하지 않습니다. 양쪽 끝이 모두 표시되는 모든 다이어그램에 표시됩니다. |
요소를 나타내는 모양에 액세스
요소를 나타내는 모양은 다음과 같이 형식에 속합니다.
IShape
IShape<ElementType>
여기서 ElementType은 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>() |
IShape 또는 IDiagram 내에 포함되어 IUseCase와 같이 지정된 형식의 요소를 나타내는 모양입니다. |
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이면 모든 모양을 표시하는 데 필요할 경우 다이어그램이 확장됩니다. |
이에 대한 예제를 보려면 맞춤 명령 정의를 참조하십시오.
다이어그램에서 모양을 제거하려면
일부 형식의 요소는 해당 요소를 삭제하지 않고 요소의 모양을 삭제할 수 있습니다.
모델 요소 |
형식을 제거하려면 |
---|---|
분류자: 클래스, 인터페이스, 열거형, 행위자, 사용 사례 또는 구성 요소 |
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;
예제: 모양 맞춤 명령
다음 코드는 모양을 반듯하게 맞추는 메뉴 명령을 구현합니다. 먼저 두 개 이상의 모양을 세로 또는 가로로 대략 맞춰서 배치해야 합니다. 그런 다음 맞춤 명령을 사용하여 가운데를 맞출 수 있습니다.
이 명령을 제공하려면 이 코드를 메뉴 명령 프로젝트에 추가한 다음 사용자들에게 결과 확장을 배포합니다. 자세한 내용은 방법: 모델링 다이어그램의 메뉴 명령 정의을 참조하십시오.
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;
}
}
}