Condividi tramite


Personalizzazione del comportamento di copia

in un linguaggio specifico di dominio (DSL) creato con Visual Studio L'sdk di visualizzazione e modellazione di, è possibile l'associazione ciò che si verifica quando gli elementi vengono copiate e incolle utente.

Comportamento standard di copia e incolla

Per abilitare la copia, impostare Abilitare incolla di copia proprietà di editor nodo nel modello DSL Esplora Risorse.

Per impostazione predefinita, quando l'utente copia gli elementi negli Appunti, i seguenti elementi vengono copiati:

  • Discendenti incorporati degli elementi selezionati.Ovvero elementi che sono le destinazioni di incorporare le relazioni presenti un controllo agli elementi copiati.)

  • Collegamenti di relazione tra gli elementi copiati.

Questa regola viene applicata in modo ricorsivo agli elementi e collegamenti copiati.

Elementi copiati e incollati

Gli elementi e i collegamenti copiati vengono serializzati e archiviati in ElementGroupPrototype (EGP), che si trova negli Appunti.

Un'immagine degli elementi copiati viene inserita negli Appunti.Ciò consente all'utente di incolla di altre applicazioni quali Word.

L'utente può incollare elementi copiati in una destinazione che può accettare gli elementi in base alla definizione di modello DSL.Ad esempio, in un modello DSL generato dal modello della soluzione dei componenti, l'utente può incollare le porte sui componenti, ma non nel diagramma, e può incollare i componenti nel diagramma, ma non su altri componenti.

Personalizzazione del comportamento di copia e incolla

Per ulteriori informazioni sulla personalizzazione del modello utilizzando il codice del programma, vedere Esplorazione e aggiornamento di un modello nel codice del programma.

  • Per abilitare o disabilitare la copia, taglia e incolla.
    Nel modello DSL Esplora Risorse, impostare Abilitare incolla di copia proprietà di editor nodo.

  • I collegamenti di copia alla stessa destinazione. Ad esempio, di avere un commento copiato il boxing collegato al medesimo elemento dell'argomento.
    impostare copia di propagazioni proprietà del ruolo a Copia di propagazione per accedere solo.Per ulteriori informazioni, vedere Personalizzazione del comportamento di copia del collegamento.

  • Elementi collegati di copia. Ad esempio, si copia un nuovo elemento, le copie di tutte le caselle collegate di commento vengono apportate anche.
    impostare copia di propagazioni proprietà del ruolo a Copia di propagazione per accedere e giocatore di ruolo opposto.Per ulteriori informazioni, vedere Personalizzazione del comportamento di copia del collegamento.

  • elementi rapidamente duplicati copiando e incollando. In genere, l'elemento appena copiato è ancora selezionato e non è possibile incollare lo stesso tipo di elemento in.
    Aggiungere una direttiva di unione dell'elemento nella classe di dominio e impostarla per inoltrare le unioni alla classe padre.Questa operazione avrà lo stesso effetto sulle operazioni di trascinamento.Per ulteriori informazioni, vedere Personalizzazione della creazione e dello spostamento di elementi.

    - oppure -

    Selezionare il diagramma prima di inserimento di elementi, override ClipboardCommandSet.ProcessOnPasteCommand().Aggiungere questo codice in un file personalizzato nel progetto 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);
      }
    } }
    
  • Creare collegamenti aggiuntivi quando le paste utente in un database di destinazione selezionata. Ad esempio, quando una casella di commento viene incollato in un elemento, un collegamento viene eseguito tra loro.
    Aggiungere una direttiva di unione dell'elemento nella classe di dominio di destinazione e impostarla per elaborare l'unione aggiungendo collegamenti.Questa operazione avrà lo stesso effetto sulle operazioni di trascinamento.Per ulteriori informazioni, vedere Personalizzazione della creazione e dello spostamento di elementi.

    - oppure -

    override ClipboardCommandSet.ProcessOnPasteCommand() per creare collegamenti aggiuntivi dopo aver chiamato il metodo di base.

  • personalizzare i formati in cui gli elementi possono essere copiati alle applicazioni esterne, ad esempio aggiungere un bordo al form bitmap.
    override MyDslClipboardCommandSet.ProcessOnMenuCopyCommand() nel progetto DslPackage.

  • Personalizzare la modalità con cui gli elementi vengono copiati negli Appunti dal comando di copia, ma non in un'operazione di trascinamento.
    override MyDslClipboardCommandSet.CopyModelElementsIntoElementGroupPrototype() nel progetto DslPackage.

  • Layout di forma mantenimento di tramite la copia e incolla.
    Quando l'utente copia più forme, è possibile mantenere le relative posizioni relative quando vengono incollati.Questa tecnica è illustrata nell'esempio a VMSDK: Esempio degli schemi circuitali.

    Per ottenere questo risultato, aggiungere forme e i connettori al ElementGroupPrototype copiato.Il metodo più pratico di cui eseguire l'override è ElementOperations.CreateElementGroupPrototype().A tale scopo, aggiungere il seguente codice al progetto di 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;
    }
    
  • Incolla forme in un percorso selezionato, ad esempio la posizione corrente del cursore.
    Quando l'utente copia più forme, è possibile mantenere le relative posizioni relative quando vengono incollati.Questa tecnica è illustrata nell'esempio a VMSDK: Esempio degli schemi circuitali.

    Per ottenere questo risultato, override ClipboardCommandSet.ProcessOnMenuPasteCommand() per utilizzare la versione di posizione-specifica ElementOperations.Merge().A tale scopo, aggiungere il seguente codice nel progetto 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();
              }
            }
          }
        }
      }
    
  • Consentire all'utente di trascinare gli elementi.
    Vedere Procedura: aggiungere un gestore di trascinamento della selezione.

