Практическое руководство. Отображение модели на схемах

В программном коде для расширения 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.

При этом не удаляется поведение из модели.

Любая другая фигура

Невозможно явно удалить из схемы другие фигуры.Фигура автоматически исчезнет, если элемент удаляется из модели, либо если родительская фигура удаляется из схемы.

Открытие и создание схем

Ee330928.collapse_all(ru-ru,VS.110).gifДоступ к текущей схеме пользователя из расширения команды или жеста

Объявите данное импортированное свойство в классе.

[Import]

IDiagramContext Context { get; set; }

 

Осуществите доступ к схеме в методе.

IClassDiagram classDiagram =

Context.CurrentDiagram as IClassDiagram;

ПримечаниеПримечание

Экземпляр IDiagram (и его вложенные типы, такие как IClassDiagram) действителен только внутри обрабатываемых команд.Не рекомендуется хранить объект IDiagram в переменной, которая сохраняется при возвращении элемента управления пользователю.

Дополнительные сведения см. в разделе Практическое руководство. Определение команды меню на схеме моделирования.

Ee330928.collapse_all(ru-ru,VS.110).gifПолучение списка открытых схем

Список схем, открытых в проекте в настоящее время.

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

Практическое руководство. Навигация по UML-модели

Другие ресурсы

Sample: Align Shapes on a Diagram menu command

Sample: Creating Elements, Shapes and Stereotypes