Compartir a través de


Personalizar la ventana Propiedades

Puede personalizar la apariencia y el comportamiento de la ventana Propiedades en el lenguaje específico de dominio (DSL) en Visual Studio. En la definición de DSL, define las propiedades de dominio en cada clase de dominio. De forma predeterminada, al seleccionar una instancia de la clase, ya sea en un diagrama o en el Explorador de modelos, todas las propiedades de dominio se muestran en la ventana Propiedades. Esto le permite ver y editar los valores de las propiedades de dominio, incluso si no los ha asignado a campos de forma en el diagrama.

Nombres, descripciones y categorías

Nombre y Nombre para mostrar. En la definición de una propiedad de dominio, el Nombre para mostrar de la propiedad es el nombre que aparece en tiempo de ejecución en la ventana Propiedades. Por el contrario, el Nombre se usa al escribir código de programa para actualizar la propiedad. El Nombre debe ser un nombre alfanumérico CLR correcto, pero el Nombre para mostrar puede contener espacios.

Al establecer el Nombre de una propiedad en la definición de DSL, su Nombre para mostrar se establece automáticamente en una copia del Nombre. Si escribe un nombre con grafía Pascal como "FuelGauge", el Nombre para mostrar contendrá automáticamente un espacio: "Fuel Gauge". Sin embargo, puede establecer explícitamente el Nombre para mostrar en otro valor.

Descripción. La Descripción de una propiedad de dominio aparece en dos lugares:

  • En la parte inferior de la ventana Propiedades cuando el usuario selecciona la propiedad. Puede usarla para explicar al usuario qué representa la propiedad.

  • En el código de programa generado. Si usa los servicios de documentación para extraer la documentación de API, aparecerá como la descripción de esta propiedad en la API.

Categoría. Una categoría es un encabezado en la ventana Propiedades.

Exponer características de estilo

Algunas de las características dinámicas de los elementos gráficos se pueden representar o exponer como propiedades de dominio. El usuario puede actualizar una característica que se ha expuesto de esta manera, y puede actualizarse más fácilmente por medio de código de programa.

Haga clic con el botón derecho en una clase de forma en la definición de DSL, elija Agregar expuestos y, a continuación, elija una característica.

En las formas puede exponer las propiedades FillColor, OutlineColor, TextColor, OutlineDashStyle, OutlineThickness y FillGradientMode. En los conectores puede exponer las propiedades Color,TextColor, DashStyle y Thickness. En los diagramas puede exponer las propiedades FillColor y TextColor.

Cuando el usuario de su DSL selecciona un elemento en un modelo, las propiedades de ese elemento se muestran en la ventana Propiedades. Sin embargo, también puede mostrar las propiedades de los elementos relacionados especificados. Esto resulta útil si ha definido un grupo de elementos que funcionan juntos. Por ejemplo, podría definir un elemento principal y un elemento de complemento opcional. Si el elemento principal se asigna a una forma y el otro no, resulta útil ver todas sus propiedades como si estuvieran en un elemento.

Este efecto se denomina reenvío de propiedades y sucede automáticamente en varios casos. En otros casos, puede lograr el reenvío de propiedades definiendo un descriptor de tipo de dominio.

Casos de reenvío de propiedades predeterminados

Cuando el usuario selecciona una forma o un conector, o bien un elemento en el Explorador, se muestran las siguientes propiedades en la ventana Propiedades:

  • Propiedades de dominio definidas en la clase de dominio del elemento de modelo, incluidas las definidas en las clases base. Una excepción son las propiedades de dominio para las que ha establecido Is Browsable en False.

  • Nombres de elementos vinculados a través de relaciones que tienen una multiplicidad de 0..1. Esto proporciona un método práctico para ver elementos vinculados opcionalmente, incluso si no ha definido una asignación del conector para la relación.

  • Propiedades de dominio de la relación de inclusión que tiene como destino el elemento. Dado que las relaciones de inclusión normalmente no se muestran explícitamente, esto permite al usuario ver sus propiedades.

  • Propiedades de dominio definidas en la forma o el conector seleccionados.

