Condividi tramite


Procedura: definire un gestore di trascinamento della selezione e doppio clic in un diagramma di modellazione

In Visual Studio Ultimate è possibile definire comandi che vengono eseguiti quando l'utente fa doppio clic sugli elementi o li trascina in un diagramma UML. È possibile comprimere queste estensioni in un pacchetto Visual Studio Integration Extension, abbreviato in VSIX (la pagina potrebbe essere in inglese), e distribuirlo ad altri utenti di Visual Studio Ultimate.

Se è già disponibile un comportamento predefinito per il tipo di diagramma e il tipo di elemento che si desidera trascinare, potrebbe non essere possibile aggiungere questo comportamento o eseguirne l'override.

Requisiti

Creazione di un gestore movimenti

Per definire un gestore movimenti per una finestra di progettazione UML, è necessario creare una classe che definisce il comportamento del gestore movimenti e incorporarla in un progetto VSIX (Visual Studio Integration Extension). Il progetto VSIX funge da contenitore per l'installazione del gestore. Esistono due metodi alternativi per definire un gestore movimenti:

  • Creazione di un gestore movimenti in un progetto VSIX distinto mediante un modello di progetto. Si tratta del metodo più rapido. Utilizzarlo se non si desidera combinare il gestore con altri tipi di estensione quali estensioni della convalida, elementi della casella degli strumenti personalizzati o gestori movimenti.

  • Creazione di progetti di gestori movimenti e progetti VSIX distinti. Utilizzare questo metodo se si desidera combinare più tipi di estensione nello stesso progetto VSIX. Ad esempio, se per il gestore movimenti è previsto che il modello osservi vincoli specifici, è possibile incorporarlo nello stesso progetto VSIX come un metodo di convalida.

Per creare un gestore movimenti in un progetto VSIX distinto

  1. Nella finestra di dialogo Nuovo progetto, sotto Progetti di modello fare clic su Estensione di movimento.

  2. Aprire il file con estensione .cs nel nuovo progetto e modificare la classe GestureExtension per implementare il gestore movimenti.

    Per ulteriori informazioni, vedere Implementazione del gestore movimenti.

  3. Testare il gestore movimenti premendo F5. Per ulteriori informazioni, vedere Esecuzione del gestore movimenti.

  4. Installare il gestore movimenti in un altro computer copiando il file bin\*\*.vsix compilato dal progetto. Per ulteriori informazioni, vedere Installazione del gestore movimenti.

