Compartir a través de


Cómo: Definir un controlador de colocación y doble clic en un diagrama de modelado

En Visual Studio Ultimate, puede definir comandos que se ejecuten cuando el usuario haga doble clic o arrastre elementos hasta un diagrama UML. Puede empaquetar estas extensiones en una Extensión de integración de Visual Studio (VSIX) y distribuirla a otros usuarios de Visual Studio Ultimate.

Si ya hay un comportamiento integrado por el tipo de diagrama y el tipo de elemento que desea arrastrar, quizás no pueda agregar o invalidar este comportamiento.

Requisitos

Crear un controlador de gestos

Para definir un controlador de movimientos para un diseñador UML, debe crear una clase que defina el comportamiento del controlador de movimientos e insertarla en una extensión de integración de Visual Studio (VSIX). Las extensiones VSIX actúan como contenedores que instalan el controlador. Hay dos métodos para definir un controlador de movimiento:

  • Crear un controlador de movimientos en una VSIX utilizando una plantilla de proyecto. Este es el método más rápido. Utilícelo si no desea combinar el controlador con otros tipos de extensión, como extensiones de validación, elementos de cuadro de herramientas o comandos de menú.

  • Crear controladores de movimiento y proyectos VSIX independientes. Use este método si desea combinar varios tipos de extensiones en la misma VSIX. Por ejemplo, si el controlador de movimientos espera que el modelo respete restricciones concretas, podría insertarlo en la misma VSIX como método de validación.

Para crear un controlador de movimientos en un VSIX propio

  1. En el cuadro de diálogo Nuevo proyecto, en Proyectos de modelado, seleccione Extensión de movimiento.

  2. Abra el archivo .cs en el nuevo proyecto y modifique la clase GestureExtension para implementar el controlador de movimientos.

    Para obtener más información, vea Implementar el controlador de movimientos.

  3. Pruebe el controlador de movimientos presionando F5. Para obtener más información, vea Ejecutar el controlador de movimientos.

  4. Instale el controlador de movimientos en otro equipo copiando el archivo bin\*\*.vsix que compila el proyecto. Para obtener más información, vea Instalar el controlador de movimientos.

Para crear el proyecto de biblioteca de clases (DLL) para el controlador de movimientos

  1. Cree un proyecto de biblioteca de clases en una nueva solución Visual Studio o en una solución existente.

    1. En el menú Archivo, elija Nuevo y haga clic en Proyecto.

    2. Bajo Plantillas instaladas, haga clic en Visual C# o en Visual Basic y a continuación, en la columna central, haga clic en Biblioteca de clases.

    3. Establezca Solución para indicar si desea crear una nueva solución o agregar un componente a una solución VSIX que ya tiene abierta.

    4. Especifique el nombre y la ubicación del proyecto, y haga clic en Aceptar.

  2. Agregue las referencias siguientes al proyecto.

    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 - solo lo necesita si está ampliando diagramas de capas. Para obtener más información, vea Crear extensiones de diagramas de capas.

  3. Agregue un archivo de clase al proyecto y establezca su contenido en el código siguiente.

    Nota

    Cambie el nombre de clase y de espacio de nombres como desee.

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

    Para obtener más información sobre qué incluir en los métodos, vea Implementar el controlador de gestos.

Debe agregar el comando de menú a un proyecto VSIX, que actúa como contenedor para instalar el comando. Si lo desea, puede incluir otros componentes en el mismo VSIX.

Para agregar un controlador de movimientos independiente a un proyecto VSIX

  1. No necesita este procedimiento si ha creado el comando de menú con un VSIX propio.

  2. Cree un proyecto VSIX, a menos que la solución ya tenga uno.

    1. En el Explorador de soluciones, haga clic con el botón secundario en la solución, seleccione Agregar y, a continuación, haga clic en Nuevo proyecto.

    2. En Plantillas instaladas, expanda Visual C# o Visual Basic y, a continuación, haga clic en Extensibilidad. En la columna central, haga clic en Proyecto VSIX.

  3. Establezca el proyecto VSIX como proyecto de inicio de la solución.

    • En el Explorador de soluciones, haga clic con el botón secundario en el proyecto VSIX y, a continuación, haga clic en Establecer como proyecto de inicio.
  4. En source.extension.vsixmanifest, en Contenido, agregue el proyecto de biblioteca de clases del controlador de gestos como un componente MEF.

    1. Abra source.extension.vsixmanifest.

    2. Haga clic en Agregar contenido.

    3. En Seleccione un tipo de contenido, seleccione MEF Component.

    4. En Seleccione un origen, haga clic en Proyecto y seleccione el nombre del proyecto de biblioteca de clases del controlador de gestos.

  5. Haga clic en Seleccionar ediciones y seleccione las ediciones de Visual Studio en las que desee que se ejecute la extensión.

  6. Establezca el nombre y los campos descriptivos de VSIX. Guarde el archivo.

