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

En Visual Studio Ultimate, puede definir elementos de menú adicionales que aparezcan cuando el usuario haga clic con el botón secundario en un diagrama UML. Puede controlar si el comando de menú aparece y está habilitado cuando el usuario haga clic con el botón secundario en cualquier elemento en el diagrama, y puede escribir código que se ejecute cuando el usuario seleccione el elemento de menú. Puede empaquetar estas extensiones en una Extensión de integración de Visual Studio (VSIX) y distribuirla a otros usuarios de Visual Studio Ultimate.

Requisitos

Definir el comando de menú

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

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

  • Crear un comando de menú y proyectos VSIX independientes. Use este método si desea combinar varios tipos de extensiones en la misma VSIX. Por ejemplo, si el comando de menú espera que el modelo respete restricciones concretas, podría incrustarlo en la misma VSIX como método de validación.

Para crear un comando de menú en una VSIX

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

  2. Abra el archivo .cs en el nuevo proyecto y modifique la clase CommandExtension para implementar el comando.

    Para obtener más información, vea Implementar el comando de menú.

  3. Puede agregar comandos adicionales a este proyecto mediante la definición de nuevas clases.

  4. Pruebe el comando de menú presionando F5. Para obtener más información, vea Ejecutar el comando de menú.

  5. Instale el comando de menú en otro equipo copiando el archivo bin\*\*.vsix que compila el proyecto. Para obtener más información, vea Instalar el comando de menú.

Para crear un comando de menú independiente en un proyecto de biblioteca de clases (DLL)

  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. En Plantillas instaladas, haga clic en Visual C# o Visual Basic. 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.

    Referencia

    Qué permite hacer

    System.ComponentModel.Composition

    Definir componentes mediante Managed Extensibility Framework (MEF).

    Microsoft.VisualStudio.Uml.Interfaces

    Leer y modificar las propiedades de los elementos del modelo.

    Microsoft.VisualStudio.ArchitectureTools.Extensibility

    Crear elementos del modelo y modificar formas en los diagramas.

    Microsoft.VisualStudio.Modeling.Sdk.10.0

    Definir controladores de eventos del modelo.

    Encapsular series de cambios en el modelo. Para obtener más información, vea Cómo: Vincular actualizaciones del modelo mediante transacciones.

    Microsoft.VisualStudio.Modeling.Sdk.Diagrams.10.0

    (no siempre es necesario)

    Obtener acceso a elementos del diagrama adicionales para controladores de gestos.

    Microsoft.VisualStudio.ArchitectureTools.Extensibility.Layer

    Solo se requiere para los comandos en diagramas de capas. Para obtener más información, vea Crear extensiones de diagramas de capas.

    Definir los comandos en un diagrama de capas.

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

    Nota

    Cambie el espacio de nombres, el nombre de clase y el valor devuelto por Text como prefiera.

    using System.ComponentModel.Composition;   
    using System.Linq;
    using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Presentation;
    using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml;
    using Microsoft.VisualStudio.Modeling.ExtensionEnablement;
    using Microsoft.VisualStudio.Uml.AuxiliaryConstructs;
    using Microsoft.VisualStudio.Uml.Classes; 
        // ADD other UML namespaces if required
    
    namespace UMLmenu1 // CHANGE
    {
      // DELETE any of these attributes if the command
      // should not appear in some types of diagram.
      [ClassDesignerExtension]
      [ActivityDesignerExtension]
      [ComponentDesignerExtension]
      [SequenceDesignerExtension]
      [UseCaseDesignerExtension]
      // [LayerDesignerExtension] // if you have installed Feature Pack 1
    
      // All menu commands must export ICommandExtension:
      [Export (typeof(ICommandExtension))]
      // CHANGE class name
      public class Menu1 : ICommandExtension
      {
        [Import]
        public IDiagramContext DiagramContext { get; set; }
    
        public void QueryStatus(IMenuCommand command)
        { // Set command.Visible or command.Enabled to false
          // to disable the menu command.
          command.Visible = command.Enabled = true;
        }
    
        public string Text
        {
          get { return "MENU COMMAND LABEL"; }
        }
    
        public void Execute(IMenuCommand command)
        {
          // A selection of starting points:
          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 (IElement element in modelStore.AllInstances<IClass>()) 
          { }
        }
      }
    }
    

    Para obtener más información acerca de qué poner en los métodos, vea Implementar el comando de menú.

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 comando de menú 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 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.

  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.

Implementar el comando de menú

La clase del comando de menú implementa los métodos necesarios para ICommandExtension.

string Text { get; }

Devuelve la etiqueta del elemento de menú.

void QueryStatus(IMenuCommand command);

Se llama cuando el usuario hace clic con el botón secundario del mouse en el diagrama.

Este método no debería cambiar el modelo.

Utilice DiagramContext.CurrentDiagram.SelectedShapes para especificar si desea que el comando aparezca y esté habilitado.

Establezca:

  • command.Visible en true si el comando debe aparecer en el menú cuando el usuario hace clic con el botón secundario del mouse en el diagrama.

  • command.Enabled en true si el usuario puede hacer clic en el comando del menú.

  • command.Text para establecer la etiqueta del menú dinámicamente.

void Execute (IMenuCommand command);

Se llama cuando el usuario hace clic en el elemento de menú, siempre que esté visible y habilitado.

 

Tener acceso al modelo en el código

Incluya la siguiente declaración en la clase de comando de menú:

