共用方式為


自訂複製行為

定義域專屬語言 (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 定義中,選取角色,以最低的關聯性,並設定屬性] 視窗中的一方傳播複本的值。

網域角色的 Propagates Copy 屬性

有三個值:

  • 並不會傳播複本

  • 傳播複製只-連結到現有的項目連結的另一端當貼上的群組時,會參考此連結的新複本。

  • 傳播到連結的複本,相反角色扮演者-已複製的群組會包含連結的另一端項目的複本。

使用 PropagateCopyToLinkOnly 複製的效用

項目,且會被複製的影像,將會影響您所做的變更。

程式設計複製和貼上行為

DSL 的行為,複製、 貼上、 建立和刪除的物件與相關的許多方面都受到的執行個體ElementOperations ,結合至圖表。 您可以修改您的 DSL 行為,藉由衍生您自己的類別,從ElementOperations和覆寫ElementOperations圖表類別的屬性。

提示

如需有關如何使用程式碼來自訂此模型的詳細資訊,請參閱巡覽及更新程式碼中的模型

複製作業的順序圖表貼上作業的順序圖表

若要定義您自己的 ElementOperations

  1. 在您的 DSL 專案中新的檔案,建立類別衍生自DesignSurfaceElementOperations

  2. 加入部分類別定義為您圖表的類別。 這個類別的名稱位於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)這會將來源項目結合成目標中。

Ff521398.collapse_all(zh-tw,VS.110).gifCanMerge()

[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;
    }
  }
}

請參閱

概念

自訂項目的建立和移動

HOW TO:加入拖放處理常式

自訂刪除行為

其他資源

範例: VMSDK 電路圖表範例