Per creare il progetto Libreria di classi (DLL) per il gestore movimenti

  1. Creare un progetto Libreria di classi, in una nuova soluzione Visual Studio o in una soluzione esistente.

    1. Scegliere Nuovo dal menu File, quindi fare clic su Progetto.

    2. In Modelli installati fare clic su Visual Basic o Visual C#, quindi nella colonna centrale fare clic su Libreria di classi.

    3. Impostare Soluzione per indicare se si desidera creare una nuova soluzione o aggiungere un componente a una soluzione VSIX già aperta.

    4. Impostare nome e percorso per il progetto, quindi fare clic su OK.

  2. Aggiungere i riferimenti seguenti al progetto.

    Microsoft.VisualStudio.Modeling.Sdk.10.0

    Microsoft.VisualStudio.Modeling.Sdk.Diagrams.10.0

    Microsoft.VisualStudio.ArchitectureTools.Extensibility

    Microsoft.VisualStudio.Uml.Interfaces

    System.ComponentModel.Composition

    System.Windows.Forms

    Microsoft.VisualStudio.ArchitectureTools.Extensibility.Layer - Se si estendono diagrammi livello, è necessario solo questo riferimento. Per ulteriori informazioni, vedere Creazione di estensioni per diagrammi livelli.

  3. Aggiungere un file di classe al progetto e impostarne il contenuto sul codice seguente.

    Nota

    Modificare i nomi di spazio dei nomi e classi in base alle proprie preferenze.

    using System.ComponentModel.Composition;
    using System.Linq;
    using System.Collections.Generic;
    using Microsoft.VisualStudio.Modeling.Diagrams;
    using Microsoft.VisualStudio.Modeling.Diagrams.ExtensionEnablement;
    using Microsoft.VisualStudio.Modeling.ExtensionEnablement;
    using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml;
    using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Presentation;
    using Microsoft.VisualStudio.Uml.AuxiliaryConstructs;
    using Microsoft.VisualStudio.Modeling;
    using Microsoft.VisualStudio.Uml.Classes;
    // ADD other UML namespaces if required
    
    namespace MyGestureHandler // CHANGE
    {
      // DELETE any of these attributes if the handler
      // should not work with some types of diagram.
      [ClassDesignerExtension]
      [ActivityDesignerExtension]
      [ComponentDesignerExtension]
      [SequenceDesignerExtension]
      [UseCaseDesignerExtension]
      // [LayerDesignerExtension]
    
      // Gesture handlers must export IGestureExtension:
      [Export(typeof(IGestureExtension))]
      // CHANGE class name
      public class MyGesture1 : IGestureExtension
      {
        [Import]
        public IDiagramContext DiagramContext { get; set; }
    
        /// <summary>
        /// Called when the user double-clicks on the diagram
        /// </summary>
        /// <param name="targetElement"></param>
        /// <param name="diagramPointEventArgs"></param>
        public void OnDoubleClick(ShapeElement targetElement, DiagramPointEventArgs diagramPointEventArgs)
        {
          // CHANGE THIS CODE FOR YOUR APPLICATION.
    
          // Get the target shape, if any. Null if the target is the diagram.
          IShape targetIShape = targetElement.CreateIShape();
    
          // Do something...
        }
    
        /// <summary>
        /// Called repeatedly when the user drags from anywhere on the screen.
        /// Return value should indicate whether a drop here is allowed.
        /// </summary>
        /// <param name="targetMergeElement">References the element to be dropped on.</param>
        /// <param name="diagramDragEventArgs">References the element to be dropped.</param>
        /// <returns></returns>
        public bool CanDragDrop(ShapeElement targetMergeElement, DiagramDragEventArgs diagramDragEventArgs)
        {
          // CHANGE THIS CODE FOR YOUR APPLICATION.
    
          // Get the target element, if any. Null if the target is the diagram.
          IShape targetIShape = targetMergeElement.CreateIShape();
    
          // This example allows drag of any UML elements.
          return GetModelElementsFromDragEvent(diagramDragEventArgs).Count() > 0;
        }
    
    
        /// <summary>
        /// Execute the action to be performed on the drop.
        /// </summary>
        /// <param name="targetDropElement"></param>
        /// <param name="diagramDragEventArgs"></param>
        public void OnDragDrop(ShapeElement targetDropElement, DiagramDragEventArgs diagramDragEventArgs)
        {
          // CHANGE THIS CODE FOR YOUR APPLICATION.
        }
    
        /// <summary>
        /// Retrieves UML IElements from drag arguments.
        /// Works for drags from UML diagrams.
        /// </summary>
        private IEnumerable<IElement> GetModelElementsFromDragEvent
                (DiagramDragEventArgs dragEvent)
        {
          //ElementGroupPrototype is the container for
          //dragged and copied elements and toolbox items.
          ElementGroupPrototype prototype =
             dragEvent.Data.
             GetData(typeof(ElementGroupPrototype))
                  as ElementGroupPrototype;
          // Locate the originals in the implementation store.
          IElementDirectory implementationDirectory =
             dragEvent.DiagramClientView.Diagram.Store.ElementDirectory;
    
          return prototype.ProtoElements.Select(
            prototypeElement =>
            {
              ModelElement element = implementationDirectory
                .FindElement(prototypeElement.ElementId);
              ShapeElement shapeElement = element as ShapeElement;
              if (shapeElement != null)
              {
                // Dragged from a diagram.
                return shapeElement.ModelElement as IElement;
              }
              else
              {
                // Dragged from UML Model Explorer.
                return element as IElement;
              }
            });
        }
    
      }
    }
    

    Per ulteriori informazioni su cosa inserire nei metodi, vedere Implementazione del gestore movimenti.