Agregar reenvío de propiedades

Para reenviar una propiedad, define un descriptor de tipo de dominio. Si tiene una relación de dominio entre dos clases de dominio, puede usar un descriptor de tipo de dominio para establecer una propiedad de dominio en la primera clase en el valor de una propiedad de dominio en la segunda clase de dominio. Por ejemplo, si tiene una relación entre una clase de dominio Libro y una clase de dominio Autor, puede usar un descriptor de tipo de dominio para hacer que la propiedad Nombre del Autor de un Libro aparezca en la ventana Propiedades cuando el usuario seleccione el Libro.

Nota

El reenvío de propiedades solo afecta a la ventana Propiedades cuando el usuario edita un modelo. No define una propiedad de dominio en la clase receptora. Si desea acceder a la propiedad de dominio reenviada en otras partes de la definición de DSL o en el código de programa, debe acceder al elemento de reenvío.

En el procedimiento siguiente se da por hecho que ha creado un DSL. En los primeros pasos se resumen los requisitos previos.

Reenviar una propiedad desde otro elemento

  1. Cree una solución de Herramientas del lenguaje específico de dominio que contenga al menos dos clases, que en este ejemplo se denominan Libro y Autor. Debe haber una relación, sea del tipo que sea, entre Libro y Autor.

    La multiplicidad del rol de origen (el rol del lado Libro) debe ser 0..1 o 1..1, de modo que cada Libro tenga un Autor.

  2. En el Explorador de DSL, haga clic con el botón derecho en la clase de dominio Libro y, a continuación, haga clic en Agregar nuevo DomainTypeDescriptor.

    Aparecerá un nodo denominado Rutas de acceso de descriptores de propiedad personalizada en el nodo Descriptor de tipo personalizado.

  3. Haga clic con el botón derecho en el nodo Descriptor de tipo personalizado y, a continuación, haga clic en Agregar nueva PropertyPath.

    Aparecerá una nueva ruta de acceso de la propiedad en el nodo Rutas de acceso de descriptores de propiedad personalizada.

  4. Seleccione la nueva ruta de acceso de la propiedad y, en la ventana Propiedades, establezca Ruta de acceso a la propiedad en la ruta de acceso del elemento de modelo adecuado.

    Para editar la ruta de acceso en una vista de árbol, haga clic en la flecha abajo situada a la derecha de esta propiedad. Para obtener más información sobre las rutas de acceso de dominio, consulte Sintaxis de las rutas de dominio. Cuando la haya editado, la ruta de acceso debe ser similar a BookReferencesAuthor.Author/!Author.

  5. Establezca Propiedad en la propiedad de dominio Nombre de Autor.

  6. Establezca Nombre para mostrar en Nombre del autor.

  7. Transforme todas las plantillas, compile y ejecute el DSL.

  8. En un diagrama de modelos, cree un libro, un autor y vincúlelos mediante la relación de referencia. Seleccione el elemento de libro y, en la ventana Propiedades, debería ver Nombre del autor, además de las propiedades del libro. Cambie el nombre del autor vinculado o vincule el libro a otro autor y observe que cambia el Nombre del autor del libro.

Editores de propiedades de carácter personalizado

La ventana de propiedades proporciona una experiencia de edición predeterminada adecuada para el tipo de cada propiedad de dominio. Por ejemplo, para un tipo enumerado, el usuario ve una lista desplegable y, para una propiedad numérica, el usuario puede escribir dígitos. Esto solo es cierto para los tipos integrados. Si especifica un tipo externo, el usuario podrá ver los valores de la propiedad, pero no editarla.

Sin embargo, puede especificar los siguientes editores y tipos:

  1. Otro editor usado con un tipo estándar. Por ejemplo, podría especificar un editor de ruta de acceso de archivo para una propiedad de cadena.

  2. Un tipo externo y un editor para la propiedad de dominio.

  3. Un editor de .NET como el editor de ruta de acceso de archivo, o puede crear su propio editor de propiedades de carácter personalizado.

    Una conversión entre un tipo externo y un tipo como String, que tiene un editor predeterminado.

    En un DSL, un tipo externo es cualquier tipo que no sea uno de los tipos simples (como Boolean o Int32) o String.

