如何:在建模图上定义菜单命令

在 Visual Studio 旗舰版中,可以定义当用户右击 UML 关系图时显示的附加菜单项。 可以控制当用户右击关系图上的任何元素时是否显示和启用菜单命令,并且可以编写在用户选择菜单项时运行的代码。 可以将这些扩展打包到 Visual Studio 集成扩展 (VSIX) 中,并将其分发给其他 Visual Studio 旗舰版用户。

要求

定义菜单命令

若要为 UML 设计器创建一个菜单命令,必须创建一个定义该命令的行为的类,并将此类嵌入到 Visual Studio 集成扩展 (VSIX)。 VSIX 充当可安装命令的容器。 可通过两种替代方法来定义菜单命令:

  • **使用项目模板在菜单命令自己的 VSIX 中创建它。**这是一种快速方法。 如果不希望将您的菜单命令与其他类型的扩展(如验证扩展、自定义工具箱项或笔势处理程序)合并,则使用此方法。

  • **创建单独的菜单命令和 VSIX 项目。**如果您想将多个类型的扩展并入同一个 VSIX 中,则请使用此方法。 例如,如果您的菜单命令需要模型遵守特定约束,则可以将菜单命令嵌入到验证方法所在的 VSIX 中。

在菜单命令自己的 VSIX 中创建它

  1. 在**“新建项目”对话框中的“建模项目”下,选择“命令扩展”**。

  2. 在新项目中打开 .cs 文件,并修改 CommandExtension 类以实现您的命令。

    有关更多信息,请参见实现菜单命令。

  3. 您可以通过定义新类向此项目中添加其他命令。

  4. 按 F5 测试菜单命令。 有关更多信息,请参见执行菜单命令。

  5. 通过复制由您的项目生成的 bin\*\*.vsix 文件,在其他计算机上安装菜单命令。 有关更多信息,请参见安装菜单命令。

在类库 (DLL) 项目中创建单独的菜单命令

  1. 在新的 Visual Studio 解决方案或现有解决方案中创建类库项目。

    1. 在**“文件”菜单上指向“新建”,然后单击“项目”**。

    2. 在**“已安装的模板”下,单击“Visual C#”“Visual Basic”。 在中间列中,单击“类库”**。

    3. 设置**“解决方案”**以指示您是希望创建新的解决方案,还是希望向已打开的 VSIX 解决方案添加组件。

    4. 设置项目的名称和位置,然后单击“确定”。

  2. 将下列引用添加到项目中。

    引用

    可完成的操作

    System.ComponentModel.Composition

    使用托管扩展性框架 (MEF) 定义组件。

    Microsoft.VisualStudio.Uml.Interfaces

    读取并更改模型元素的属性。

    Microsoft.VisualStudio.ArchitectureTools.Extensibility

    在关系图上创建模型元素、修改形状。

    Microsoft.VisualStudio.Modeling.Sdk.10.0

    定义模型事件处理程序。

    将一系列更改封装到模型中。 有关更多信息,请参见如何:使用事务链接模型更新

    Microsoft.VisualStudio.Modeling.Sdk.Diagrams.10.0

    (并不总是需要)

    访问笔势处理程序的其他关系图元素。

    Microsoft.VisualStudio.ArchitectureTools.Extensibility.Layer

    仅层关系图上的命令需要。 有关更多信息,请参见创建层关系图的扩展

    在层关系图上定义命令。

  3. 将类文件添加到项目中,并将其内容设置为下面的代码。

    提示

    根据您的喜好更改 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] // if you have installed Feature Pack 1
    
      // All menu commands must export ICommandExtension:
      [Export (typeof(ICommandExtension))]
      // CHANGE class name
      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 项目中添加单独的菜单命令

  1. 如果您已使用菜单命令自己的 VSIX 创建了它,则不需要此过程。

  2. 创建一个 VSIX 项目(如果解决方案中已有一个 VSIX 项目,则无需执行此步骤)。

    1. 在**“解决方案资源管理器”中,右击该解决方案,指向“添加”,然后单击“新建项目”**。

    2. 在**“已安装的模板”下,展开“Visual C#”“Visual Basic”,然后单击“扩展性”。 在中间列中,单击“VSIX 项目”**。

  3. 将 VSIX 项目设置为解决方案的启动项目。

    在解决方案资源管理器中,右击该 VSIX 项目,然后单击**“设为启动项目”**。

  4. source.extension.vsixmanifest 中的**“内容”**下,将类库项目添加为 MEF 组件。

    1. 打开 source.extension.vsixmanifest

    2. 单击**“添加内容”**。

    3. 在**“选择内容类型”处,选择“MEF 组件”**。

    4. 在**“选择源”处,单击“项目”**,并选择类库项目的名称。

  5. 单击**“选择版本”**,并选择要运行扩展的 Visual Studio 版本。

  6. 设置 VSIX 的名称字段和描述性字段。 保存该文件。