Personalizzazione del comportamento di copia del collegamento

Quando si copia un elemento, il comportamento standard è che tutti gli elementi incorporati vengono copiati.È possibile modificare il comportamento di copia standard.Nella definizione di modello DSL, selezionare un ruolo su un lato di una relazione e nella Finestra Proprietà impostare copia di propagazioni valore.

Propagazione della proprietà Copia del ruolo di dominio

Esistono tre valori:

  • non propagare la copia

  • Per propagare la copia per accedere solo - quando il gruppo viene incollato, la nuova copia del collegamento farà riferimento all'elemento esistente all'altra estremità del collegamento.

  • Per propagare la copia per accedere e il giocatore di ruolo opposto al gruppo copiato include una copia dell'elemento all'altra estremità del collegamento.

Effetto della copia con PropagateCopyToLinkOnly

Le modifiche apportate influiranno sia gli elementi che l'immagine che viene copiata.

Comportamento di copia e incolla di programmazione

Molti aspetti del comportamento di un modello DSL rispetto alla copia, la copia, la creazione e all'eliminazione degli oggetti sono determinati da un'istanza di ElementOperations ciò è abbinato al diagramma.È possibile modificare il comportamento del modello DSL derivando la classe personalizzata da ElementOperations ed eseguire l'override ElementOperations proprietà della classe del diagramma.

SuggerimentoSuggerimento

Per ulteriori informazioni sulla personalizzazione del modello utilizzando il codice del programma, vedere Esplorazione e aggiornamento di un modello nel codice del programma.

Diagramma sequenza per l'operazione CopiaDiagramma sequenza per l'operazione Incolla

Per definire diventi proprietaria ElementOperations

  1. In un nuovo file del progetto di modello DSL, creare una classe derivata da DesignSurfaceElementOperations.

  2. Aggiungere una definizione di classe parziale per la classe del diagramma.Il nome di questa classe è disponibile in Dsl\GeneratedCode\Diagrams.cs.

    Nella classe del diagramma, override ElementOperations per restituire un'istanza della sottoclasse di ElementOperations.È necessario restituire la stessa istanza a ogni chiamata.

Aggiungere questo codice in un file di codice personalizzato nel progetto 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
  }

Vengono ricevuti gli elementi trascinati da altri modelli