È necessario aggiungere il comando di menu a un progetto VSIX, che fungerà da contenitore per l'installazione del comando. Se si desidera, è possibile includere altri componenti nello stesso progetto VSIX.

Per aggiungere un gestore movimenti distinto a un progetto VSIX

  1. Questa routine non è necessaria se si è creato il comando di menu con un progetto VSIX distinto.

  2. Creare un progetto VSIX, a meno che la soluzione non ne includa già uno.

    1. In Esplora soluzioni fare clic con il pulsante destro del mouse sulla soluzione, scegliere Aggiungi, quindi Nuovo progetto.

    2. In Modelli installati espandere Visual Basic o Visual C#, quindi fare clic su Extensibility. Nella colonna centrale fare clic su Progetto VSIX.

  3. Impostare il progetto VSIX come progetto di avvio della soluzione.

    • In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto VSIX e scegliere Imposta come progetto di avvio.
  4. In source.extension.vsixmanifest, sotto Contenuto, aggiungere il progetto Libreria di classi del gestore movimenti come componente MEF.

    1. Aprire source.extension.vsixmanifest

    2. Fare clic su Aggiungi contenuto.

    3. In Seleziona un tipo di contenuto selezionare Componente MEF.

    4. In Selezionare un'origine fare clic su Progetto e selezionare il nome del progetto Libreria di classi del gestore movimenti.

  5. Fare clic su Seleziona versioni e selezionare le edizioni di Visual Studio in cui si desidera eseguire l'estensione.

  6. Impostare il nome e i campi descrittivi del progetto VSIX. Salvare il file.

Esecuzione del gestore movimenti

A scopo di test, eseguire il gestore movimenti in modalità di debug.

Per testare il gestore movimenti

  1. Premere F5 oppure scegliere Avvia debug dal menu Debug.

    Verrà avviata un'istanza sperimentale di Visual Studio.

    Risoluzione dei problemi. Se non viene avviata una nuova istanza di Visual Studio:

    • Se si dispone di più progetti, verificare che il progetto VSIX sia impostato come progetto di avvio della soluzione.

    • In Esplora soluzioni fare clic con il pulsante destro del mouse sul nome del progetto di avvio o del solo progetto disponibile, quindi scegliere Proprietà. Fare clic sulla scheda Debug nell'editor delle proprietà del progetto. Assicurarsi che la stringa nel campo Avvia programma esterno sia il percorso completo di Visual Studio, in genere:

      C:\Programmi\Microsoft Visual Studio 10.0\Common7\IDE\devenv.exe

  2. Nell'istanza sperimentale di Visual Studio aprire o creare un progetto di modello e un diagramma di modellazione. Utilizzare un diagramma appartenente a uno dei tipi elencati negli attributi della classe del gestore movimenti.

  3. Fare doppio clic in un punto qualsiasi del diagramma. Dovrebbe venire chiamato il gestore relativo al doppio clic.

  4. Trascinare un elemento da Esplora modelli UML nel diagramma. Dovrebbe venire chiamato il gestore relativo al trascinamento.

Risoluzione dei problemi: se il gestore movimenti non funziona, verificare gli aspetti seguenti:

  • Il progetto di gestore movimenti è elencato come componente MEF nell'elenco Contenuto in source.extensions.manifest nel progetto VSIX.

  • I parametri di tutti gli attributi Import ed Export sono validi.

  • Il metodo CanDragDrop non restituisce false.

  • Il tipo di diagramma di modello utilizzato (classi UML, di sequenza e così via) viene elencato come uno degli attributi della classe del gestore movimenti [ClassDesignerExtension], [SequenceDesignerExtension] e così via.

  • Non vi sono funzionalità predefinite già definite per questo tipo di elemento di destinazione ed eliminato.