Ejecutar el controlador de movimientos

Para fines de prueba, ejecute el controlador de movimientos en modo de depuración.

Para probar el controlador de gestos

  1. Presione F5 o bien, en el menú Depurar, haga clic en Iniciar depuración.

    Se iniciará una instancia experimental de Visual Studio.

    Solución de problemas: si no se inicia un nuevo Visual Studio:

    • Si tiene más de un proyecto, asegúrese de que el proyecto VSIX está configurado como proyecto de inicio de la solución.

    • En el Explorador de soluciones, haga clic con el botón secundario en el proyecto de inicio o único y después haga clic en Propiedades. En el editor de propiedades del proyecto, haga clic en la pestaña Depurar. Asegúrese de que la cadena del campo Programa externo de inicio es el nombre de ruta de acceso completo de Visual Studio, normalmente:

      C:\Archivos de programa\Microsoft Visual Studio 10.0\Common7\IDE\devenv.exe

  2. En la instancia experimental de Visual Studio, abra o cree un proyecto de modelado, y abra o cree un diagrama de modelado. Use un diagrama perteneciente a uno de los tipos enumerados en los atributos de la clase del controlador de gestos.

  3. Haga doble clic en cualquier parte del diagrama. Se debe llamar al controlador de doble clic.

  4. Arrastre un elemento desde el Explorador de UML al diagrama. Se debe llamar al controlador de arrastre.

Solución de problemas: si el controlador de movimientos no funciona, asegúrese de lo siguiente:

  • El proyecto de controlador de gestos se muestra como un componente MEF en la lista Contenido de source.extensions.manifest en el proyecto VSIX.

  • Los parámetros de todos los atributos Import y Export son válidos.

  • El método CanDragDrop no devuelve false.

  • El tipo de diagrama del modelo que está usando (clase UML, secuencia, etc.) se muestra como uno de los atributos de la clase del controlador de gestos [ClassDesignerExtension], [SequenceDesignerExtension], etc.

  • No hay ninguna funcionalidad integrada ya definida para este tipo de elemento de destino y colocado.

Implementar el controlador de gestos

Los métodos del controlador de gestos

La clase del controlador de gestos implementa y exporta IGestureExtension. Los métodos que necesita definir son los siguientes:

bool CanDragDrop (ShapeElement target, DiagramDragEventArgs dragEvent)

Devuelve true para permitir que el elemento de origen al que se hace referencia en dragEvent se coloque en este destino.

Este método no debe realizar cambios al modelo. Debe funcionar rápidamente, ya que se usa para determinar el estado de la flecha a medida que el usuario mueve el mouse.

void OnDragDrop (ShapeElement target, DiagramDragEventArgs dragEvent)

Actualiza el modelo en función del objeto de origen al que se hace referencia en dragEvent y el destino.

Se le llama cuando el usuario suelta el mouse después de arrastrar.

void OnDoubleClick (ShapeElement target, DiagramPointEventArgs pointEvent)

target es la forma en la que el usuario hizo doble clic.

Puede escribir controladores capaces de admitir no solo elementos de UML sino también una gran variedad de otros elementos, como archivos, nodos de una vista de clases .NET, nodos del Explorador de arquitectura, etc. El usuario puede arrastrar cualquiera de estos elementos a un diagrama UML, siempre que escriba un método OnDragDrop que pueda descodificar el formato serializado de los elementos. Los métodos de descodificación varían entre los tipos de elementos.

