Instrucciones para el diseño de controles con estilos

En este documento se resume un conjunto de procedimientos recomendados que se deben tener en cuenta al diseñar un control con el que pretende crear estilos y plantillas con facilidad. Este conjunto de procedimientos se obtiene de una gran serie de pruebas y errores al trabajar con estilos de control de temas para el conjunto de controles WPF integrado. Se ha observado que una aplicación de estilos correcta es tanto una función de un modelo de objeto bien diseñado como del propio estilo. Este documento está destinado a los autores de controles, y no a los autores de estilos.

Terminología

La "aplicación de estilos y las plantillas" hacen referencia al conjunto de tecnologías que permiten a un autor de controles aplazar los aspectos visuales del control al estilo y la plantilla del control. Este conjunto de tecnologías incluye:

  • estilos (incluidos los configuradores de propiedad, los desencadenadores y los guiones gráficos),

  • Recursos.

  • plantillas de control,

  • plantillas de datos.

Para obtener una introducción a los estilos y plantillas, vea Aplicar estilos y plantillas.

Antes de empezar: conocer los propios controles

Antes de empezar con estas instrucciones, es importante conocer y definir el uso común de un control propio. La aplicación de estilos brinda un conjunto de posibilidades que suele ser difícil de controlar. Los controles escritos para un uso general (es decir, en muchas aplicaciones y por parte de muchos desarrolladores) se enfrentan al desafío de que la aplicación de estilo puede utilizarse para realizar cambios de gran alcance en el aspecto visual del control. De hecho, el control con estilo puede no asemejarse ni siquiera a las intenciones del autor del control. Habida cuenta de que la aplicación de estilo suele ofrecer una flexibilidad sin límites, puede basarse en la idea del uso común para ayudar a definir el ámbito de sus decisiones.

Para entender el uso común del control, es conveniente pensar en la propuesta de valor del control. ¿Qué aporta este control a la tabla que no puede ofrecer ningún otro control? El uso común no conlleva ningún aspecto visual concreto, sino más bien la filosofía del control y un conjunto razonable de expectativas sobre su uso. Este conocimiento permite realizar algunas suposiciones sobre el modelo de composición y los comportamientos definidos por el estilo del control en el caso habitual. En el caso de ComboBox, por ejemplo, conocer el uso común no aporta ninguna información sobre si una clase ComboBox tiene esquinas redondas, pero sí sobre el hecho de que ComboBox probablemente necesita una ventana emergente y cierto nivel de alternancia si la ventana está abierta.