Definir una propiedad de dominio que tenga un tipo externo

  1. En el Explorador de soluciones, agregue una referencia al ensamblado (DLL) que contiene el tipo externo, en el proyecto Dsl.

    El ensamblado puede ser un ensamblado .NET o un ensamblado que suministre.

  2. Agregue el tipo a la lista Tipos de dominio, a menos que ya lo haya hecho.

    1. Abra DslDefinition.dsl y, en el Explorador de DSL, haga clic con el botón derecho en el nodo raíz y, a continuación, haga clic en Agregar nuevo tipo externo.

      Aparecerá una nueva entrada en el nodo Tipos de dominio.

      Advertencia

      El elemento de menú está en el nodo raíz de DSL, no en el nodo Tipos de dominio.

    2. Establezca el nombre y el espacio de nombres del nuevo tipo en la ventana Propiedades.

  3. Agregue una propiedad de dominio a una clase de dominio de la manera habitual.

    En la ventana Propiedades, seleccione el tipo externo en la lista desplegable del campo Tipo.

    En esta fase, los usuarios pueden ver los valores de la propiedad, pero no pueden editarla. Los valores mostrados se obtienen de la función ToString(). Puede escribir código de programa que establezca el valor de la propiedad, por ejemplo, en un comando o regla.

Establecer un editor de propiedades

Agregue un atributo CLR a la propiedad de dominio de la siguiente forma:

[System.ComponentModel.Editor (
   typeof(AnEditor),
   typeof(System.Drawing.Design.UITypeEditor))]

Puede establecer el atributo en una propiedad mediante la entrada Atributo personalizado en la ventana Propiedades.

El tipo de AnEditor debe derivarse del tipo especificado en el segundo parámetro. El segundo parámetro debe ser UITypeEditor o ComponentEditor. Para obtener más información, vea EditorAttribute.

Puede especificar su propio editor o un editor de .NET, como FileNameEditor o ImageEditor. Por ejemplo, use el procedimiento siguiente para tener una propiedad en la que el usuario pueda escribir un nombre de archivo.

Definir una propiedad de dominio de nombre de archivo

  1. Agregue una propiedad de dominio a una clase de dominio en la definición de DSL.

  2. Seleccione la nueva propiedad. En el campo Atributo personalizado de la ventana Propiedades, escriba el atributo siguiente. Para escribir este atributo, haga clic en los puntos suspensivos [...] y escriba el nombre de atributo y los parámetros por separado:

    [System.ComponentModel.Editor (
       typeof(System.Windows.Forms.Design.FileNameEditor)
       , typeof(System.Drawing.Design.UITypeEditor))]
    
    
  3. Deje el Tipo de la propiedad de dominio en su valor predeterminado de String.

  4. Para probar el editor, compruebe que los usuarios pueden abrir el editor de nombres de archivo para editar la propiedad de dominio.

    1. Presione CTRL+F5 o F5. En la solución de depuración, abra un archivo de prueba. Cree un elemento de la clase de dominio y selecciónelo.

    2. En la ventana Propiedades, seleccione la propiedad de dominio. El campo de valor muestra puntos suspensivos [...].

    3. Haga clic en los puntos suspensivos. Aparecerá un cuadro de diálogo de archivo. Seleccione un archivo y cierre el cuadro de diálogo. Ahora la ruta de acceso del archivo es el valor de la propiedad de dominio.

Definir su propio editor de propiedades

Puede definir su propio editor. Lo haría para permitir al usuario editar un tipo que haya definido o editar un tipo estándar de una manera especial. Por ejemplo, podría permitir al usuario escribir una cadena que represente una fórmula.

Para definir un editor, escriba una clase derivada de UITypeEditor. La clase debe invalidar:

  • EditValue, para interactuar con el usuario y actualizar el valor de propiedad.

  • GetEditStyle, para especificar si el editor abrirá un cuadro de diálogo o proporcionará un menú desplegable.

