Sdílet prostřednictvím


Přizpůsobení chování kopírování

V jazyce specifickém pro doménu (DSL) vytvořeném pomocí sady Visual Studio Visualization and Modeling SDK můžete změnit, co se stane, když uživatel zkopíruje a vloží prvky.

Standardní chování kopírování a vložení

Chcete-li povolit kopírování, nastavte vlastnost Povolit kopírování vložit uzlu Editor v Průzkumníku DSL.

Když uživatel ve výchozím nastavení zkopíruje prvky do schránky, zkopírují se také následující prvky:

  • Vložené potomky vybraných prvků (To znamená, že prvky, které jsou cílem relací vkládání, které jsou zdrojové u zkopírovaných elementů.)

  • Vazby mezi zkopírovanými prvky.

    Toto pravidlo se rekurzivně vztahuje na zkopírované prvky a odkazy.

    Copied and pasted elements

    Zkopírované prvky a odkazy jsou serializovány a uloženy v ElementGroupPrototype protokolu (EGP), který je umístěn ve schránce.

    Obrázek zkopírovaných prvků je také umístěn do schránky. To uživateli umožní vložit do jiných aplikací, jako je Word.

    Uživatel může vložit zkopírované prvky do cíle, které mohou přijímat prvky podle definice DSL. Například v DSL generované ze šablony řešení komponent může uživatel vložit porty do komponent, ale ne do diagramu; a může vložit součásti do diagramu, ale ne do jiných součástí.

Přizpůsobení chování kopírování a vložení

Další informace o přizpůsobení modelu pomocí kódu programu naleznete v tématu Navigace a aktualizace modelu v kódu programu.

Povolí nebo zakáže kopírování, vyjmutí a vložení. V DSL Exploreru nastavte vlastnost Povolit kopírování vložit uzlu Editor.

Zkopírujte odkazy na stejný cíl. Pokud chcete mít například zkopírované pole komentáře propojené se stejným prvkem předmětu. Nastavte vlastnost Šíření kopírování role na rozšířit kopírování pouze na propojení. Další informace naleznete v tématu Přizpůsobení chování kopírování odkazu.

Zkopírujte propojené prvky. Když například zkopírujete nový prvek, vytvoří se také kopie všech propojených polí komentářů. Nastavte vlastnost Šíření kopírování role rozšířit kopii pro propojení a opačný přehrávač rolí. Další informace naleznete v tématu Přizpůsobení chování kopírování odkazu.

Rychlé duplikování prvků zkopírováním a vložením Za normálních okolností je položka, kterou jste právě zkopírovali, stále vybraná a do ní nelze vložit stejný typ prvku. Přidejte do třídy domény direktivu Element Merge a nastavte ji tak, aby přeposílala sloučení do nadřazené třídy. To bude mít stejný vliv na operace přetažení. Další informace naleznete v tématu Přizpůsobení vytváření a přesouvání elementů.

- nebo -

Vyberte diagram před vložením prvků přepsáním ClipboardCommandSet.ProcessOnPasteCommand(). Přidejte tento kód do vlastního souboru v projektu 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);
  }
} }

Vytvořit další odkazy, když uživatel vloží do vybraného cíle. Když je například pole komentáře vloženo do elementu, vytvoří se mezi nimi propojení. Přidejte do cílové třídy domény direktivu Sloučení elementů a nastavte ji pro zpracování sloučení přidáním odkazů. To bude mít stejný vliv na operace přetažení. Další informace naleznete v tématu Přizpůsobení vytváření a přesouvání elementů.

- nebo -

Přepsání ClipboardCommandSet.ProcessOnPasteCommand() pro vytvoření dalších odkazů po volání základní metody.

Přizpůsobte formáty, ve kterých lze prvky zkopírovat do externích aplikací – například pro přidání ohraničení do rastrového formuláře. Přepsat MyDslClipboardCommandSet.ProcessOnMenuCopyCommand() v projektu DslPackage.

Přizpůsobte, jak se prvky zkopírují do schránky příkazem kopírování, ale ne v operaci přetažení. Přepsat MyDslClipboardCommandSet.CopyModelElementsIntoElementGroupPrototype() v projektu DslPackage.

Zachovat rozložení obrazce pomocí kopírování a vložení Když uživatel kopíruje více obrazců, můžete při vkládání zachovat jejich relativní pozice. Tento postup ukazuje příklad v ukázce VMSDK: Diagramy okruhů.

Chcete-li dosáhnout tohoto efektu, přidejte obrazce a spojnice do zkopírovaného ElementGroupPrototype. Nejpohodlnější metodou přepsání je ElementOperations.CreateElementGroupPrototype(). Uděláte to tak, že do projektu Dsl přidáte následující kód:


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

Vložte obrazce do zvoleného umístění, například aktuální pozici kurzoru. Když uživatel kopíruje více obrazců, můžete při vkládání zachovat jejich relativní pozice. Tento postup ukazuje příklad v ukázce VMSDK: Diagramy okruhů.

Chcete-li dosáhnout tohoto efektu, přepsání ClipboardCommandSet.ProcessOnMenuPasteCommand() pro použití verze specifické pro ElementOperations.Merge()umístění . Uděláte to tak, že do projektu DslPackage přidáte následující kód:


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();
          }
        }
      }
    }
  }

Nechte uživatele přetáhnout prvky. Viz Postupy: Přidání obslužné rutiny přetažení myší.

Když uživatel zkopíruje prvek, standardní chování je, že všechny vložené elementy jsou také zkopírovány. Můžete upravit standardní chování kopírování. V definici DSL vyberte roli na jedné straně relace a v okno Vlastnosti nastavte hodnotu Šíření kopírování.

Propagates Copy property of domain role

