如何:在关系图上显示模型
在 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 获取该路径。 这不会从模型中删除行为。 |
任何其他形状 |
无法从关系图中显式删除其他形状。如果从模型中删除了元素或从关系图中移除了父形状,则该形状也将自动消失。 |
打开和创建关系图
从命令或笔势扩展访问用户的当前关系图
在您的类中声明此导入的属性:
[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;
}
}
}
请参见
概念
其他资源
Sample: Align Shapes on a Diagram menu command(示例:根据关系图菜单命令对齐形状)
Sample: Creating Elements, Shapes and Stereotypes(示例:创建元素、形状和构造型)