ElementOperations può essere utilizzato per definire la copia, lo spostamento, l'eliminazione e il comportamento di trascinamento della selezione.Come dimostra l'utilizzo di ElementOperations, l'esempio fornito di seguito definisce il comportamento di trascinamento della selezione personalizzate.Tuttavia, per questo scopo è opportuno considerare l'alternativa descritta in Procedura: aggiungere un gestore di trascinamento della selezione, più estensibile.

Definire due metodi nella classe di ElementOperations:

  • CanMerge(ModelElement targetElement, System.Windows.Forms.IDataObject data) quale determina se l'elemento di origine può essere trascinato nella forma, il connettore o nel diagramma di destinazione.

  • MergeElementGroupPrototype(ModelElement targetElement, ElementGroupPrototype sourcePrototype) le associazioni l'elemento di origine nel database di destinazione.

Ff521398.collapse_all(it-it,VS.110).gifCanMerge()

[CanMerge()] viene chiamato per determinare il feedback che deve essere fornito all'utente quando il mouse viene spostato nel diagramma.I parametri del metodo sono l'elemento su cui il mouse è posizionando e dati sul database di origine da cui l'operazione di trascinamento è stata eseguita.L'utente può trascinare in qualsiasi elemento dello schermo.Di conseguenza, l'oggetto di origine può essere di molti tipi diversi e può essere serializzato in diversi formati.Se l'origine è un modello UML o DSL, il parametro dati è la serializzazione di un oggetto ElementGroupPrototype.Le operazioni di trascinamento, di copia e della casella degli strumenti utilizzano ElementGroupPrototypes per rappresentare frammenti dei modelli.

Un prototipo del gruppo di elemento può essere contenuto un numero qualsiasi di elementi e collegamenti.I tipi di elemento possono essere identificati dai GUID.Il GUID è la forma in cui è stato trascinato, non l'elemento del modello sottostante.Nell'esempio seguente, CanMerge() restituisce true se la forma della classe da un diagramma UML viene trascinato in questo diagramma.

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

Questo metodo viene chiamato quando l'utente rilascia un elemento in un diagramma, una forma, o su un connettore.Deve unire il contenuto trascinato nell'elemento di destinazione.In questo esempio, il codice determina se riconosce la combinazione di tipi del prototipo e il database di destinazione; in tal caso, il metodo converte gli elementi trascinati in un prototipo degli elementi che devono essere aggiunte al modello.Il metodo di base viene chiamato per eseguire l'unione, degli elementi conversione o non convertiti.

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

Questo esempio si occupa degli elementi della classe UML trascinati da un diagramma classi UML.Il modello DSL non è progettato per memorizzare direttamente le classi UML, ma, viene creato un elemento DSL per ogni classe trascinata UML.Ciò risulta utile, ad esempio, se il modello DSL è un diagramma dell'istanza.L'utente può trascinare le classi nel diagramma per creare istanze di tali classi.

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

Comportamento della copia standard

Il codice in questa sezione sono illustrati i metodi che è possibile eseguire l'override per modificare il comportamento di copia.Per vedere come ottenere diventi proprietaria delle personalizzazioni, questa sezione viene illustrato il codice che esegue l'override dei metodi relativi alla copia, ma non modifica il comportamento standard.

Quando l'utente preme il tasto CTRL + c oppure utilizzare il comando di menu di copia, il metodo ProcessOnMenuCopyCommand viene chiamato.È possibile osservare come si tratta dell'impostazione in DslPackage\Generated Code\CommandSet.cs.Per ulteriori informazioni su come i controlli vengono configurati, vedere Procedura: aggiungere un comando al menu di scelta rapida.

È possibile eseguire l'override di ProcessOnMenuCopyCommand aggiunta di una definizione di classe parziale di MyDslClipboardCommandSet nel progetto 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);
}
}

Ogni diagramma dispone di un'istanza singletona di ElementOperations.È possibile fornire per contenere il derivato.Questo file, che può essere inserito nel progetto di modello DSL, è comporterebbe al codice che esegue l'override:

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

Vedere anche

Concetti

Personalizzazione della creazione e dello spostamento di elementi

Procedura: aggiungere un gestore di trascinamento della selezione

Personalizzazione del comportamento di eliminazione

Altre risorse

esempio: Esempio degli schemi circuitali VMSDK