Implementazione del gestore movimenti

Metodi del gestore movimenti

La classe del gestore movimenti implementa ed esporta IGestureExtension. I metodi da definire sono i seguenti:

bool CanDragDrop (ShapeElement target, DiagramDragEventArgs dragEvent)

Restituisce true per consentire all'elemento di origine a cui viene fatto riferimento in dragEvent di essere rilasciato in questa destinazione.

Questo metodo non deve apportare modifiche al modello. Deve funzionare rapidamente, in quanto viene utilizzato per determinare lo stato della freccia quando l'utente sposta il mouse.

void OnDragDrop (ShapeElement target, DiagramDragEventArgs dragEvent)

Aggiorna il modello in base all'oggetto di origine a cui viene fatto riferimento in dragEvent e alla destinazione.

Chiamato quando l'utente rilascia il mouse dopo un'operazione di trascinamento.

void OnDoubleClick (ShapeElement target, DiagramPointEventArgs pointEvent)

target è la forma sui cui l'utente ha fatto doppio clic.

È possibile scrivere gestori che possono accettare non solo UML ma anche un'ampia varietà di altri elementi, ad esempio file, nodi in una visualizzazione della classe .NET, nodi di Esplora architettura e così via. L'utente può trascinare uno di questi elementi in un diagramma UML, purché venga scritto un metodo OnDragDrop in grado di decodificare il form serializzato degli elementi. I metodi di decodifica variano da un tipo di elemento all'altro.

I parametri di questi metodi sono i seguenti:

  • ShapeElement target. Forma o diagramma su cui l'utente ha trascinato un qualche elemento.

    ShapeElement è una classe nell'implementazione sottostante gli strumenti di modellazione UML. Per ridurre il rischio di collocare i diagrammi e il modello UML in uno stato incoerente, è consigliabile non utilizzare direttamente i metodi di questa classe. Al contrario, eseguire il wrapping dell'elemento in IShape, quindi utilizzare i metodi descritti in Procedura: visualizzare un modello nei diagrammi.

    • Per ottenere IShape:

      IShape targetIShape = target.CreateIShape(target);
      
    • Per ottenere l'elemento del modello impostato come destinazione dall'operazione di trascinamento o doppio clic:

      IElement target = targetIShape.Element;
      

      È possibile eseguire il cast di questo elemento a un tipo di elemento più specifico.

    • Per ottenere l'archivio modelli UML che contiene il modello UML:

      IModelStore modelStore = 
        targetIShape.Element.GetModelStore(); 
      
    • Per ottenere l'accesso all'host e al provider del servizio:

      target.Store.GetService(typeof(EnvDTE.DTE)) as EnvDTE.DTE
      
  • DiagramDragEventArgs eventArgs. Questo parametro fornisce il form serializzato dell'oggetto di origine di un'operazione di trascinamento:

    System.Windows.Forms.IDataObject data = eventArgs.Data;  
    

    È possibile trascinare elementi di molti tipi diversi in un diagramma, da parti diverse di Visual Studio o dal desktop di Windows. Tipi diversi di elemento sono codificati in modi diversi in IDataObject. Per estrarre da esso gli elementi, fare riferimento alla documentazione per il tipo appropriato di oggetto.

    Se l'oggetto di origine è un elemento UML trascinato da Esplora modelli UML o da un altro diagramma UML, fare riferimento a Procedura: ottenere elementi di modelli UML da IDataObject.

Scrittura del codice dei metodi

Per ulteriori informazioni sulla scrittura del codice per leggere e aggiornare il modello, vedere Programmazione con l'API UML.

Per informazioni sull'accesso alle informazioni sul modello in un'operazione di trascinamento, vedere Procedura: ottenere elementi di modelli UML da IDataObject.

Se si utilizza un diagramma di sequenza, vedere anche Procedura: modificare i diagrammi di sequenza tramite l'API UML.

