如何:在建模图上定义菜单命令
在最终的Visual Studio,可以在UML关系图的快捷菜单的其他菜单项。您可以控制菜单命令是否显示并启用任何组件的快捷菜单在关系图上,因此,您可以运行编写代码,以便在用户选择菜单项。可以将这些扩展打包到一个Visual Studio集成扩展(VSIX)并分发给其他Visual Studio用户。
要求
Visual Studio SDK,您可以从 Visual Studio库获取。
Visual Studio可视化和建模SDK,则可以从 Visual Studio在代码库中的可视化和建模SDK获取。
定义菜单命令
若要为 UML 设计器创建一个菜单命令,必须创建一个定义该命令的行为的类,并将此类嵌入到 Visual Studio 集成扩展 (VSIX)。VSIX 充当可安装命令的容器。可通过两种替代方法来定义菜单命令:
**使用项目模板在菜单命令自己的 VSIX 中创建它。**这是一种快速方法。如果不希望将您的菜单命令与其他类型的扩展(如验证扩展、自定义工具箱项或笔势处理程序)合并,则使用此方法。
**创建单独的菜单命令和 VSIX 项目。**如果将多个类型的扩展并入同一个 VSIX 中,则请使用此方法。例如,如果您的菜单命令需要模型遵守特定约束,则可以将菜单命令嵌入到验证方法所在的 VSIX 中。
在菜单命令自己的 VSIX 中创建它
在**“新建项目”对话框中的“建模项目”下,选择“命令扩展”**。
在新项目中打开 .cs 文件,并修改 CommandExtension 类以实现您的命令。
有关更多信息,请参见实现菜单命令。
您可以通过定义新类向此项目中添加其他命令。
按 F5 测试菜单命令。有关更多信息,请参见执行菜单命令。
通过复制由您的项目生成的 bin\*\*.vsix 文件,在其他计算机上安装菜单命令。有关更多信息,请参见安装菜单命令。
这是一种替代方法:
在单独的选件类库(DLL)中创建菜单命令项目
创建选件类库项目,并在新Visual Studio解决方案,或在现有解决方案。
在**“文件”菜单上,选择“新建”和“项目”**。
在 ***** 安装的模板 *****下,选择 *** visual C# *** 或 *** Visual Basic ***。在中间列中,选择 *** 类库 ***。
设置**“解决方案”**以指示您是希望创建新的解决方案,还是希望向已打开的 VSIX 解决方案添加组件。
设置项目的名称和位置,然后单击“确定”。
将下列引用添加到项目中。
引用
可完成的操作
System.ComponentModel.Composition
使用托管扩展性框架 (MEF) 定义组件。
Microsoft.VisualStudio.Uml.Interfaces
读取并更改模型元素的属性。
Microsoft.VisualStudio.ArchitectureTools.Extensibility
在关系图上创建模型元素、修改形状。
Microsoft.VisualStudio.Modeling.Sdk.11.0
定义模型事件处理程序。
将一系列更改封装到模型中。有关更多信息,请参见如何:使用事务链接模型更新。
Microsoft.VisualStudio.Modeling.Sdk.Diagrams.11.0
(并不总是需要)
访问笔势处理程序的其他关系图元素。
Microsoft.VisualStudio.ArchitectureTools.Extensibility.Layer
仅层关系图上的命令需要。有关更多信息,请参见扩展层关系图。
在层关系图上定义命令。
将类文件添加到项目中,并将其内容设置为下面的代码。
说明 根据您的喜好更改 Text 返回的命名空间、类名和值。
如果定义多个命令,它们按字母顺序出现在菜单类名。
using System.ComponentModel.Composition; using System.Linq; using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Presentation; using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml; using Microsoft.VisualStudio.Modeling.ExtensionEnablement; using Microsoft.VisualStudio.Uml.AuxiliaryConstructs; using Microsoft.VisualStudio.Uml.Classes; // ADD other UML namespaces if required namespace UMLmenu1 // CHANGE { // DELETE any of these attributes if the command // should not appear in some types of diagram. [ClassDesignerExtension] [ActivityDesignerExtension] [ComponentDesignerExtension] [SequenceDesignerExtension] [UseCaseDesignerExtension] // [LayerDesignerExtension] // All menu commands must export ICommandExtension: [Export (typeof(ICommandExtension))] // CHANGE class name – determines order of appearance on menu: public class Menu1 : ICommandExtension { [Import] public IDiagramContext DiagramContext { get; set; } public void QueryStatus(IMenuCommand command) { // Set command.Visible or command.Enabled to false // to disable the menu command. command.Visible = command.Enabled = true; } public string Text { get { return "MENU COMMAND LABEL"; } } public void Execute(IMenuCommand command) { // A selection of starting points: IDiagram diagram = this.DiagramContext.CurrentDiagram; foreach (IShape<IElement> shape in diagram.GetSelectedShapes<IElement>()) { IElement element = shape.Element; } IModelStore modelStore = diagram.ModelStore; IModel model = modelStore.Root; foreach (IElement element in modelStore.AllInstances<IClass>()) { } } } }
有关要在方法中放置的内容的更多信息,请参见实现菜单命令。
您必须将菜单命令添加到一个充当命令安装容器的 VSIX 项目中。如果需要,可以将其他组件包括在同一 VSIX 中。
若要添加菜单命令添加到VSIX项目
如果您已使用菜单命令自己的 VSIX 创建了它,则不需要此过程。
创建一个 VSIX 项目(如果解决方案中已有一个 VSIX 项目,则无需执行此步骤)。
在 *** 解决方案资源管理器 ***,解决方案中的快捷菜单上,选择 *** 添加 ***,*** 新项目 ***。
在 ***** 安装的模板 *****下,展开 *** visual C# *** 或 *** Visual Basic ***,然后选择 *** 扩展性 ***。在中间列中,选择 *** VSIX项目 ***。
在解决方案资源管理器中,在VSIX项目的快捷菜单上,选择 *** 设置为启动项目 ***。
打开 source.extension.vsixmanifest。
在 *** 元数据 *** 选项卡上,将一个名称VSIX中。
在 *** 安装目标 *** 选项卡上,将Visual Studio最终和高质量作为目标。
在 *** 属性 *** 选项卡中,选择 新建,然后在对话框中,设置:
*** 类型 *** = *** MEF组件 ***
源 = *** 在当前解决方案中的项目 ***
项目 = 您的选件类库项目
实现菜单命令
该菜单命令类为 ICommandExtension 实现所需的方法。
string Text { get; } |
返回菜单项的标签。 |
void QueryStatus(IMenuCommand command); |
当用户在关系图中右击时调用。 此方法不应更改模型。 DiagramContext.CurrentDiagram.SelectedShapes 用于确定是否显示并启用该命令。 设置:
|
void Execute (IMenuCommand command); |
当用户单击菜单项(如果它可见并已启用)时调用。
|
访问代码中的模型
在菜单命令类中包括以下声明:
[Import] public IDiagramContext DiagramContext { get; set; }
...
IDiagramContext 的声明允许您在方法中编写可访问关系图、当前选定内容和模型的代码:
IDiagram diagram = this.DiagramContext.CurrentDiagram;
foreach (IShape<IElement> shape in diagram.GetSelectedShapes<IElement>())
{ IElement element = shape.Element; ... }
IModelStore modelStore = diagram.ModelStore;
IModel model = modelStore.Root;
foreach (IElement element in modelStore.AllInstances<IUseCase>()) {...}
导航和更新模型
可通过 API 使用 UML 模型的所有元素。可以从当前选择或模型的根中访问所有其他元素。有关更多信息,请参见如何:导航 UML 模型和使用 UML API 编程。
如果您处理的是序列图,请参见如何:使用 UML API 编辑序列图。
还可以利用 API 执行以下操作:更改元素的属性、删除元素和关系以及创建新的元素和关系。
默认情况下,将在单独的事务中执行您将在 Execute 方法中所做的每项更改。用户将能够单独撤消每项更改。如果您希望将这些更改组合成单个事务,请使用ILinkedUndoTransaction,如如何:使用事务链接模型更新中所述。
使用 UI 线程进行更新
在某些情况下,从后台线程对模型进行更新会很有用。例如,如果您的命令从速度较慢的资源加载数据,则可以在后台线程中执行加载,以便用户能够查看正在进行的更改,并在必要时取消该操作。
但请注意,该模型存储区并不是线程安全的。应始终使用用户界面 (UI) 线程进行更新,如果可能,应阻止用户在后台操作正在进行的过程中执行编辑。有关示例,请参见如何:从后台线程中更新 UML 模型。
执行菜单命令
仅为测试目的在调试模式下执行命令。
测试菜单命令
按 *** F5 ***,或者在 *** 调试 *** 菜单上,选择 *** 开始调试 ***。
这将启动 Visual Studio 的实验实例。
疑难解答:如果新的 Visual Studio 未启动:
如果您有多个项目,请确保将 VSIX 项目设置为解决方案的启动项目。
在解决方案资源管理器中,该启动或项目的快捷菜单仅,选择 属性。在项目属性编辑器中,选择 *** 调试 *** 选项。确保**“启动外部程序”**字段中的字符串是 Visual Studio 的完整路径名,通常为:
C:\Program Files\Microsoft Visual Studio 11.0\Common7\IDE\devenv.exe
在实验性 Visual Studio 中,打开或创建一个建模项目,然后打开或创建一个建模图。使用属于菜单命令类的特性中列出的某个类型的关系图。
打开快捷菜单关系图上的任意位置。您的命令应出现在菜单中。
疑难解答:如果您的命令没有出现在菜单中,请确保:
菜单命令项目列表为 *** 属性 *** 选项的一个MEF组件在VSIX项目的 source.extensions.manifest。
Import 和 Export 特性的参数有效。
QueryStatus 方法未将 command.Enabled 或 Visible 字段设置为 false。
正在使用的模型关系图的类型(UML 类、序列等)作为菜单命令类特性([ClassDesignerExtension]、[SequenceDesignerExtension] 等)之一列出。
安装和卸载扩展
既可以在您自己的计算机上也可以在其他计算机上安装 Visual Studio 扩展。
安装扩展
在您的计算机中,查找由 VSIX 项目生成的 .vsix 文件。
在 *** 解决方案资源管理器 ***,在VSIX项目的快捷菜单上,选择 *** 打开在Windows资源管理器中的文件夹 ***。
找到文件 bin\*\您的项目.vsix
将 .vsix 文件复制到要安装扩展的目标计算机上。该计算机可为您自己的计算机或其他计算机。
目标计算机必须装有 source.extension.vsixmanifest 中指定的 Visual Studio 版本之一。
在目标计算机上,请通过双击以打开 .vsix 文件,如。
**“Visual Studio Extension Installer”**将会打开并安装扩展。
启动或重新启动 Visual Studio。
卸载扩展
在 *** 工具 *** 菜单中,选择 *** 扩展管理器 ***。
展开**“已安装的扩展”**。
选择扩展,然后选择 *** 卸载 ***。
在极少数情况下,有错误的扩展无法加载并在错误窗口中创建报告,但不显示在扩展管理器中。在这种情况下,可以通过从以下位置删除文件来移除扩展:
%LocalAppData%\Local\Microsoft\VisualStudio\11.0\Extensions
示例
下面的示例演示一个菜单命令的代码,该命令用于交换类图中两个元素的名称。此代码必须在 Visual Studio 扩展项目中生成,并按前面几节所述进行安装。
using System.Collections.Generic; // for IEnumerable
using System.ComponentModel.Composition;
// for [Import], [Export]
using System.Linq; // for IEnumerable extensions
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Presentation;
// for IDiagramContext
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml;
// for designer extension attributes
using Microsoft.VisualStudio.Modeling.Diagrams;
// for ShapeElement
using Microsoft.VisualStudio.Modeling.ExtensionEnablement;
// for IGestureExtension, ICommandExtension, ILinkedUndoContext
using Microsoft.VisualStudio.Uml.Classes;
// for class diagrams, packages
/// <summary>
/// Extension to swap names of classes in a class diagram.
/// </summary>
namespace SwapClassNames
{
// Declare the class as an MEF component:
[Export(typeof(ICommandExtension))]
[ClassDesignerExtension]
// Add more ExportMetadata attributes to make
// the command appear on diagrams of other types.
public class NameSwapper : ICommandExtension
{
// MEF required interfaces:
[Import]
public IDiagramContext Context { get; set; }
[Import]
public ILinkedUndoContext LinkedUndoContext { get; set; }
/// <summary>
/// Swap the names of the currently selected elements.
/// </summary>
/// <param name="command"></param>
public void Execute(IMenuCommand command)
{
// Get selected shapes that are IClassifiers -
// IClasses, IInterfaces, IEnumerators.
var selectedShapes = Context.CurrentDiagram
.GetSelectedShapes<IClassifier>();
if (selectedShapes.Count() < 2) return;
// Get model elements displayed by shapes.
IClassifier firstElement = selectedShapes.First().Element;
IClassifier lastElement = selectedShapes.Last().Element;
// Do the swap in a transaction so that user
// cannot undo one change without the other.
using (ILinkedUndoTransaction transaction =
LinkedUndoContext.BeginTransaction("Swap names"))
{
string firstName = firstElement.Name;
firstElement.Name = lastElement.Name;
lastElement.Name = firstName;
transaction.Commit();
}
}
/// <summary>
/// Called by Visual Studio to determine whether
/// menu item should be visible and enabled.
/// </summary>
public void QueryStatus(IMenuCommand command)
{
int selectedClassifiers = Context.CurrentDiagram
.GetSelectedShapes<IClassifier>().Count();
command.Visible = selectedClassifiers > 0;
command.Enabled = selectedClassifiers == 2;
}
/// <summary>
/// Name of the menu command.
/// </summary>
public string Text
{
get { return "Swap Names"; }
}
}
}