Практическое руководство. Отображение модели на схемах
В программном коде для расширения 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);
Примечание
Если метод Display используется внутри транзакции ILinkedUndo, иногда метод не возвращает IShape.При этом фигура создана правильно, и доступ к ней можно получить через IElement.Shapes().
|
Дочерний элемент классификатора |
Атрибут, операция, часть, порт. |
Автоматический — код не требуется. Отображается в качестве части родительского элемента. |
Поведение |
Взаимодействие (последовательность), Действие |
Привяжите поведение к соответствующей схеме. Каждое поведение можно привязать только к одной схеме за раз. Например: 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>() |
Фигуры, содержащиеся в 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()
Доступ к схемам в проекте.
API-интерфейс Visual Studio можно использовать для открытия и создания проектов и схем моделирования.
Обратите внимание на приведение из 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;
}
}
}
См. также
Основные понятия
Практическое руководство. Навигация по UML-модели