Oltre ai parametri dei metodi, è inoltre possibile dichiarare una proprietà importata nella classe che fornisce accesso al modello e al diagramma corrente.

[Import] public IDiagramContext DiagramContext { get; set; }

La dichiarazione di IDiagramContext consente di scrivere codice nei metodi che sia in grado di accedere al diagramma, alla selezione corrente e al modello:

IDiagram diagram = this.DiagramContext.CurrentDiagram;
foreach (IShape<IElement> shape in diagram.GetSelectedShapes<IElement>)
{ IElement element = shape.Element; ... }
IModelStore modelStore = diagram.ModelStore;
IModel model = modelStore.Root;
foreach (IDiagram diagram in modelStore.Diagrams) {...}
foreach (IElement element in modelStore.AllInstances<IUseCase>) {...}

Per ulteriori informazioni, vedere Procedura: esplorare il modello UML.

Installazione e disinstallazione di un'estensione

È possibile installare un'estensione di Visual Studio sia nel computer in uso che in altri computer.

Per installare un'estensione

  1. Nel computer trovare il file .vsix compilato dal progetto VSIX.

    1. In Esplora soluzioni fare clic con il pulsante destro del mouse sul progetto VSIX, quindi scegliere Apri cartella in Esplora risorse.

    2. Individuare il file bin\*\Progetto.vsix

  2. Copiare il file .vsix nel computer di destinazione in cui si desidera installare l'estensione. Si può trattare del computer in uso o di un altro computer.

    Nel computer di destinazione deve essere presente una delle versioni di Visual Studio specificata in source.extension.vsixmanifest.

  3. Nel computer di destinazione fare doppio clic sul file .vsix.

    Verrà visualizzato Visual Studio Extension Installer e verrà installata l'estensione.

  4. Avviare o riavviare Visual Studio.

Per disinstallare un'estensione

  1. Scegliere Gestione estensioni dal menu Strumenti.

  2. Espandere Estensioni installate.

  3. Selezionare l'estensione, quindi fare clic su Disinstalla.

Raramente, può verificarsi che un'estensione errata non venga caricata creando un rapporto nella finestra di errore. Tale estensione non viene però visualizzata in Gestione estensioni. In tal caso, è possibile rimuovere l'estensione eliminando il file da:

%LocalAppData%\Local\Microsoft\VisualStudio\10.0\Extensions

Esempio

Nell'esempio seguente viene illustrato come creare linee di vita in un diagramma sequenza, in base alle parti e alle porte di un componente, trascinato da un diagramma componente.

Per testarlo, premere F5. Verrà aperta un'istanza sperimentale di Visual Studio. In questa istanza aprire un modello UML e creare un componente in un diagramma componente. Aggiungere a questo componente alcune interfacce e parti di componente interne. Selezionare le interfacce e le parti. Trascinare quindi le interfacce e le parti su un diagramma di sequenza. Trascinare dal diagramma dei componenti alla scheda per il diagramma di sequenza e quindi verso il basso nel diagramma di sequenza. Verrà visualizzata una linea di vita per ogni interfaccia e parte.

Per ulteriori informazioni sull'associazione di interazioni ai diagrammi di sequenza, vedere Procedura: modificare i diagrammi di sequenza tramite l'API UML.

using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using Microsoft.VisualStudio.Modeling;
using Microsoft.VisualStudio.Modeling.Diagrams;
using Microsoft.VisualStudio.Modeling.Diagrams.ExtensionEnablement;
using Microsoft.VisualStudio.Modeling.ExtensionEnablement;
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml;
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Presentation;
using Microsoft.VisualStudio.Uml.AuxiliaryConstructs;
using Microsoft.VisualStudio.Uml.Classes;
using Microsoft.VisualStudio.Uml.Interactions;
using Microsoft.VisualStudio.Uml.CompositeStructures;
using Microsoft.VisualStudio.Uml.Components;