实现菜单命令

该菜单命令类为 ICommandExtension 实现所需的方法。

string Text { get; }

返回菜单项的标签。

void QueryStatus(IMenuCommand command);

当用户在关系图中右击时调用。

此方法不应更改模型。

DiagramContext.CurrentDiagram.SelectedShapes 用于确定是否显示并启用该命令。

设置:

  • 如果当用户在关系图中右击时该命令必须显示在菜单中,则将 command.Visible 设置为 true

  • 如果用户可以在菜单中单击该命令,则将 command.Enabled 设置为 true

  • command.Text 用于动态设置菜单标签

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 模型

执行菜单命令

仅为测试目的在调试模式下执行命令。

测试菜单命令

  1. F5,或在**“调试”菜单上单击“开始调试”**。

    这将启动 Visual Studio 的实验实例。

    疑难解答:如果新的 Visual Studio 未启动:

    • 如果您有多个项目,请确保将 VSIX 项目设置为解决方案的启动项目。

    • 在解决方案资源管理器中,右击启动项目,然后单击“属性”。 在项目属性编辑器中,单击**“调试”选项卡。 确保“启动外部程序”**字段中的字符串是 Visual Studio 的完整路径名,通常为:

      C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\devenv.exe

  2. 在实验性 Visual Studio 中,打开或创建一个建模项目,然后打开或创建一个建模图。 使用属于菜单命令类的特性中列出的某个类型的关系图。

  3. 在关系图上的任意位置右击。 您的命令应出现在菜单中。

    疑难解答:如果您的命令没有出现在菜单中,请确保:

    • 该菜单命令项目作为一个 MEF 组件在 VSIX 项目的 source.extensions.manifest 中的**“内容”**列表中列出。

    • Import 和 Export 特性的参数有效。

    • QueryStatus 方法未将 command.Enabled 或 Visible 字段设置为 false。

    • 正在使用的模型关系图的类型(UML 类、序列等)作为菜单命令类特性([ClassDesignerExtension]、[SequenceDesignerExtension] 等)之一列出。

安装和卸载扩展

既可以在您自己的计算机上也可以在其他计算机上安装 Visual Studio 扩展。

安装扩展

  1. 在您的计算机中,查找由 VSIX 项目生成的 .vsix 文件。

    1. 在**“解决方案资源管理器”中,右击 VSIX 项目,然后单击“在 Windows 资源管理器中打开文件夹”**。

    2. 找到文件 bin\*\您的项目.vsix

  2. .vsix 文件复制到要安装扩展的目标计算机上。 该计算机可为您自己的计算机或其他计算机。

    目标计算机必须装有 source.extension.vsixmanifest 中指定的 Visual Studio 版本之一。

  3. 在目标计算机上,双击 .vsix 文件。

    **“Visual Studio Extension Installer”**将会打开并安装扩展。

  4. 启动或重新启动 Visual Studio。

卸载扩展

  1. 在**“工具”菜单上,单击“扩展管理器”**。

  2. 展开**“已安装的扩展”**。

  3. 选择扩展,然后单击**“卸载”**。

在极少数情况下,有错误的扩展无法加载并在错误窗口中创建报告,但不显示在扩展管理器中。 在这种情况下,可以通过从以下位置删除文件来移除扩展:

%LocalAppData%\Local\Microsoft\VisualStudio\10.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"; }
  }
  }

}

请参见

概念

扩展 UML 模型和关系图

如何:为 UML 模型定义验证约束

使用 UML API 编程

其他资源

如何:定义和安装建模扩展

如何:在建模图上定义放置和双击处理程序

如何:定义自定义建模工具箱项

如何:使用 UML API 编辑序列图

Sample: Command to Align Shapes on a UML Diagram(示例:用于对齐 UML 关系图上的形状的命令)