Directrices generales

  • No aplicar de manera estricta los contratos de plantilla. El contrato de la plantilla de un control puede constar de elementos, comandos, enlaces, desencadenadores o incluso configuraciones de propiedades necesarios o previstos para que un control funcione correctamente.

    • Minimice los contratos lo máximo posible.

    • Diseño con la expectativa de que, durante el período de diseño (es decir, al usar una herramienta de diseño), es habitual que una plantilla de control presente el estado de incompleta. WPF no ofrece una infraestructura con el estado de "redacción", por lo que los controles deben crearse con la expectativa de que dicho estado puede ser válido.

    • No lance excepciones cuando no se respeta cualquier aspecto de un contrato de plantilla. En este sentido, los paneles no deben iniciar excepciones si tienen demasiados o muy pocos elementos secundarios.

  • Funcionalidad periférica de factor en elementos del asistente con plantillas. Cada control debe centrarse en su funcionalidad principal y en su propuesta de valor verdadero, cuya definición debe realizarse según el uso común del control. Para ello, use los elementos de redacción y del asistente dentro de la plantilla, a fin de habilitar las visualizaciones y los comportamientos periféricos, es decir, aquellos comportamientos y visualizaciones que no contribuyen a la funcionalidad principal del control. Los elementos del asistente se dividen en tres categorías:

    • Los tipos del asistente independientes son controles públicos y reutilizables o primitivos que se usan de forma "anónima" en una plantilla, lo que significa que ni el elemento del asistente ni el control con estilo tienen constancia del otro. Técnicamente, cualquier elemento puede ser un tipo anónimo, pero en este contexto, el término describe los tipos que encapsulan la funcionalidad especializada para habilitar escenarios concretos.

    • Los elementos del asistente basados en tipos son nuevos tipos que encapsulan la funcionalidad especializada. Estos elementos se suelen diseñar con un intervalo de funcionalidad menor que los controles comunes o primitivos. A diferencia de los elementos del asistente independientes, los elementos del asistente basados en tipos son conscientes del contexto en el que se utilizan y normalmente deben compartir los datos con el control a cuya plantilla pertenecen.

    • Los elementos del asistente con nombre son controles comunes o primitivos que un control espera encontrar por su nombre dentro de su plantilla. A estos elementos se les asigna un nombre conocido en la plantilla, de tal manera que se permite a un control encontrar el elemento e interactuar con él mediante programación. En cualquier plantilla solo puede haber un elemento con un nombre determinado.

    En la tabla siguiente se muestran los elementos del asistente que hoy emplean los estilos del control, aunque no se trata de una lista exhaustiva:

    Elemento Tipo Usado por
    ContentPresenter Basado en tipos Button, CheckBox, RadioButton, Frame, etc. (todos los tipos ContentControl)
    ItemsPresenter Basado en tipos ListBox, ComboBox, Menu, etc. (todos los tipos ItemsControl)
    ToolBarOverflowPanel con nombre ToolBar
    Popup Independiente ComboBox, ToolBar, Menu, ToolTip, etc.
    RepeatButton con nombre Slider, ScrollBar, etc.
    ScrollBar con nombre ScrollViewer
    ScrollViewer Independiente ListBox, ComboBox, Menu, Frame, etc.
    TabPanel Independiente TabControl
    TextBox con nombre ComboBox
    TickBar Basado en tipos Slider
  • Minimizar las configuraciones de propiedad o los enlaces especificados por el usuario necesarios en los elementos del asistente. Es habitual que un elemento del asistente requiera determinados enlaces o configuraciones de propiedad para poder funcionar correctamente en una plantilla de control. En la medida de lo posible, el elemento del asistente y el control con plantilla deben establecer esta configuración. Al establecer propiedades o enlaces, hay que tener precaución de no invalidar los valores establecidos por el usuario. Los procedimientos recomendados específicos son los siguientes:

    • Los elementos del asistente con nombre deben identificarse mediante el elemento primario, y este último debe establecer la configuración necesaria en el elemento del asistente.

    • Los elementos del asistente basados en tipos deben establecer directamente en ellos mismos la configuración requerida. Para ello, es posible que el elemento del asistente deba consultar información del contexto en que se usa, incluido su TemplatedParent (el tipo de control de la plantilla en que se usa). Por ejemplo, ContentPresenter enlaza automáticamente la propiedad Content de TemplatedParent con su propiedad Content cuando se usa en un tipo derivado ContentControl.

    • No se pueden optimizar de esta forma los elementos del asistente independientes porque, por definición, ni el elemento del asistente ni el primario saben nada el uno del otro.

  • Usar la propiedad Name para marcar los elementos dentro de una plantilla. Un control que necesita buscar un elemento en su estilo para tener acceso a él mediante programación debe hacerlo mediante la propiedad Name y el paradigma FindName. Un control no debería producir una excepción cuando no se encuentra un elemento, pero de forma silenciosa y sencilla debe deshabilitar la funcionalidad que requiere ese elemento.

  • Usar procedimientos recomendados para expresar el estado del control y el comportamiento de un estilo. A continuación se presenta una lista ordenada de los procedimientos recomendados para expresar los cambios de estado del control y el comportamiento de un estilo. Debe usar el primer elemento de la lista que habilita su escenario.

    1. Enlace de propiedades. Ejemplo: enlace entre ComboBox.IsDropDownOpen y ToggleButton.IsChecked.

    2. Animaciones de propiedades o cambios de propiedades desencadenados. Ejemplo: el estado de selección mediante movimiento del mouse de una clase Button.

    3. Comando. Ejemplo: LineUpCommand / LineDownCommand en ScrollBar.

    4. Elementos del asistente independientes. Ejemplo: TabPanel en TabControl.

    5. Tipos del asistente basados en tipos. Ejemplo: ContentPresenter en Button, TickBar en Slider.

    6. Elementos del asistente con nombre. Ejemplo: TextBox en ComboBox.

    7. Eventos traspasados desde los tipos del asistente con nombre. Si realiza escuchas de eventos traspasados desde un elemento de estilo, debe requerir que el elemento que genera el evento se pueda identificar de manera exclusiva. Ejemplo: Thumb en ToolBar.

    8. Comportamiento personalizado de OnRender. Ejemplo: ButtonChrome en Button.

  • Usar desencadenadores de estilo (a diferencia de los desencadenadores de plantilla) con moderación. Los desencadenadores que afectan a las propiedades de elementos en la plantilla se deben declarar en la plantilla. Los desencadenadores que afectan a las propiedades del control (no TargetName) se pueden declarar en el estilo, a menos que sepa que el cambio de la plantilla también destruye el desencadenador.

  • Ser coherente con los patrones de estilo existentes. Muchas veces, hay varias formas de resolver un problema. Debe conocer los patrones de aplicación de estilos de control existentes y, cuando sea posible, mantener la coherencia con ellos. Esto es especialmente importante para los controles que derivan del mismo tipo base (por ejemplo, ContentControl, ItemsControl, RangeBase, etc.).

  • Exponer propiedades para habilitar escenarios de personalización comunes sin volver a crear plantillas. WPF no admite partes acoplables/personalizables, por lo que un usuario de control solo puede usar dos métodos de personalización: configurar propiedades directamente o configurar propiedades con estilos. Habida cuenta de lo anterior, resulta conveniente exponer un número limitado de propiedades orientadas a escenarios de personalización muy comunes y de alta prioridad, ya que, de lo contrario, sería necesario volver a crear plantillas. A continuación se presentan procedimientos recomendados para saber cuándo y cómo habilitar escenarios de personalización:

    • Las personalizaciones muy comunes deben exponerse como propiedades en el control, a fin de que la plantilla pueda usarlas.

    • Las personalizaciones menos comunes (aunque no poco habituales) deben exponerse como propiedades adjuntas que la plantilla debe usar.

    • Resulta aceptable que sea necesario volver a crear plantillas en el caso de las personalizaciones conocidas pero poco habituales.

