Instrucciones para el diseño de controles con estilos
Actualización: noviembre 2007
Este documento resume un conjunto de procedimientos recomendados que se debe tener en cuenta al diseñar un control con estilos y plantillas de fácil uso. Los vimos a través de muchas pruebas y errores al trabajar en los estilos del control de tema del conjunto del control integrado de WPF. Aprendimos que un estilo correcto está formado tanto por una función de un modelo de objetos bien diseñado como por el propio estilo. El lector al que está dirigido este documento es el autor del control, y no el autor del estilo.
Este tema contiene las secciones siguientes.
- Terminología
- Antes de empezar: introducción al control
- Instrucciones generales
- Consideraciones del tema
- Temas relacionados
Terminología
"Diseño de estilos y plantillas" hace referencia al conjunto de tecnologías que permiten al autor de un control transferir los aspectos visuales de éste a su estilo y plantilla. Este conjunto de tecnologías incluye los siguientes elementos:
Estilos (incluidos los establecedores de propiedades, los desencadenadores y los guiones gráficos).
Recursos.
Plantillas de control.
Plantillas de datos.
Para obtener una introducción al diseño de estilos y plantillas, vea Aplicar estilos y plantillas.
Antes de empezar: introducción al control
Antes de pasar a las instrucciones, es importante entender el control y haber definido su uso común. El diseño de estilos expone un conjunto de posibilidades que suele ser difícil de controlar. Los controles que se escriben para usarlos mucho (en muchas aplicaciones, por muchos programadores) se enfrentan al desafío de que el diseño de estilos se puede utilizar para efectuar cambios difíciles de conseguir en el aspecto visual del control. De hecho, puede que el control con estilos no se parezca en absoluto a la idea inicial de su autor. Puesto que la flexibilidad proporcionada por los estilos es básicamente ilimitada, puede usar la idea de uso común para ayudarle a delimitar el ámbito de sus decisiones.
Para entender el uso común del control, es práctico pensar en la finalidad del mismo. ¿Qué aporta el control a la tabla que no pueda aportar ningún otro control? El uso común no implica ningún aspecto visual concreto, sino más bien la filosofía del control y un conjunto razonable de expectativas de uso. Esta comprensión permite plantearse algunas suposiciones sobre el modelo de la composición y los comportamientos definidos por estilo del control en el caso común. En el caso de ComboBox, por ejemplo, entender el uso común no le ayudará a formarse una idea sobre si un objeto ComboBox concreto tiene las esquinas redondeadas, pero sí lo hará sobre el hecho de que ComboBox, probablemente, necesite una ventana emergente y algo de alternancia si está abierto.
Instrucciones generales
No aplique el proceso de desarrollo de la plantilla de manera estricta. El proceso de desarrollo de la plantilla de un control podría estar formado por elementos, comandos, enlaces, desencadenadores o incluso los valores de propiedades necesarios o esperados de un control para que funcione correctamente.
Simplifique el desarrollo al máximo posible.
El diseño basado en las expectativas del tiempo de diseño (es decir, al usar una herramienta de diseño) tiene como resultado una plantilla de control cuyo estado es incompleto. WPF no proporciona ninguna infraestructura de estado de "creación"; por tanto, los controles se deben crear con la expectativa de que tal estado pudiera ser válido.
No produzca excepciones cuando no se siga un aspecto cualquiera de un diseño de plantilla. En estas líneas, los paneles no deben producir excepciones si tienen demasiados elementos secundarios o muy pocos.
Tenga en cuenta la funcionalidad periférica de los elementos de la aplicación auxiliar de la plantilla. Los controles se deben basar en su funcionalidad básica y en propuestas reales, y definir mediante el uso común del control. Para ello, utilice los elementos de creación y de la aplicación auxiliar en la plantilla para habilitar comportamientos y visualizaciones periféricos; es decir, los comportamientos y visualizaciones que no contribuyen a la funcionalidad básica del control. Los elementos de la aplicación auxiliar se clasifican en tres categorías:
Los tipos de la aplicación auxiliar independientes son controles públicos y reutilizables o primitivos que se usan "anónimamente" en una plantilla; es decir, ni el elemento de la aplicación auxiliar ni el control con estilo se dan cuenta de la existencia del otro. Técnicamente, el tipo de cualquier elemento puede ser anónimo pero, en este contexto, el término describe los tipos que encapsulan la funcionalidad especializada para habilitar escenarios concretos.
Los elementos de aplicación auxiliar basados en tipo 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 de la aplicación auxiliar independientes, los elementos de aplicación auxiliar basados en tipo son conscientes del contexto en el que se usan y, normalmente, deben compartir los datos con el control a cuya plantilla pertenecen.
Los elementos de aplicación auxiliar con nombre son los controles comunes o primitivos que espera un control para buscar por nombre en su plantilla. A estos elementos se les asigna un nombre conocido en la plantilla para que, de esta forma, el control busque el elemento e interactúe con él mediante programación. Puede haber sólo uno elemento con un nombre concreto en una plantilla cualquiera.
En la tabla siguiente, se muestran los elementos de aplicación auxiliar usados por los estilos de control actuales (esta lista no es una lista completa):
Elemento
Tipo
Utilizado por
Basado en tipo
Button, CheckBox, RadioButton, Frame, etc. (todos los tipos de ContentControl)
Basado en tipo
ListBox, ComboBox, Menu, etc. (todos los tipos de ItemsControl)
Con nombre
Independiente
Con nombre
Con nombre
Independiente
Independiente
Con nombre
Basado en tipo
Reduzca el número necesario de enlaces especificados por el usuario o los valores de las propiedades de los elementos de la aplicación auxiliar. Es normal que un elemento de aplicación auxiliar requiera algunos valores de enlaces o de propiedades para funcionar correctamente en la plantilla del control. El elemento de la aplicación auxiliar y el control con plantilla deben establecer estos valores en la medida de lo posible. Al establecer propiedades o enlaces, cuidado, se debe tener cuidado para no invalidar los valores establecidos por el usuario. Los procedimientos recomendados específicos son los siguientes:
Los elementos de la aplicación auxiliar con nombre los debe identificar el elemento primario y éste debe establecer cualquier valor necesario del elemento de la aplicación auxiliar.
Los elementos de la aplicación auxiliar basados en tipo deben establecer los valores necesarios directamente en sí mismos. De esta forma, quizá el elemento de la aplicación auxiliar deba consultar el contexto de la información en la que se usa, incluido su TemplatedParent (el tipo de control de la plantilla en la que se utiliza). Por ejemplo, ContentPresenter enlaza automáticamente la propiedad Content de su TemplatedParent a su propiedad Content cuando se usa en un tipo derivado de ContentControl.
Los elementos de la aplicación auxiliar independientes no se pueden optimizar de esta manera porque, por definición, ni el elemento de aplicación auxiliar ni el primario saben que existe el otro.
Use la propiedad Name para marcar los elementos de una plantilla. Un control que necesite buscar un elemento en su estilo para tener acceso a él mediante programación debe hacerlo con la propiedad Name y el ejemplo de FindName. Un control no debe producir ninguna excepción cuando no se encuentre un elemento, sino deshabilitar correctamente la funcionalidad que requería ese elemento.
Utilice los procedimientos recomendados para expresar el estado del control y el comportamiento de un estilo. A continuación, se muestra una lista ordenada de los procedimientos recomendados para expresar los cambios del estado del control y de comportamiento de un estilo. Debe utilizar el primer elemento de la lista que habilita su escenario.
Enlace de Propiedad. Ejemplo: enlace entre ComboBox.IsDropDownOpen y ToggleButton.IsChecked.
Cambios de propiedad desencadenados o animaciones de propiedad. Ejemplo: estado de desplazamiento del puntero de Button.
Comando. Ejemplo: LineUpCommand / LineDownCommand de ScrollBar.
Elementos independientes de la aplicación auxiliar. Ejemplo: TabPanel de TabControl.
Tipos de aplicación auxiliar basados en tipo. Ejemplo: ContentPresenter de Button, TickBar de Slider.
Elementos con nombre de la aplicación auxiliar. Ejemplo: TextBox de ComboBox.
Eventos traspasados desde los tipos con nombre de la aplicación auxiliar. Si realiza escuchas de eventos traspasados desde un elemento de estilo, es necesario que se pueda identificar exclusivamente el elemento que genera el evento. Ejemplo: Thumb de ToolBar.
Personalice el comportamiento de OnRender. Ejemplo: ButtonChrome de Button.
Utilice desencadenadores de estilo (en contraposición a los desencadenadores de plantilla) con moderación. Los desencadenadores que afectan las propiedades de los elementos de la plantilla se deben declarar en la plantilla. Los desencadenadores que afectan las propiedades del control (sin TargetName) se pueden declarar en el estilo a menos que sepa que al cambiar la plantilla, también se destruye el desencadenador.
Sea coherente con los modelos de estilo existentes. Muchas veces existen varias formas de resolver un problema. Tenga en cuenta y sea coherente con los modelos de los estilos de un control existente en la medida de lo posible. Esto es especialmente importante para los controles que se derivan del mismo tipo base (por ejemplo, ContentControl, ItemsControl, RangeBase, etc.).
Exponga las propiedades para habilitar los escenarios de personalización comunes sin volver a crear plantillas. WPF no admite las partes que se pueden conectar o personalizar; por tanto, al usuario de un control le quedan sólo dos métodos de personalización: establecer las propiedades directamente o mediante estilos. Teniendo esto presente, es correcto exponer un número limitado de propiedades cuyo destino sean escenarios de personalización muy comunes y prioritarios que, de otro modo, requerirían volver a crear las plantillas. A continuación se detallan los procedimientos recomendados para el momento en el que se habilitan los escenarios de personalización y cómo se habilitan:
Las personalizaciones muy comunes se deben exponer como propiedades del control y las debe usar la plantilla.
Las personalizaciones menos comunes (aunque no raras) se deben exponer como propiedades adjuntas y las debe usar la plantilla.
Es aceptable que las personalizaciones conocidas pero raras requieran que se vuelva a crear la plantilla.
Consideraciones del tema
Los estilos del tema deben intentar ser coherentes con la semántica de la propiedad en todos los temas, pero sin garantizarlo. Como parte de su documentación, el control debe tener un documento que describa la semántica de la propiedad del control; es decir, el "significado" de una propiedad de un control. Por ejemplo, el control ComboBox debe definir el significado de la propiedad Background dentro de ComboBox. Los estilos predeterminados del control deben intentar seguir la semántica definida en ese documento en todos los temas. Los usuarios del control, por otra parte, deben tener en cuenta que la semántica de la propiedad puede cambiar de un tema a otro. En algunos casos, una propiedad especificada no se puede expresar en las restricciones visuales requeridas por un tema concreto. (Por ejemplo, el tema Clásico no tiene ni un solo borde al que se pueda aplicar Thickness para muchos controles.)
Los estilos del tema no tienen que ser coherentes con la semántica del desencadenador en todos los temas. El comportamiento expuesto por un estilo de control a través de los desencadenadores o animaciones puede variar de un tema a otro. Los usuarios del control deben tener en cuenta que un control no usará necesariamente el mismo mecanismo para lograr un comportamiento concreto en todos los temas. Por ejemplo, un tema puede usar una animación para expresar el comportamiento de desplazamiento del puntero mientras que otro usa un desencadenador. De esta forma, se pueden producir incoherencias en la conservación del comportamiento en los controles personalizados. (Si se cambia la propiedad de segundo plano, por ejemplo, puede que no afecte el estado de desplazamiento del puntero del control si dicho estado se expresa con un desencadenador. Sin embargo, si este estado se implementa utilizando una animación, al cambiar a segundo plano, se podría interrumpir irremediablemente la animación y, por consiguiente, la transición de estado.)
Los estilos del tema no tiene que ser coherentes con la semántica del "diseño" en todos los temas. Por ejemplo, el estilo predeterminado no tiene que garantizar que un control vaya a tener el mismo tamaño en todos los temas ni que un control vaya a tener los mismos márgenes o relleno de contenido en todos los temas.