自定义复制行为
在创建 Visual Studio 可视化和建模 SDK 的域特定语言 (DSL) (dsl),可以修改会发生什么,当用户复制和粘贴元素。
标准复制和粘贴行为
若要启用副本,请设置 编辑 节点的 启用副本粘贴 属性在 DSL 资源管理器中。
默认情况下,那么,当用户元素复制到剪贴板时,下面的元素也会复制:
所选元素的所有子代。(即嵌入关系的目标是源将复制的组件。) 的元素
复制的组件之间的关系链接。
此递归规则适用于复制的组件和链接。
复制的组件和链接。 ElementGroupPrototype (EGP) 序列化并存储,随即被放到剪贴板上。
复制的组件的图像在剪贴板还放置。这允许用户粘贴到其他应用程序 (如 Word。
用户可以粘贴到可以根据 DSL 定义接受元素的目标上的复制的组件。例如,从该元素解决方案模板生成的 DSL,用户可以粘贴端口在元素,该元素,但不在关系图上;可以将元素粘贴在关系图上,,而在其他元素上。
自定义复制和粘贴行为
有关自定义使用程序代码模型的更多信息,请参见 在程序代码中导航和更新模型。
启用或禁用复制, " 剪切 ",并粘贴。
在 DSL 资源管理器中,设置 编辑 节点的 启用副本粘贴 属性。**复制指向同一目标。**例如,具有相同主题元素所链接到的一个复制的注释框。
将角色的 传播复制 属性设置为 仅链接的传播复制。有关更多信息,请参见 自定义链接复制行为。复制链接的元素。例如,那么,当您将一个新元素时,所有链接的注释框的副本。
将角色的 传播复制 属性设置为 对链接和对方角色的扮演者的传播复制。有关更多信息,请参见 自定义链接复制行为。**请通过复制和粘贴快速地重复元素。**通常,您要复制的项仍处于选中状态,因此,您不能粘贴上来相同的元素。
添加一个元素合并指令添加到域类,并将其设置为向前合并到父类。这将对拖动操作的同一角色。有关更多信息,请参见 自定义元素创建和移动。- 或 -
在将元素粘贴选择关系图,通过重写的 ClipboardCommandSet.ProcessOnPasteCommand()。将自定义文件的此代码在 DslPackage 项目:
namespace Company.MyDsl { using System.Linq; using Microsoft.VisualStudio.Modeling.Diagrams; using Microsoft.VisualStudio.Modeling.Shell; partial class MyDslClipboardCommandSet { protected override void ProcessOnMenuPasteCommand() { // Deselect the current selection after copying: Diagram diagram = (this.CurrentModelingDocView as SingleDiagramDocView).Diagram; this.CurrentModelingDocView .SelectObjects(1, new object[] { diagram }, 0); } } }
**创建额外的链接,当一个选定的目标上的用户粘贴。**例如,那么,当注释框中粘贴在元素上时,链接创建在它们之间。
添加一个元素合并指令到目标域类,并将其设置为通过添加链接处理为合并。这将对拖动操作的同一角色。有关更多信息,请参见 自定义元素创建和移动。- 或 -
重写 ClipboardCommandSet.ProcessOnPasteCommand() 在调用基方法之后创建其他的链接。
自定义组件可以复制 到外部应用程序 ),添加边框更改为位图窗体的布局 例如。
重写在 DslPackage 项目的 MyDslClipboardCommandSet.ProcessOnMenuCopyCommand() 。自定义组件如何复制到剪贴板由复制命令,但是,不在拖动操作。
重写在 DslPackage 项目的 MyDslClipboardCommandSet.CopyModelElementsIntoElementGroupPrototype() 。保留形状格式进行复制和粘贴。
当用户复制多个形状时,可以保留它们的相对位置,并粘贴时。此方法由示例演示。 VMSDK: Circuit Diagrams sample若要获得效果,请添加形状并连接到复制的 ElementGroupPrototype。重写的最简便的方法是 ElementOperations.CreateElementGroupPrototype()。为此,请将以下代码添加到 DSL 项目:
public class MyElementOperations : DesignSurfaceElementOperations { // Create an EGP to add to the clipboard. // Called when the elements to be copied have been // collected into an ElementGroup. protected override ElementGroupPrototype CreateElementGroupPrototype(ElementGroup elementGroup, ICollection<ModelElement> elements, ClosureType closureType) { // Add the shapes and connectors: // Get the elements already in the group: ModelElement[] mels = elementGroup.ModelElements .Concat(elementGroup.ElementLinks) // Omit if the paste target is not the diagram. .ToArray(); // Get their shapes: IEnumerable<PresentationElement> shapes = mels.SelectMany(mel => PresentationViewsSubject.GetPresentation(mel)); elementGroup.AddRange(shapes); return base.CreateElementGroupPrototype (elementGroup, elements, closureType); } public MyElementOperations(IServiceProvider serviceProvider, ElementOps1Diagram diagram) : base(serviceProvider, diagram) { } } // Replace the standard ElementOperations // singleton with your own: partial class MyDslDiagram // EDIT NAME { /// <summary> /// Singleton ElementOperations attached to this diagram. /// </summary> public override DesignSurfaceElementOperations ElementOperations { get { if (singleton == null) { singleton = new MyElementOperations(this.Store as IServiceProvider, this); } return singleton; } } private MyElementOperations singleton = null; }
在所选位置粘贴形状,如当前光标位置。
当用户复制多个形状时,可以保留它们的相对位置,并粘贴时。此方法由示例演示。 VMSDK: Circuit Diagrams sample若要获得效果,请重写 ClipboardCommandSet.ProcessOnMenuPasteCommand() 使用 ElementOperations.Merge()的位置特定版本。为此,请将 DslPackage 项目中的以下代码:
partial class MyDslClipboardCommandSet // EDIT NAME { /// <summary> /// This method assumes we only want to paste things onto the diagram /// - not onto anything contained in the diagram. /// The base method pastes in a free space on the diagram. /// But if the menu was used to invoke paste, we want to paste in the cursor position. /// </summary> protected override void ProcessOnMenuPasteCommand() { NestedShapesSampleDocView docView = this.CurrentModelingDocView as NestedShapesSampleDocView; // Retrieve data from clipboard: System.Windows.Forms.IDataObject data = System.Windows.Forms.Clipboard.GetDataObject(); Diagram diagram = docView.CurrentDiagram; if (diagram == null) return; if (!docView.IsContextMenuShowing) { // User hit CTRL+V - just use base method. // Deselect anything that's selected, otherwise // pasted item will be incompatible: if (!this.IsDiagramSelected()) { docView.SelectObjects(1, new object[] { diagram }, 0); } // Paste into a convenient spare space on diagram: base.ProcessOnMenuPasteCommand(); } else { // User right-clicked - paste at mouse position. // Utility class: DesignSurfaceElementOperations op = diagram.ElementOperations; ShapeElement pasteTarget = diagram; // Check whether what's in the paste buffer is acceptable on the target. if (pasteTarget != null && op.CanMerge(pasteTarget, data)) { // Although op.Merge would be a no-op if CanMerge failed, we check CanMerge first // so that we don't create an empty transaction (after which Undo would be no-op). using (Transaction t = diagram.Store.TransactionManager.BeginTransaction("paste")) { PointD place = docView.ContextMenuMousePosition; op.Merge(pasteTarget, data, PointD.ToPointF(place)); t.Commit(); } } } } }
让用户拖放元素。
请参见 如何:添加拖放处理程序。
自定义链接复制行为
当用户将一个组件时,标准行为是所有嵌入元素也被复制。可以修改标准复制的行为。在 DSL 定义,选择一个角色关系中的一端,并在 " 属性 " 窗口中设置 传播复制 值。
有三个值:
不要将复制
将传播仅复制链接 ),以便组粘贴时,此链接的新副本将引用为现有元素在链接的另一端。
传播复制链接和对方角色的扮演者 - 复制的组包含该元素的副本在链接的另一端。
您所做的更改将影响元素和复制的图像。
编程复制和粘贴行为
DSL 的行为的许多方面有关复制、粘贴、对象的创建和删除的是已与关系图 ElementOperations 的实例管理。您可以通过派生拥有类从 ElementOperations 并重写关系图类的 ElementOperations 属性修改 DSL 的行为。
提示 |
---|
有关自定义使用程序代码模型的更多信息,请参见 在程序代码中导航和更新模型。 |
定义拥有 ElementOperations
在 DSL 项目的新文件,创建从 DesignSurfaceElementOperations派生的类。
添加关系图类的分部类定义。此类的名称可以在 Dsl\GeneratedCode\Diagrams.cs找到。
在关系图类,请重写 ElementOperations 返回 ElementOperations 子类的实例。您应返回同一个实例在每次调用。
将自定义代码文件的此代码在 DslPackage 项目:
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;
using Microsoft.VisualStudio.Modeling.Diagrams.ExtensionEnablement;
public partial class MyDslDiagram
{
public override DesignSurfaceElementOperations ElementOperations
{
get
{
if (this.elementOperations == null)
{
this.elementOperations = new MyElementOperations(this.Store as IServiceProvider, this);
}
return this.elementOperations;
}
}
private MyElementOperations elementOperations = null;
}
public class MyElementOperations : DesignSurfaceElementOperations
{
public MyElementOperations(IServiceProvider serviceProvider, MyDslDiagram diagram)
: base(serviceProvider, diagram)
{ }
// Overridden methods follow
}
接收来自其他模型拖动项
ElementOperations 还可用于定义复制、移动、删除和拖放行为。作为使用的演示 ElementOperations,给出的示例此处定义自定义拖放行为。但是,在中,为该目的在 如何:添加拖放处理程序考虑替代方法,将介绍更为可扩展的。
定义在 ElementOperations 类的两个方法:
确定 的CanMerge(ModelElement targetElement, System.Windows.Forms.IDataObject data) 源元素是否可以拖动到目标形状、链接或关系图上。
合并 源元素为目标的MergeElementGroupPrototype(ModelElement targetElement, ElementGroupPrototype sourcePrototype) 。
CanMerge()
[CanMerge()] 调用来确定应为用户的反馈,当鼠标位于关系图之间移动。对方法的参数为鼠标悬停的有关拖动操作的源的元素和数据。用户可以从任何位置屏幕拖动。因此,源对象是许多不同类型,并且可以序列化使用不同的布局。如果源是 DSL 或 UML 模型,数据参数是 ElementGroupPrototype的序列化。拖动、复制和工具箱操作使用 ElementGroupPrototypes 表示模型的片段。
元素组原型可以包含任意数量的元素和链接。元素类型可由其 GUID 标识。GUID 拖动的是形状,而不是基础模型元素。在下面的示例中,因此,如果从 UML 关系图的类形状拖动此关系图上, CanMerge() 返回 true。
public override bool CanMerge(ModelElement targetShape, System.Windows.Forms.IDataObject data)
{
// Extract the element prototype from the data.
ElementGroupPrototype prototype = ElementOperations.GetElementGroupPrototype(this.ServiceProvider, data);
if (targetShape is MyTargetShape && prototype != null &&
prototype.RootProtoElements.Any(rootElement =>
rootElement.DomainClassId.ToString()
== "3866d10c-cc4e-438b-b46f-bb24380e1678")) // Guid of UML Class shapes
// or SourceClass.DomainClassId
return true;
return base.CanMerge(targetShape, data);
}
MergeElementGroupPrototype()
,当用户拖放到关系图、形状或连接上时,的元素调用此方法。它应将被拖动的内容添加到目标元素。在此示例中,代码以确定它是否识别目标和原型类型的组合;如果存在,则方法将被拖动的元素转换应添加到设计组件的原型。该基方法调用执行合并,转换的或不重元素。
public override void MergeElementGroupPrototype(ModelElement targetShape, ElementGroupPrototype sourcePrototype)
{
ElementGroupPrototype prototypeToMerge = sourcePrototype;
MyTargetShape pel = targetShape as MyTargetShape;
if (pel != null)
{
prototypeToMerge = ConvertDraggedTypeToLocal(pel, sourcePrototype);
}
if (prototypeToMerge != null)
base.MergeElementGroupPrototype(targetShape, prototypeToMerge);
}
此示例处理 UML 从 UML 类关系图中拖动的类元素。DSL 并非旨在直接存储 UML 类,,相反,我们创建每个拖动 UML 类的一个 DSL 元素。,如果 DSL 是实例关系图,例如,这会很有用。用户可以拖动到关系图上的类来创建这些类的实例。
private ElementGroupPrototype ConvertDraggedTypeToLocal (MyTargetShape snapshot, ElementGroupPrototype prototype)
{
// Find the UML project:
EnvDTE.DTE dte = snapshot.Store.GetService(typeof(EnvDTE.DTE)) as EnvDTE.DTE;
foreach (EnvDTE.Project project in dte.Solution.Projects)
{
IModelingProject modelingProject = project as IModelingProject;
if (modelingProject == null) continue; // not a modeling project
IModelStore store = modelingProject.Store;
if (store == null) continue;
// Look for the shape that was dragged:
foreach (IDiagram umlDiagram in store.Diagrams())
{
// Get modeling diagram that implements UML diagram:
Diagram diagram = umlDiagram.GetObject<Diagram>();
Guid elementId = prototype.SourceRootElementIds.FirstOrDefault();
ShapeElement shape = diagram.Partition.ElementDirectory.FindElement(elementId) as ShapeElement;
if (shape == null) continue;
IClass classElement = shape.ModelElement as IClass;
if (classElement == null) continue;
// Create a prototype of elements in my DSL, based on the UML element:
Instance instance = new Instance(snapshot.Store);
instance.Type = classElement.Name;
// Pack them into a prototype:
ElementGroup group = new ElementGroup(instance);
return group.CreatePrototype();
}
}
return null;
}
标准复制行为
本节中的代码显示为的方法可以重写更改复制的行为。为了帮助您了解如何实现拥有自定义项,本节说明在复制涉及的方法的代码,但是,不更改标准行为。
当用户按 CTRL+C 或使用复制菜单命令时,方法 ProcessOnMenuCopyCommand 调用。可以查看此如何在 DslPackage\Generated Code\CommandSet.cs设置为。有关如何安装的更多信息,请参见 如何:向快捷菜单中添加命令。
可以通过添加 MyDslClipboardCommandSet 的分部类定义将重写 ProcessOnMenuCopyCommand 在 DslPackage 项目的。
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;
partial class MyDslClipboardCommandSet
{
/// <summary>
/// Override ProcessOnMenuCopyCommand() to copy elements to the
/// clipboard in different formats, or to perform additional tasks
/// before or after copying – for example deselect the copied elements.
/// </summary>
protected override void ProcessOnMenuCopyCommand()
{
IList<ModelElement> selectedModelElements = this.SelectedElements;
if (selectedModelElements.Count == 0) return;
// System container for clipboard data.
// The IDataObject can contain data in several formats.
IDataObject dataObject = new DataObject();
Bitmap bitmap = null; // For export to other programs.
try
{
#region Create EGP for copying to a DSL.
this.CopyModelElementsIntoElementGroupPrototype
(dataObject, selectedModelElements);
#endregion
#region Create bitmap for copying to another application.
// Find all the shapes associated with this selection:
List<ShapeElement> shapes = new List<ShapeElement>(
this.ResolveExportedShapesForClipboardImages
(dataObject, selectedModelElements));
bitmap = this.CreateBitmapForClipboard(shapes);
if (bitmap != null)
{
dataObject.SetData(DataFormats.Bitmap, bitmap);
}
#endregion
// Add the data to the clipboard:
Clipboard.SetDataObject(dataObject, true, 5, 100);
}
finally
{
// Dispose bitmap after SetDataObject:
if (bitmap != null) bitmap.Dispose();
}
}
/// <summary>
/// Override this to customize the element group that is copied to the clipboard.
/// </summary>
protected override void CopyModelElementsIntoElementGroupPrototype(IDataObject dataObject, IList<ModelElement> selectedModelElements)
{
return this.ElementOperations.Copy(dataObject, selectedModelElements);
}
}
每个关系图都具有 ElementOperations 一个实例。可以提供拥有的派生对象。此文件,在 DSL 项目可以放在节点中,行为方式与它重写的代码相同:
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;
namespace Company.MyDsl
{
partial class MyDslDiagram
{
/// <summary>
/// Singleton ElementOperations attached to this diagram.
/// </summary>
public override DesignSurfaceElementOperations ElementOperations
{
get
{
if (this.elementOperations == null)
{
this.elementOperations = new MyElementOperations(this.Store as IServiceProvider, this);
}
return this.elementOperations;
}
}
private MyElementOperations elementOperations = null;
}
// Our own version of ElementOperations so that we can override:
public class MyElementOperations : DesignSurfaceElementOperations
{
public MyElementOperations(IServiceProvider serviceProvider, ElementOps1Diagram diagram)
: base(serviceProvider, diagram)
{ }
/// <summary>
/// Copy elements to the clipboard data.
/// Provides a hook for adding custom data.
/// </summary>
public override void Copy(System.Windows.Forms.IDataObject data,
ICollection<ModelElement> elements,
ClosureType closureType,
System.Drawing.PointF sourcePosition)
{
if (CanAddElementGroupFormat(elements, closureType))
{
AddElementGroupFormat(data, elements, closureType);
}
// Override these to store additional data:
if (CanAddCustomFormat(elements, closureType))
{
AddCustomFormat(data, elements, closureType, sourcePosition);
}
}
protected override void AddElementGroupFormat(System.Windows.Forms.IDataObject data, ICollection<ModelElement> elements, ClosureType closureType)
{
// Add the selected elements and those implied by the propagate copy rules:
ElementGroup elementGroup = this.CreateElementGroup(elements, closureType);
// Mark all the elements that are not embedded under other elements:
this.MarkRootElements(elementGroup, elements, closureType);
// Store in the clipboard data:
ElementGroupPrototype elementGroupPrototype = this.CreateElementGroupPrototype(elementGroup, elements, closureType);
data.SetData(ElementGroupPrototype.DefaultDataFormatName, elementGroupPrototype);
}
/// <summary>
/// Override this to store additional elements in the element group:
/// </summary>
protected override ElementGroupPrototype CreateElementGroupPrototype(ElementGroup elementGroup, ICollection<ModelElement> elements, ClosureType closureType)
{
ElementGroupPrototype prototype = new ElementGroupPrototype(this.Partition, elementGroup.RootElements, elementGroup);
return prototype;
}
/// <summary>
/// Create an element group from the given starting elements, using the
/// copy propagation rules specified in the DSL Definition.
/// By default, this includes all the embedded descendants of the starting elements,
/// and also includes reference links where both ends are already included.
/// </summary>
/// <param name="startElements">model elements to copy</param>
/// <param name="closureType"></param>
/// <returns></returns>
protected override ElementGroup CreateElementGroup(ICollection<ModelElement> startElements, ClosureType closureType)
{
// ElementClosureWalker finds all the connected elements,
// according to the propagate copy rules specified in the DSL Definition:
ElementClosureWalker walker = new ElementClosureWalker(this.Partition,
closureType, // Normally ClosureType.CopyClosure
startElements,
true, // Do not load other models.
null, // Optional list of domain roles not to traverse.
true); // Include relationship links where both ends are already included.
walker.Traverse(startElements);
IList<ModelElement> closureList = walker.ClosureList;
Dictionary<object, object> closureContext = walker.Context;
// create a group for this closure
ElementGroup group = new ElementGroup(this.Partition);
group.AddRange(closureList, false);
// create the element group prototype for the group
foreach (object key in closureContext.Keys)
{
group.SourceContext.ContextInfo[key] = closureContext[key];
}
return group;
}
}
}