Existují tři hodnoty:

  • Nešírovat kopii

  • Rozšířit kopii pouze na propojení – pokud je skupina vložena, nová kopie tohoto odkazu bude odkazovat na existující prvek na druhém konci odkazu.

  • Šíření kopie pro propojení a opačného hráče rolí – zkopírovaná skupina obsahuje kopii prvku na druhém konci odkazu.

    Effect of copying with PropagateCopyToLinkOnly

    Provedené změny ovlivní prvky i obrázek, který se zkopíruje.

Programovací chování kopírování a vložení

Mnoho aspektů chování DSL s ohledem na kopírování, vkládání, vytváření a odstraňování objektů se řídí instancí ElementOperations , která je svázána s diagramem. Chování DSL můžete upravit odvozením vlastní třídy a ElementOperations přepsáním ElementOperations vlastnosti třídy diagramu.

Tip

Další informace o přizpůsobení modelu pomocí kódu programu naleznete v tématu Navigace a aktualizace modelu v kódu programu.

Sequence diagram for the Copy operation

Sequence diagram of Paste operation

Definování vlastních ElementOperations

  1. V novém souboru v projektu DSL vytvořte třídu, která je odvozena z DesignSurfaceElementOperations.

  2. Přidejte pro třídu diagramu částečnou definici třídy. Název této třídy najdete v dsl\GeneratedCode\Diagrams.cs.

    Ve třídě diagramu přepište ElementOperations , aby se vrátila instance podtřídy ElementOperations. Při každém volání byste měli vrátit stejnou instanci.

    Přidejte tento kód do vlastního souboru kódu v projektu 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
  }

Příjem položek přetažených z jiných modelů

ElementOperations lze také použít k definování chování kopírování, přesouvání, odstraňování a přetažení. Jako ukázku použití ElementOperations zde uvedený příklad definuje vlastní chování přetažení myší. Pro tento účel však můžete zvážit alternativní přístup popsaný v tématu Postupy: Přidání obslužné rutiny přetažení, která je rozšiřitelnější.

Definujte dvě metody ve třídě ElementOperations:

  • CanMerge(ModelElement targetElement, System.Windows.Forms.IDataObject data) určuje, zda lze zdrojový prvek přetáhnout na cílový obrazec, spojnici nebo diagram.

  • MergeElementGroupPrototype(ModelElement targetElement, ElementGroupPrototype sourcePrototype) který kombinuje zdrojový prvek do cíle.

CanMerge()

CanMerge() je volána k určení zpětné vazby, kterou by měl uživatel dát při pohybu myší přes diagram. Parametry metody jsou element, na který myš najede myší, a data o zdroji, ze kterého byla provedena operace přetažení. Uživatel může přetáhnout z libovolného místa na obrazovce. Zdrojový objekt proto může mít mnoho různých typů a lze ho serializovat v různých formátech. Pokud je zdrojem MODEL DSL nebo UML, datový parametr je serializace objektu ElementGroupPrototype. Operace přetažení, kopírování a sady nástrojů používají ElementGroupPrototypes k reprezentaci fragmentů modelů.

Prototyp skupiny elementů může obsahovat libovolný počet prvků a odkazů. Typy prvků lze identifikovat pomocí jejich identifikátorů GUID. Identifikátor GUID je obrazce, který byl přetažen, nikoli element základního modelu. V následujícím příkladu vrátí hodnotu true, CanMerge() pokud je do tohoto diagramu přetažen obrazec třídy z diagramu 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()

Tato metoda se volá, když uživatel zahodí prvek do diagramu, obrazce nebo spojnice. Měl by sloučit přetažený obsah do cílového prvku. V tomto příkladu kód určuje, zda rozpozná kombinaci cílových a prototypových typů; Pokud ano, metoda převede přetažené elementy do prototypu prvků, které by měly být přidány do modelu. Základní metoda je volána k provedení sloučení, buď z převedených nebo nekonvertovaných prvků.

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

Tento příklad se zabývá prvky třídy UML přetažené z diagramu tříd UML. DSL není určen k přímému ukládání tříd UML, ale místo toho vytvoříme prvek DSL pro každou přetaženou třídu UML. To by bylo užitečné například v případě, že DSL je diagram instance. Uživatel může přetáhnout třídy do diagramu a vytvořit instance těchto tříd.


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

Standardní chování kopírování

Kód v této části ukazuje metody, které můžete přepsat a změnit chování kopírování. Tato část vám pomůže zjistit, jak dosáhnout vlastních přizpůsobení, ukazuje kód, který přepíše metody zahrnuté do kopírování, ale nemění standardní chování.

Když uživatel stiskne kombinaci kláves CTRL+C nebo použije příkaz nabídky Kopírovat, volá se metoda ProcessOnMenuCopyCommand . Uvidíte, jak je toto nastavení nastaveno v DslPackage\Generated Code\CommandSet.cs. Další informace o tom, jak jsou příkazy nastaveny, naleznete v tématu Postupy: Přidání příkazu do místní nabídky.

ProcessOnMenuCopyCommand můžete přepsat přidáním částečné definice třídy MyDslClipboardCommandSet v projektu 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);
}
}

Každý diagram má jednu instanci ElementOperations. Můžete zadat vlastní derivát. Tento soubor, který lze umístit do projektu DSL, se chová stejně jako kód, který přepíše:

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

Poznámka:

Komponenta Transformace textové šablony se automaticky nainstaluje jako součást sady funkcí vývoje rozšíření sady Visual Studio. Můžete ho také nainstalovat z karty Jednotlivé komponenty Instalační program pro Visual Studio v kategorii sad SDK, knihoven a architektur. Nainstalujte komponentu Modeling SDK z karty Jednotlivé komponenty .