Los parámetros de estos métodos son:

  • ShapeElement target. La forma o el diagrama en el que el usuario ha arrastrado algo.

    ShapeElement es una clase de la implementación subyacente a las herramientas de modelado UML. Para reducir el riesgo de provocar un estado incoherente en el modelo y diagramas UML, se recomienda no utilizar los métodos de esta clase directamente. En su lugar, ajuste el elemento en un objeto IShape y, a continuación, utilice los métodos descritos en Cómo: Mostrar un modelo en diagramas.

    • Para obtener un objeto IShape:

      IShape targetIShape = target.CreateIShape(target);
      
    • Para obtener el elemento del modelo que es el destino de la operación de arrastre o doble clic:

      IElement target = targetIShape.Element;
      

      Puede convertirlo en un tipo de elemento más específico.

    • Para obtener el almacén de modelos UML que contiene el modelo UML:

      IModelStore modelStore = 
        targetIShape.Element.GetModelStore(); 
      
    • Para obtener acceso al host y al proveedor de servicios:

      target.Store.GetService(typeof(EnvDTE.DTE)) as EnvDTE.DTE
      
  • DiagramDragEventArgs eventArgs. Este parámetro lleva el formato serializado del objeto de origen de una operación de arrastre:

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

    Puede arrastrar elementos de muchos tipos diferentes a un diagrama, de diferentes partes de Visual Studio, o del escritorio de Windows. Los diferentes tipos de elemento se codifican de maneras diferentes en IDataObject. Para extraer los elementos de él, consulte la documentación para saber cuál es el tipo de objeto adecuado.

    Si el objeto de origen es un elemento UML arrastrado desde el Explorador de modelos UML o desde otro diagrama UML, consulte Cómo: Obtener elementos del modelo UML a partir de IDataObject.

Escribir el código de los métodos

Para obtener más información acerca de cómo se escribe código para leer y actualizar el modelo, vea Programar con la API de UML.

Para obtener información acerca de cómo se obtiene acceso a la información del modelo en una operación de arrastre, vea Cómo: Obtener elementos del modelo UML a partir de IDataObject.

Si está trabajando con un diagrama de secuencia, vea también Cómo: Modificar diagramas de secuencia usando la API de UML.

Además de los parámetros de los métodos, también puede declarar una propiedad importada en la clase que proporcione acceso al diagrama y modelo actuales.

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

La declaración de IDiagramContext le permite escribir en los métodos código que tiene acceso al diagrama, la selección actual y el modelo:

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>) {...}

Para obtener más información, vea Cómo: Navegar por el modelo UML.

Instalar y desinstalar una extensión

Puede instalar una extensión de Visual Studio en su propio equipo y en otros equipos.

Para instalar una extensión

  1. En el equipo, busque el archivo .vsix compilado en el proyecto VSIX.

    1. En el Explorador de soluciones, haga clic con el botón secundario en el proyecto VSIX y, a continuación, haga clic en Abrir carpeta en el Explorador de Windows.

    2. Busque el archivo bin\*\SuProyecto.vsix

  2. Copie el archivo .vsix en el equipo de destino en el que desea instalar la extensión. Puede tratarse de su propio equipo o de otro.

    El equipo de destino debe tener una de las ediciones de Visual Studio que especificó en source.extension.vsixmanifest.

  3. En el equipo de destino, haga doble clic en el archivo .vsix.

    El Instalador de extensiones de Visual Studio se abre e instala la extensión.

  4. Inicie o reinicie Visual Studio.

Para desinstalar una extensión

  1. En el menú Herramientas, haga clic en Administrador de extensiones.

  2. Expanda Extensiones instaladas.

  3. Seleccione la extensión y, a continuación, haga clic Desinstalar.

En contadas ocasiones, una extensión defectuosa no se carga y crea un informe en la ventana de error, aunque no aparece en el Administrador de extensiones. En ese caso, puede quitar la extensión eliminando el archivo de:

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

Ejemplo

En el siguiente ejemplo se muestra cómo crear las líneas de vida de un diagrama de secuencia, basándose en los elementos y puertos de un componente, arrastrado desde un diagrama de componentes.

Para probarlo, presione F5. Se abre una instancia experimental de Visual Studio. En esta instancia, abra un modelo UML y cree un componente en un diagrama de componentes. Agregue algunas interfaces y elementos internos a este componente. Seleccione las interfaces y los elementos. A continuación, arrastre las interfaces y los elementos a un diagrama de secuencia. (Arrastre desde el diagrama de componentes hasta la pestaña correspondiente al diagrama de secuencia y, a continuación, hacia abajo en el diagrama de secuencia.) Aparecerá una línea de vida para cada interfaz y elemento.

Para obtener más información acerca de cómo enlazar interacciones a los diagramas de secuencia, vea Cómo: Modificar diagramas de secuencia usando la API de 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)
  {
  }
}

El código de GetModelElementsFromDragEvent() se describe en Cómo: Obtener elementos del modelo UML a partir de IDataObject.

Vea también

Otros recursos

Cómo: Definir e instalar una extensión de modelado

Ampliar modelos y diagramas UML

Cómo: Definir un comando de menú en un diagrama de modelado

Cómo: Definir restricciones de validación para modelos UML

Programar con la API de UML