[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 (IElement element in modelStore.AllInstances<IUseCase>()) {...}

Los elementos del modelo UML están todos disponibles a través de la API. Desde la selección actual o la raíz del modelo, puede tener acceso a todos los demás elementos. Para obtener más información, vea Cómo: Navegar por el modelo UML y Programar con la API de UML.

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

La API también le permite cambiar las propiedades de los elementos, eliminar elementos y relaciones, y crear nuevos elementos y relaciones.

De forma predeterminada, cada cambio que efectúe en el método Execute se realizará en una transacción independiente. El usuario podrá deshacer cada cambio independientemente. Si desea agrupar los cambios en una única transacción, use ILinkedUndoTransaction tal como se describe en Cómo: Vincular actualizaciones del modelo mediante transacciones.

Usar el subproceso de la interfaz de usuario en las actualizaciones

En ciertos casos, puede resultar útil hacer actualizaciones en el modelo desde un subproceso en segundo plano. Por ejemplo, si el comando carga datos de un recurso que es lento, puede realizar la carga en un subproceso en segundo plano para que el usuario pueda ver los cambios mientras se producen y cancelar la operación si fuera necesario.

Sin embargo, debe tener en cuenta que el almacén de modelos no es seguro para subprocesos. Siempre debe usar el subproceso de la interfaz de usuario para realizar las actualizaciones y, si es posible, evitar que el usuario realice modificaciones mientras la operación en segundo plano está en curso. Para obtener un ejemplo, vea Cómo: Actualizar un modelo UML a partir de un subproceso en segundo plano.

Ejecutar el comando de menú

A efectos de prueba, ejecute el comando en modo de depuración.

Para probar el comando de menú

  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 que pertenezca a uno de los tipos que aparecen en la lista de atributos de la clase del comando de menú.

  3. Haga clic con el botón secundario en cualquier parte del diagrama. El comando debería aparecer en el menú.

    Solución de problemas: si el comando no aparece en el menú, asegúrese de que:

    • El proyecto de comando de menú aparece como componente MEF en la lista Contenido de source.extensions.manifest en el proyecto VSIX.

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

    • El método QueryStatus no establece los campos command.Enabled o Visible en false.

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

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 ejemplo siguiente se muestra el código de un comando de menú que intercambiará los nombres de dos elementos en un diagrama de clases. Este código debe compilarse en un proyecto de extensión de Visual Studio e instalarse tal y como se describió en las secciones anteriores.

using System.Collections.Generic; // for IEnumerable
using System.ComponentModel.Composition;
  // for [Import], [Export]
using System.Linq; // for IEnumerable extensions
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Presentation;
  // for IDiagramContext
using Microsoft.VisualStudio.ArchitectureTools.Extensibility.Uml;
  // for designer extension attributes
using Microsoft.VisualStudio.Modeling.Diagrams;
  // for ShapeElement
using Microsoft.VisualStudio.Modeling.ExtensionEnablement;
  // for IGestureExtension, ICommandExtension, ILinkedUndoContext
using Microsoft.VisualStudio.Uml.Classes;
  // for class diagrams, packages

/// <summary>
/// Extension to swap names of classes in a class diagram.
/// </summary>
namespace SwapClassNames
{
  // Declare the class as an MEF component:
  [Export(typeof(ICommandExtension))]
  [ClassDesignerExtension]
  // Add more ExportMetadata attributes to make
  // the command appear on diagrams of other types.
  public class NameSwapper : ICommandExtension
  {
  // MEF required interfaces:
  [Import]
  public IDiagramContext Context { get; set; }
  [Import]
  public ILinkedUndoContext LinkedUndoContext { get; set; }

  /// <summary>
  /// Swap the names of the currently selected elements.
  /// </summary>
  /// <param name="command"></param>
  public void Execute(IMenuCommand command)
  {
    // Get selected shapes that are IClassifiers -
    // IClasses, IInterfaces, IEnumerators.
    var selectedShapes = Context.CurrentDiagram
     .GetSelectedShapes<IClassifier>();
    if (selectedShapes.Count() < 2) return;

    // Get model elements displayed by shapes.
    IClassifier firstElement = selectedShapes.First().Element;
    IClassifier lastElement = selectedShapes.Last().Element;

    // Do the swap in a transaction so that user
    // cannot undo one change without the other.
    using (ILinkedUndoTransaction transaction =
    LinkedUndoContext.BeginTransaction("Swap names"))
    {
    string firstName = firstElement.Name;
    firstElement.Name = lastElement.Name;
    lastElement.Name = firstName;
    transaction.Commit();
    }
  }

  /// <summary>
  /// Called by Visual Studio to determine whether
  /// menu item should be visible and enabled.
  /// </summary>
  public void QueryStatus(IMenuCommand command)
  { 
    int selectedClassifiers = Context.CurrentDiagram
     .GetSelectedShapes<IClassifier>().Count();
    command.Visible = selectedClassifiers > 0;
    command.Enabled = selectedClassifiers == 2;
  }

  /// <summary>
  /// Name of the menu command.
  /// </summary>
  public string Text
  {
    get { return "Swap Names"; }
  }
  }

}

Vea también

Otros recursos

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

Ampliar modelos y diagramas UML

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

Cómo: Definir un elemento personalizado en un cuadro de herramientas de modelado

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

Cómo: Modificar diagramas de secuencia usando la API de UML

Programar con la API de UML

Ejemplo Command to Align Shapes on a UML Diagram