También puede proporcionar una representación gráfica del valor de la propiedad que se mostrará en la cuadrícula de propiedades. Para ello, invalide GetPaintValueSupported y PaintValue. Para obtener más información, vea UITypeEditor.

Nota

Agregue el código en un archivo de código independiente en el proyecto Dsl.

Por ejemplo:

internal class TextFileNameEditor : System.Windows.Forms.Design.FileNameEditor
{
  protected override void InitializeDialog(System.Windows.Forms.OpenFileDialog openFileDialog)
  {
    base.InitializeDialog(openFileDialog);
    openFileDialog.Filter = "Text files(*.txt)|*.txt|All files (*.*)|*.*";
    openFileDialog.Title = "Select a text file";
  }
}

Para usar este editor, establezca el Atributo personalizado de una propiedad de dominio en:

[System.ComponentModel.Editor (
   typeof(MyNamespace.TextFileNameEditor)
   , typeof(System.Drawing.Design.UITypeEditor))]

Para obtener más información, vea UITypeEditor.

Proporcionar una lista desplegable de valores

Puede proporcionar una lista de valores para que un usuario elija.

Nota

Esta técnica proporciona una lista de valores que pueden cambiar en tiempo de ejecución. Si desea proporcionar una lista que no cambie, considere la posibilidad de usar en su lugar un tipo enumerado como el tipo de la propiedad de dominio.

Para definir una lista de valores estándar, agregue a la propiedad de dominio un atributo CLR que tenga el siguiente formato:

[System.ComponentModel.TypeConverter
(typeof(MyTypeConverter))]

Defina una clase que se derive de TypeConverter. Agregue el código en un archivo independiente en el proyecto Dsl. Por ejemplo:

/// <summary>
/// Type converter that provides a list of values
/// to be displayed in the property grid.
/// </summary>
/// <remarks>This type converter returns a list
/// of the names of all "ExampleElements" in the
/// current store.</remarks>
public class MyTypeConverter : System.ComponentModel.TypeConverter
{
  /// <summary>
  /// Return true to indicate that we return a list of values to choose from
  /// </summary>
  /// <param name="context"></param>
  public override bool GetStandardValuesSupported
    (System.ComponentModel.ITypeDescriptorContext context)
  {
    return true;
  }

  /// <summary>
  /// Returns true to indicate that the user has
  /// to select a value from the list
  /// </summary>
  /// <param name="context"></param>
  /// <returns>If we returned false, the user would
  /// be able to either select a value from
  /// the list or type in a value that is not in the list.</returns>
  public override bool GetStandardValuesExclusive
      (System.ComponentModel.ITypeDescriptorContext context)
  {
    return true;
  }

  /// <summary>
  /// Return a list of the values to display in the grid
  /// </summary>
  /// <param name="context"></param>
  /// <returns>A list of values the user can choose from</returns>
  public override StandardValuesCollection GetStandardValues
      (System.ComponentModel.ITypeDescriptorContext context)
  {
    // Try to get a store from the current context
    // "context.Instance"  returns the element(s) that
    // are currently selected i.e. whose values are being
    // shown in the property grid.
    // Note that the user could have selected multiple objects,
    // in which case context.Instance will be an array.
    Store store = GetStore(context.Instance);

    List<string> values = new List<string>();

    if (store != null)
    {
      values.AddRange(store.ElementDirectory
        .FindElements<ExampleElement>()
        .Select<ExampleElement, string>(e =>
      {
        return e.Name;
      }));
    }
    return new StandardValuesCollection(values);
  }

  /// <summary>
  /// Attempts to get to a store from the currently selected object(s)
  /// in the property grid.
  /// </summary>
  private Store GetStore(object gridSelection)
  {
    // We assume that "instance" will either be a single model element, or
    // an array of model elements (if multiple items are selected).

    ModelElement currentElement = null;

    object[] objects = gridSelection as object[];
    if (objects != null && objects.Length > 0)
    {
      currentElement = objects[0] as ModelElement;
    }
    else
    {
        currentElement = gridSelection as ModelElement;
    }

    return (currentElement == null) ? null : currentElement.Store;
  }

}