自訂複製行為
定義域專屬語言 (DSL) 以建立Visual Studio視覺化和模型的 SDK,您可以變更使用者複製並貼上項目的時,會發生什麼事。
一般的複製和貼上行為
若要啟用複製,將啟用複製貼上 屬性的 編輯器 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()之後呼叫基底方法建立額外的連結。
自訂的格式複製項目可以給外部應用程式 – 例如,若要新增框線到點陣圖格式。
覆寫 MyDslClipboardCommandSet.ProcessOnMenuCopyCommand() DslPackage 專案中。自訂如何將項目複製到 [剪貼簿] 的 [複製] 命令,但不是會在拖曳作業。
覆寫 MyDslClipboardCommandSet.CopyModelElementsIntoElementGroupPrototype() DslPackage 專案中。保持圖形格式,透過複製並貼上。
當使用者複製多個圖形時,您可以在貼後就會保留其相對位置。在這個範例示範這項技術VMSDK: 電路圖表範例。若要達到此效果,加入圖案及連接線複製的 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: 電路圖表範例。若要達到此效果,覆寫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(); } } } } }
可讓使用者將拖放項目。
請參閱 HOW TO:加入拖放處理常式。
自訂連結的複製行為
當使用者複製項目時,標準行為是由任何內嵌的項目也會複製。您可以修改標準的複製行為。在 DSL 定義中,選取角色,以最低的關聯性,並設定屬性] 視窗中的一方傳播複本的值。
有三個值:
並不會傳播複本
傳播複製只-連結到現有的項目連結的另一端當貼上的群組時,會參考此連結的新複本。
傳播到連結的複本,相反角色扮演者-已複製的群組會包含連結的另一端項目的複本。
項目,且會被複製的影像,將會影響您所做的變更。
程式設計複製和貼上行為
DSL 的行為,複製、 貼上、 建立和刪除的物件與相關的許多方面都受到的執行個體ElementOperations ,結合至圖表。您可以修改您的 DSL 行為,藉由衍生您自己的類別,從ElementOperations和覆寫ElementOperations圖表類別的屬性。
提示 |
---|
如需有關如何使用程式碼來自訂此模型的詳細資訊,請參閱巡覽及更新程式碼中的模型。 |
若要定義您自己的 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 的所有恐怖類,此處提供的範例會定義自訂的拖放行為。不過,為達到此目的您可能要考慮在所說明的替代方法HOW TO:加入拖放處理常式,這是更具擴充性。
ElementOperations 類別中定義兩種方法:
CanMerge(ModelElement targetElement, System.Windows.Forms.IDataObject data)決定是否來源項目可以將它們拖曳至目標、 連接器的圖形圖表。
MergeElementGroupPrototype(ModelElement targetElement, ElementGroupPrototype sourcePrototype)這會將來源項目結合成目標中。
CanMerge()
[CanMerge()]呼叫來決定當滑鼠移過圖表應該提供給使用者的意見反應。方法的參數是控制滑鼠指標停留,項目,並從中執行拖曳作業的來源的相關資料。使用者可以從任何位置上拖曳到螢幕。因此,來源物件可以有許多不同的型別,而且可以在不同的格式進行序列化。資料參數 DSL 或 UML 模型的來源時,已序列化的ElementGroupPrototype。拖曳、 複製及工具箱作業會使用 ElementGroupPrototypes 來表示模型的片段。
項目群組原型可以包含任何數目的項目和連結。可由其 Guid 識別項目型別。GUID 是拖曳時,不對應的模型項目] 圖形。在下列範例中, CanMerge() ,則傳回 true,如果 UML 圖上的 [類別] 圖形拖曳到這個圖表。
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 類別,但是相反地,我們建立一個 DSL 元件的每個拖曳 UML 類別。這或許有用,例如,如果 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。如需有關命令所設定的詳細資訊,請參閱HOW TO:在捷徑功能表中加入命令。
您可以藉由新增的部分類別定義覆寫 ProcessOnMenuCopyCommand MyDslClipboardCommandSet 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;
}
}
}
請參閱
概念
其他資源
範例: VMSDK 電路圖表範例