Consideraciones sobre temas

  • Los estilos de temas deben tratar de tener una semántica de propiedades coherente en todos los temas, pero sin garantizarlo. Como parte de su documentación, el control debe tener un documento en que se describa la semántica de la propiedad del control, es decir, el "significado" de una propiedad para un control. Por ejemplo, el control ComboBox debe definir el significado de la propiedad Background en ComboBox. Los estilos predeterminados del control deben intentar seguir la semántica definida en ese documento en todos los temas. Por otro lado, los usuarios del control deben ser conscientes de que la semántica de la propiedad puede cambiar de un tema a otro. En algunos casos, es posible que una propiedad determinada no pueda expresarse dentro de las restricciones visuales requeridas por un tema concreto. (Por ejemplo, el tema Clásico no tiene un solo borde en el que Thickness se puede aplicar a muchos controles).

  • Los estilos de temas no necesitan tener una semántica de desencadenadores coherente en todos los temas. El comportamiento expuesto por un estilo de control mediante desencadenadores o animaciones puede variar de un tema a otro. Los usuarios del control deben ser conscientes de que un control no usará necesariamente el mismo mecanismo para lograr un comportamiento concreto en todos los temas. Un tema, por ejemplo, puede usar una animación para expresar el comportamiento de selección mediante movimiento del mouse mientras que otro tema usa un desencadenador. Esto puede provocar incoherencias en la conservación del comportamiento en los controles personalizados. (Cambiar la propiedad de fondo, por ejemplo, podría no afectar al estado de selección del control si ese estado se expresa mediante un desencadenador. Sin embargo, si el estado de selección se implementa mediante una animación, cambiar a fondo podría interrumpir la animación de forma irremediable y, por tanto, la transición de estado).

  • Los estilos de temas no necesitan tener una semántica de "diseño" coherente en todos los temas. Por ejemplo, el estilo predeterminado no necesita garantizar que un control puede ocupar el mismo tamaño en todos los temas o garantizar que un control tendrá los mismos márgenes de contenido o espaciado interno en todos los temas.

Consulte también