如何:在建模图上定义笔势处理程序
在最终的 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 文件,并修改 GestureExtension 类来实现您的笔势处理程序。
有关更多信息,请参见实现笔势处理程序。
按 F5 测试笔势处理程序。有关更多信息,请参见执行笔势处理程序。
通过复制您的项目所生成的 bin\*\*.vsix 文件,将笔试处理程序安装到其他计算机上。有关更多信息,请参见安装笔势处理程序。
这是一种替代方法:
创建笔势处理程序的单独选件类库 (dll) 项目
在新的 Visual Studio 解决方案或现有解决方案中创建类库项目。
在**“文件”菜单上,选择“新建”和“项目”**。
在 已安装的模板下,展开 Visual C# 或 Visual Basic中,然后在中间列选择 类库。
将下列引用添加到项目中。
Microsoft.VisualStudio.Modeling.Sdk.11.0
Microsoft.VisualStudio.Modeling.Sdk.Diagrams.11.0
Microsoft.VisualStudio.ArchitectureTools.Extensibility
Microsoft.VisualStudio.Uml.Interfaces
System.ComponentModel.Composition
System.Windows.Forms
Microsoft.VisualStudio.ArchitectureTools.Extensibility.Layer – 只在扩展层关系图时才需要。有关更多信息,请参见扩展层关系图。
将类文件添加到项目中,并将其内容设置为下面的代码。
说明 根据您的喜好更改命名空间和类名。
using System.ComponentModel.Composition; using System.Linq; using System.Collections.Generic; using Microsoft.VisualStudio.Modeling.Diagrams; using Microsoft.VisualStudio.Modeling.Diagrams.ExtensionEnablement; using Microsoft.VisualStudio.Modeling.ExtensionEnablement; using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml; using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Presentation; using Microsoft.VisualStudio.Uml.AuxiliaryConstructs; using Microsoft.VisualStudio.Modeling; using Microsoft.VisualStudio.Uml.Classes; // ADD other UML namespaces if required namespace MyGestureHandler // CHANGE { // DELETE any of these attributes if the handler // should not work with some types of diagram. [ClassDesignerExtension] [ActivityDesignerExtension] [ComponentDesignerExtension] [SequenceDesignerExtension] [UseCaseDesignerExtension] // [LayerDesignerExtension] // Gesture handlers must export IGestureExtension: [Export(typeof(IGestureExtension))] // CHANGE class name public class MyGesture1 : IGestureExtension { [Import] public IDiagramContext DiagramContext { get; set; } /// <summary> /// Called when the user double-clicks on the diagram /// </summary> /// <param name="targetElement"></param> /// <param name="diagramPointEventArgs"></param> public void OnDoubleClick(ShapeElement targetElement, DiagramPointEventArgs diagramPointEventArgs) { // CHANGE THIS CODE FOR YOUR APPLICATION. // Get the target shape, if any. Null if the target is the diagram. IShape targetIShape = targetElement.CreateIShape(); // Do something... } /// <summary> /// Called repeatedly when the user drags from anywhere on the screen. /// Return value should indicate whether a drop here is allowed. /// </summary> /// <param name="targetMergeElement">References the element to be dropped on.</param> /// <param name="diagramDragEventArgs">References the element to be dropped.</param> /// <returns></returns> public bool CanDragDrop(ShapeElement targetMergeElement, DiagramDragEventArgs diagramDragEventArgs) { // CHANGE THIS CODE FOR YOUR APPLICATION. // Get the target element, if any. Null if the target is the diagram. IShape targetIShape = targetMergeElement.CreateIShape(); // This example allows drag of any UML elements. return GetModelElementsFromDragEvent(diagramDragEventArgs).Count() > 0; } /// <summary> /// Execute the action to be performed on the drop. /// </summary> /// <param name="targetDropElement"></param> /// <param name="diagramDragEventArgs"></param> public void OnDragDrop(ShapeElement targetDropElement, DiagramDragEventArgs diagramDragEventArgs) { // CHANGE THIS CODE FOR YOUR APPLICATION. } /// <summary> /// Retrieves UML IElements from drag arguments. /// Works for drags from UML diagrams. /// </summary> private IEnumerable<IElement> GetModelElementsFromDragEvent (DiagramDragEventArgs dragEvent) { //ElementGroupPrototype is the container for //dragged and copied elements and toolbox items. ElementGroupPrototype prototype = dragEvent.Data. GetData(typeof(ElementGroupPrototype)) as ElementGroupPrototype; // Locate the originals in the implementation store. IElementDirectory implementationDirectory = dragEvent.DiagramClientView.Diagram.Store.ElementDirectory; return prototype.ProtoElements.Select( prototypeElement => { ModelElement element = implementationDirectory .FindElement(prototypeElement.ElementId); ShapeElement shapeElement = element as ShapeElement; if (shapeElement != null) { // Dragged from a diagram. return shapeElement.ModelElement as IElement; } else { // Dragged from UML Model Explorer. return element as IElement; } }); } } }
有关要在方法中放置的内容的更多信息,请参见实现笔势处理程序。
您必须将菜单命令添加到一个充当命令安装容器的 VSIX 项目中。如果需要,可以将其他组件包括在同一 VSIX 中。
将单独的笔势处理程序添加到 VSIX 项目
如果您在自己的 VSIX 中,创建了笔势处理程序不需要此过程。
创建一个 VSIX 项目(如果解决方案中已有一个 VSIX 项目,则无需执行此步骤)。
在 解决方案资源管理器,解决方案中的快捷菜单上,选择 添加,新建项目。
在 已安装的模板下,展开 Visual C# 或 Visual Basic,然后选择 扩展性。在中间列中,选择 VSIX 项目。
将 VSIX 项目设置为解决方案的启动项目。
- 在解决方案资源管理器中,在 VSIX 项目的快捷菜单上,选择 设为启动项目。
在 source.extension.vsixmanifest,添加笔势处理程序选件类库项目添加为 MEF 组件:
在 元数据 选项卡上,将一个名称 VSIX 中。
在 安装目标 选项卡上,将 Visual Studio 最终和高质量作为目标。
在 资产 选项卡中,选择 新建,然后在对话框中,设置:
类型 = MEF 组件
源 = 当前解决方案中的项目
项目 = 您的选件类库项目
执行笔势处理程序
仅为测试目的在调试模式下执行笔势处理程序。
测试笔势处理程序
按 F5,或在**“调试”菜单上单击“开始调试”**。
这将启动 Visual Studio 的实验实例。
疑难解答:如果新的 Visual Studio 未启动:
如果您有多个项目,请确保将 VSIX 项目设置为解决方案的启动项目。
在解决方案资源管理器中,该启动或项目的快捷菜单仅,选择属性。在项目属性编辑器中,选择 调试 选项。确保**“启动外部程序”**字段中的字符串是 Visual Studio 的完整路径名,通常为:
C:\Program Files\Microsoft Visual Studio 11.0\Common7\IDE\devenv.exe
在实验性 Visual Studio 中,打开或创建一个建模项目,然后打开或创建一个建模图。使用属于笔势处理程序类的特性中列出的某个类型的关系图。
在关系图上的任意位置双击。应调用您的双击处理程序。
将一个元素从 UML 资源管理器拖动到该关系图上。应调用您的拖动处理程序。
疑难解答:如果笔势处理程序未运行,请确保:
笔势处理程序项目列表为 资产 选项的一个 MEF 组件在 VSIX 项目的 source.extensions.manifest。
所有 Import 和 Export 特性的参数都有效。
CanDragDrop 方法未返回 false。
正在使用的模型关系图的类型(UML 类、序列等)作为笔势处理程序类特性(如 [ClassDesignerExtension]、[SequenceDesignerExtension] 等)之一列出。
尚未为此类型的目标和放置的元素定义内置功能。
实现笔势处理程序
笔势处理程序方法
笔势处理程序类实现并导出 IGestureExtension。以下是您需要定义的方法:
bool CanDragDrop (ShapeElement target, DiagramDragEventArgs dragEvent) |
返回 true 将允许在此目标上放置在 dragEvent 中引用的源元素。 此方法不应更改模型。由于此方法是用于在用户移动鼠标时确定箭头状态,因此它应快速工作。 |
void OnDragDrop (ShapeElement target, DiagramDragEventArgs dragEvent) |
基于目标和在 dragEvent 中引用的源对象来更新模型。 当用户在拖动后释放鼠标按键时调用。 |
void OnDoubleClick (ShapeElement target, DiagramPointEventArgs pointEvent) |
target 是用户双击的形状。 |
您可以编写可接受 UML 以及各种其他项(如文件、.NET 类视图中的节点、体系结构资源管理器节点等)的处理程序。用户可将这些项中的任意项拖至 UML 关系图,前提是编写了可对这些项的序列化格式进行解码的 OnDragDrop 方法。解码方法因项类型而异。
这些方法的参数为:
ShapeElement target.用户已将某项拖至其上的形状或关系图。
ShapeElement 是作为 UML 建模工具的基础的实现中的一个类。为降低使 UML 模型和关系图处于不一致状态的风险,建议您不要直接使用此类的方法。而是在 IShape 中包装元素,然后使用如何:在关系图上显示模型中所述的方法。
获取 IShape:
IShape targetIShape = target.CreateIShape(target);
获取拖动或双击操作的目标模型元素:
IElement target = targetIShape.Element;
可将此元素强制转换为一个更明确的元素类型。
获取包含 UML 模型的 UML 模型存储区:
IModelStore modelStore = targetIShape.Element.GetModelStore();
获取对宿主和服务提供程序的访问权限:
target.Store.GetService(typeof(EnvDTE.DTE)) as EnvDTE.DTE
DiagramDragEventArgs eventArgs.此参数传送拖动操作的序列化格式的源对象:
System.Windows.Forms.IDataObject data = eventArgs.Data;
可以从 Visual Studio 的不同部分或从 Windows 桌面将许多不同类型的元素拖动到关系图上。在 IDataObject 中按照不同的方式对不同类型的元素进行编码。若要从其中提取元素,请参考适当类型对象的文档。
如果您的源对象是从 UML 模型资源管理器或其他 UML 关系图中拖动的 UML 元素,请参考如何:从 IDataObject 获取 UML 模型元素。
编写方法的代码
有关编写代码以读取和更新模型的更多信息,请参见使用 UML API 编程。
有关在拖动操作中访问模型信息的信息,请参见如何:从 IDataObject 获取 UML 模型元素。
如果您处理的是序列图,请参见如何:使用 UML API 编辑序列图。
除了方法参数外,您还可以在提供对当前关系图和模型访问的类中声明一个导入属性。
[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 (IDiagram diagram in modelStore.Diagrams) {...}
foreach (IElement element in modelStore.AllInstances<IUseCase>) {...}
有关更多信息,请参见如何:导航 UML 模型。
安装和卸载扩展
既可以在您自己的计算机上也可以在其他计算机上安装 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
示例
下面的示例演示如何在序列图中基于从组件图中拖动的组件的部件和端口来创建生命线。
若要对其进行测试,请按 F5。这将打开一个 Visual Studio 实验实例。在此实例中,打开 UML 模型并在组件图上创建一个组件。向该组件添加一些接口和内部组件部件。选择接口和部件。然后,将接口和部件拖动到序列图上。(从组件图向上拖动到序列图的选项卡,然后向下拖动到序列图中。)将为每个接口和部件显示一条生命线。
有关将交互绑定到序列图的更多信息,请参见如何:使用 UML API 编辑序列图。
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;
using Microsoft.VisualStudio.Modeling.Diagrams.ExtensionEnablement;
using Microsoft.VisualStudio.Modeling.ExtensionEnablement;
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml;
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Presentation;
using Microsoft.VisualStudio.Uml.AuxiliaryConstructs;
using Microsoft.VisualStudio.Uml.Classes;
using Microsoft.VisualStudio.Uml.Interactions;
using Microsoft.VisualStudio.Uml.CompositeStructures;
using Microsoft.VisualStudio.Uml.Components;
/// <summary>
/// Creates lifelines from component ports and parts.
/// </summary>
[Export(typeof(IGestureExtension))]
[SequenceDesignerExtension]
public class CreateLifelinesFromComponentParts : IGestureExtension
{
[Import]
public IDiagramContext Context { get; set; }
/// <summary>
/// Called by the modeling framework when
/// the user drops something on a target.
/// </summary>
/// <param name="target">The target shape or diagram </param>
/// <param name="dragEvent">The item being dragged</param>
public void OnDragDrop(ShapeElement target,
DiagramDragEventArgs dragEvent)
{
ISequenceDiagram diagram = Context.CurrentDiagram
as ISequenceDiagram;
IInteraction interaction = diagram.Interaction;
if (interaction == null)
{
// Sequence diagram is empty: create an interaction.
interaction = diagram.ModelStore.Root.CreateInteraction();
interaction.Name = Context.CurrentDiagram.Name;
diagram.Bind(interaction);
}
foreach (IConnectableElement connectable in
GetConnectablesFromDrag(dragEvent))
{
ILifeline lifeline = interaction.CreateLifeline();
lifeline.Represents = connectable;
lifeline.Name = connectable.Name;
}
}
/// <summary>
/// Called by the modeling framework to determine whether
/// the user can drop something on a target.
/// Must not change anything.
/// </summary>
/// <param name="target">The target shape or diagram</param>
/// <param name="dragEvent">The item being dragged</param>
/// <returns>true if this item can be dropped on this target</returns>
public bool CanDragDrop(ShapeElement target,
DiagramDragEventArgs dragEvent)
{
IEnumerable<IConnectableElement> connectables = GetConnectablesFromDrag(dragEvent);
return connectables.Count() > 0;
}
///<summary>
/// Get dragged parts and ports of an IComponent.
///</summary>
private IEnumerable<IConnectableElement>
GetConnectablesFromDrag(DiagramDragEventArgs dragEvent)
{
foreach (IElement element in
GetModelElementsFromDragEvent(dragEvent))
{
IConnectableElement part = element as IConnectableElement;
if (part != null)
{
yield return part;
}
}
}
/// <summary>
/// Retrieves UML IElements from drag arguments.
/// Works for drags from UML diagrams.
/// </summary>
private IEnumerable<IElement> GetModelElementsFromDragEvent
(DiagramDragEventArgs dragEvent)
{
//ElementGroupPrototype is the container for
//dragged and copied elements and toolbox items.
ElementGroupPrototype prototype =
dragEvent.Data.
GetData(typeof(ElementGroupPrototype))
as ElementGroupPrototype;
// Locate the originals in the implementation store.
IElementDirectory implementationDirectory =
dragEvent.DiagramClientView.Diagram.Store.ElementDirectory;
return prototype.ProtoElements.Select(
prototypeElement =>
{
ModelElement element = implementationDirectory
.FindElement(prototypeElement.ElementId);
ShapeElement shapeElement = element as ShapeElement;
if (shapeElement != null)
{
// Dragged from a diagram.
return shapeElement.ModelElement as IElement;
}
else
{
// Dragged from UML Model Explorer.
return element as IElement;
}
});
}
public void OnDoubleClick(ShapeElement targetElement, DiagramPointEventArgs diagramPointEventArgs)
{
}
}
如何:从 IDataObject 获取 UML 模型元素中介绍了 GetModelElementsFromDragEvent() 的代码。