/// <summary>
/// Creates lifelines from component ports and parts.
/// </summary>
[Export(typeof(IGestureExtension))]
[SequenceDesignerExtension]
public class CreateLifelinesFromComponentParts : IGestureExtension
{
  [Import]
  public IDiagramContext Context { get; set; }

  /// <summary>
  /// Called by the modeling framework when
  /// the user drops something on a target.
  /// </summary>
  /// <param name="target">The target shape or diagram </param>
  /// <param name="dragEvent">The item being dragged</param>
  public void OnDragDrop(ShapeElement target,
           DiagramDragEventArgs dragEvent)
  {
    ISequenceDiagram diagram = Context.CurrentDiagram
            as ISequenceDiagram;
    IInteraction interaction = diagram.Interaction;
    if (interaction == null)
    {
      // Sequence diagram is empty: create an interaction.
      interaction = diagram.ModelStore.Root.CreateInteraction();
      interaction.Name = Context.CurrentDiagram.Name;
      diagram.Bind(interaction);
    }
    foreach (IConnectableElement connectable in
       GetConnectablesFromDrag(dragEvent))
    {
      ILifeline lifeline = interaction.CreateLifeline();
      lifeline.Represents = connectable;
      lifeline.Name = connectable.Name;
    }
  }

  /// <summary>
  /// Called by the modeling framework to determine whether
  /// the user can drop something on a target.
  /// Must not change anything.
  /// </summary>
  /// <param name="target">The target shape or diagram</param>
  /// <param name="dragEvent">The item being dragged</param>
  /// <returns>true if this item can be dropped on this target</returns>
  public bool CanDragDrop(ShapeElement target,
           DiagramDragEventArgs dragEvent)
  {
    IEnumerable<IConnectableElement> connectables = GetConnectablesFromDrag(dragEvent);
    return connectables.Count() > 0;
  }

  ///<summary>
  /// Get dragged parts and ports of an IComponent.
  ///</summary>
  private IEnumerable<IConnectableElement>
    GetConnectablesFromDrag(DiagramDragEventArgs dragEvent)
  {
    foreach (IElement element in
      GetModelElementsFromDragEvent(dragEvent))
    {
      IConnectableElement part = element as IConnectableElement;
      if (part != null)
      {
        yield return part;
      }
    }
  }

  /// <summary>
  /// Retrieves UML IElements from drag arguments.
  /// Works for drags from UML diagrams.
  /// </summary>
  private IEnumerable<IElement> GetModelElementsFromDragEvent
          (DiagramDragEventArgs dragEvent)
  {
    //ElementGroupPrototype is the container for
    //dragged and copied elements and toolbox items.
    ElementGroupPrototype prototype =
       dragEvent.Data.
       GetData(typeof(ElementGroupPrototype))
            as ElementGroupPrototype;
    // Locate the originals in the implementation store.
    IElementDirectory implementationDirectory =
       dragEvent.DiagramClientView.Diagram.Store.ElementDirectory;

    return prototype.ProtoElements.Select(
      prototypeElement =>
      {
        ModelElement element = implementationDirectory
          .FindElement(prototypeElement.ElementId);
        ShapeElement shapeElement = element as ShapeElement;
        if (shapeElement != null)
        {
          // Dragged from a diagram.
          return shapeElement.ModelElement as IElement;
        }
        else
        {
          // Dragged from UML Model Explorer.
          return element as IElement;
        }
      });
  }

  public void OnDoubleClick(ShapeElement targetElement, DiagramPointEventArgs diagramPointEventArgs)
  {
  }
}

Il codice di GetModelElementsFromDragEvent() è descritto nell'argomento Procedura: ottenere elementi di modelli UML da IDataObject.

Vedere anche

Concetti

Estensione di modelli e diagrammi UML

Procedura: definire un comando di menu in un diagramma di modellazione

Procedura: definire vincoli di convalida per i modelli UML

Programmazione con l'API UML

Altre risorse

Procedura: definire e installare un'estensione di modellazione