如何:在关系图上显示模型

在 Visual Studio 旗舰版 的扩展的程序代码中,可以控制模型元素在关系图中的显示方式。

  • 本主题内容:

    • 在关系图上显示元素

    • 访问表示元素的形状

    • 移动形状并调整其大小

    • 从关系图中移除形状

    • 打开和创建关系图

    • 示例:使形状对齐的命令

在关系图上显示元素

创建某个元素(如用例或操作)后,用户可在 UML 模型资源管理器中看到该元素,但它不会始终自动显示在关系图中。在某些情况下,必须编写代码来显示该元素。下表总结了这些例外情况。

 

元素类型

例如

若要显示此元素,您的代码必须为

Classifier

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 事务内执行显示,则方法有时不会返回任何 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(zh-cn,VS.110).gif从命令或笔势扩展访问用户的当前关系图

在您的类中声明此导入的属性:

[Import]

IDiagramContext Context { get; set; }

 

在某个方法中,访问关系图:

IClassDiagram classDiagram =

Context.CurrentDiagram as IClassDiagram;

说明说明

IDiagram 的实例(及其子类型,如 IClassDiagram)仅在要处理的命令中有效。建议不要将 IDiagram 对象保留在当控件返回给用户时依然存在的变量中。

有关更多信息,请参见如何:在建模图上定义菜单命令

Ee330928.collapse_all(zh-cn,VS.110).gif获取已打开的关系图的列表

项目中当前已打开的关系图的列表:

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;
    }
  }
}

请参见

概念

扩展 UML 模型和关系图

如何:导航 UML 模型

其他资源

Sample: Align Shapes on a Diagram menu command(示例:根据关系图菜单命令对齐形状)

Sample: Creating Elements, Shapes and Stereotypes(示例:创建元素、形状和构造型)