Nota
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Un panel es un objeto que proporciona un comportamiento de diseño para los elementos secundarios que contiene, cuando se ejecuta el sistema de diseño Extensible Application Markup Language (XAML) y se representa la interfaz de usuario de la aplicación.
APIs importantes: Panel, ArrangeOverride, MeasureOverride
Puedes definir paneles personalizados para el diseño XAML derivando una clase personalizada de la clase Panel . Para proporcionar comportamiento a tu panel, reescribe los métodos MeasureOverride y ArrangeOverride, proporcionando lógica que mida y arregle los elementos secundarios.
La clase base del Panel
Para definir una clase de panel personalizada, puede derivar de la clase Panel directamente o derivar de alguna de las clases de panel prácticas que no están selladas, como Grid o StackPanel. Es más sencillo partir de Panel, ya que puede ser difícil superar la lógica de diseño existente de un panel que ya tiene un comportamiento de diseño. Además, un panel con comportamiento podría tener propiedades existentes que no son relevantes para las características de diseño del panel.
Desde Panel, su panel personalizado hereda estas APIs:
- La propiedad Niños.
- Las propiedades Background, ChildrenTransitions, IsItemsHost, y los identificadores de propiedad de dependencia. Ninguna de estas propiedades es virtual, por lo que normalmente no se sobrescriben ni se reemplazan. Normalmente no necesita estas propiedades para escenarios de panel personalizados, ni siquiera para leer valores.
- Los métodos de invalidación de diseño MeasureOverride y ArrangeOverride. Estos se definieron originalmente mediante FrameworkElement. La clase base Panel no invalida estas funciones, pero los paneles prácticos como Grid sí tienen implementaciones de invalidación que se implementan como código nativo y son ejecutadas por el sistema. Proporcionar nuevas (o adicionales) implementaciones para ArrangeOverride y MeasureOverride constituye la mayor parte del esfuerzo necesario para definir un panel personalizado.
- Todas las demás APIs de FrameworkElement, UIElement y DependencyObject, como Height, Visibility y así sucesivamente. A veces se hace referencia a los valores de estas propiedades en las anulaciones de diseño, pero no son virtuales, por lo que normalmente no se reemplazan ni se sobrescriben.
Este enfoque aquí es describir los conceptos de diseño XAML, por lo que puedes considerar todas las posibilidades de cómo un panel personalizado puede y debe comportarse en el diseño. Si prefiere sumergirse directamente y ver un ejemplo de implementación de panel personalizado, vea BoxPanel, un ejemplo de panel personalizado.
La propiedad Children
La propiedad Children es relevante para un panel personalizado porque todas las clases derivadas de Panel usan la propiedad Children como el lugar para almacenar sus elementos secundarios contenidos en una colección. Children se designa como la propiedad de contenido XAML para la clase Panel, y todas las clases derivadas de Panel pueden heredar el comportamiento de la propiedad de contenido XAML. Si una propiedad se designa como la propiedad de contenido de XAML, esto significa que el marcado XAML puede omitir un elemento de propiedad al especificar dicha propiedad en el marcado, y los valores se establecen como elementos secundarios de marcado inmediatos (el "contenido"). Por ejemplo, si deriva una clase denominada CustomPanel del Panel que no define ningún comportamiento nuevo, puede seguir usando este marcado:
<local:CustomPanel>
<Button Name="button1"/>
<Button Name="button2"/>
</local:CustomPanel>
Cuando un analizador XAML lee este marcado, se sabe que Children es la propiedad de contenido XAML para todos los tipos derivados de Panel, por lo que el analizador agregará los dos elementos Button al valor UIElementCollection de la propiedad Children. La propiedad de contenido de XAML facilita una relación padre-hijo simplificada en el marcado XAML para definir una interfaz de usuario. Para obtener más información sobre las propiedades de contenido XAML y cómo se rellenan las propiedades de la colección cuando se analiza XAML, consulta la guía de sintaxis XAML.
El tipo de colección que mantiene el valor de la propiedad Children es la clase UIElementCollection. UIElementCollection es una colección fuertemente tipada que usa UIElement como tipo de elemento aplicado. UIElement es un tipo base heredado por cientos de tipos prácticos de elementos de interfaz de usuario, por lo que el control de tipos aquí es intencionalmente flexible. Sin embargo, se aplica que no se puede tener un Brush como elemento secundario directo de un Panel, y, por lo general, significa que solo los elementos que se espera que sean visibles en la interfaz de usuario y que participen en el diseño se encontrarán como elementos secundarios en un Panel.
Normalmente, un panel personalizado acepta cualquier elemento secundario UIElement mediante una definición XAML, usando simplemente las características de la propiedad Children as-is. Como un escenario avanzado, podría apoyar una verificación adicional de tipos de los elementos secundarios al iterar por la colección en las sobrescrituras de diseño.
Además de recorrer en bucle la colección de hijos de
Invalidación de los métodos de diseño
El modelo básico para los métodos de invalidación de diseño (MeasureOverride y ArrangeOverride) es que deben recorrer en iteración todos los elementos secundarios y llamar al método de diseño específico de cada elemento secundario. El primer ciclo de diseño se inicia cuando el sistema de diseño XAML establece el objeto visual para la ventana raíz. Dado que cada padre invoca el diseño en sus hijos, esto propaga una invocación a los métodos de diseño a cada elemento de la interfaz de usuario que debería formar parte de un diseño. En el diseño XAML, hay dos fases: medir y, a continuación, organizar.
No obtienes ningún comportamiento de método de diseño incorporado para MeasureOverride y ArrangeOverride de la clase base Panel. Los elementos de Children no se representarán automáticamente como parte del árbol visual XAML. Depende de usted hacer que los elementos sean reconocidos por el proceso de layout, invocando métodos de layout en cada uno de los elementos que se encuentran en Children a través de un pase de layout dentro de sus implementaciones de MeasureOverride y ArrangeOverride.
No hay ninguna razón para llamar a implementaciones base en invalidaciones de diseño a menos que tenga su propia herencia. Los métodos nativos para el comportamiento de diseño (si existen) se ejecutan independientemente, y no llamar a la implementación base desde invalidaciones no impedirá que se produzca el comportamiento nativo.
Durante el paso de medida, la lógica de diseño consulta a cada elemento hijo para conocer su tamaño deseado, llamando al método Medir en ese elemento hijo. Al llamar al método Measure de
Durante el paso de organización, las posiciones y tamaños de los elementos secundarios se determinan en el espacio x-y y la composición de diseño se prepara para el renderizado. El código debe llamar a Organizar en cada elemento hijo de Children para que el sistema de diseño detecte que el elemento pertenece al diseño. La llamada Arrange es un precursor de la composición y representación; informa al sistema de disposición dónde va ese elemento, al enviarse la composición para su representación.
Muchas propiedades y valores contribuyen a cómo funcionará la lógica de diseño en tiempo de ejecución. Una manera de pensar en el proceso de diseño es que los elementos sin elementos secundarios (generalmente el elemento más profundamente anidado de la interfaz de usuario) son los que pueden finalizar primero las medidas. No tienen dependencias de los elementos secundarios que influyen en su tamaño deseado. Es posible que tengan sus propios tamaños deseados, y estas son sugerencias de tamaño mientras el diseño no se haya realizado. A continuación, el paso de medida continúa recorriendo el árbol visual hasta llegar al elemento raíz, de modo que este tenga sus medidas y todas las medidas puedan ser finalizadas.
El diseño candidato debe ajustarse a la ventana de la aplicación actual o se recortarán partes de la interfaz de usuario. Los paneles suelen ser el lugar donde se determina la lógica de recorte. La lógica de panel puede determinar qué tamaño está disponible desde la implementación de MeasureOverride, y puede ser necesario aplicar las restricciones de tamaño a los subelementos y dividir el espacio entre los subelementos para que todo se acomode de la mejor manera posible. Idealmente, el resultado de un diseño debe utilizar diversas propiedades de todas sus partes y, al mismo tiempo, ajustarse dentro de la ventana de la aplicación. Esto requiere una buena implementación para la lógica de diseño de los paneles y también un diseño de interfaz de usuario sensato en parte de cualquier código de aplicación que compile una interfaz de usuario con ese panel. Ningún diseño de panel será bueno si el diseño general de la interfaz de usuario incluye más elementos secundarios de los que posiblemente caben en la aplicación.
En gran medida, lo que hace que funcione el sistema de diseño es que cualquier elemento basado en FrameworkElement ya tiene su propio comportamiento inherente al actuar como un elemento hijo en un contenedor. Por ejemplo, hay varias APIs de FrameworkElement que influyen en el comportamiento del diseño o son necesarias para que el diseño funcione correctamente. Estos incluyen:
- DesiredSize (realmente una propiedad de UIElement)
- AlturaReal y AnchoReal
- Altura y Anchura
- margen
- evento LayoutUpdated
- AlineaciónHorizontal y AlineaciónVertical
- ArrangeOverride y MeasureOverride métodos
- Organizar y métodos Measure: tienen implementaciones nativas definidas en el nivel de FrameworkElement, que manejan la acción de diseño a nivel de elemento.
MedidaSobrecarga
El método MeasureOverride tiene un valor de retorno que el sistema de diseño usa como el tamaño inicial DesiredSize para el propio panel, cuando el método Measure es llamado en el panel por su elemento primario en el diseño. Las opciones lógicas dentro del método son tan importantes como lo que devuelve y la lógica suele influir en qué valor se devuelve.
Todas las implementaciones de deben recorrer a través de Childreny llamar al método Measure en cada elemento hijo. Al llamar al método Measure de
Este es un esqueleto muy básico de un método MeasureOverride:
protected override Size MeasureOverride(Size availableSize)
{
Size returnSize; //TODO might return availableSize, might do something else
//loop through each Child, call Measure on each
foreach (UIElement child in Children)
{
child.Measure(new Size()); // TODO determine how much space the panel allots for this child, that's what you pass to Measure
Size childDesiredSize = child.DesiredSize; //TODO determine how the returned Size is influenced by each child's DesiredSize
//TODO, logic if passed-in Size and net DesiredSize are different, does that matter?
}
return returnSize;
}
Los elementos suelen tener un tamaño natural en el momento en que están listos para el diseño. Después del paso de la medida, el desiredSize de
Algunos elementos no tienen un tamaño natural porque tienen valores Auto para Height y Width. Estos elementos utilizan el availableSizecompleto, ya que eso es lo que representa un valor de automático: ajustar el tamaño del elemento al máximo tamaño disponible, que el padre de diseño inmediato comunica al llamar a Measure con availableSize. En la práctica, siempre hay alguna medida a la que una interfaz de usuario se dimensiona (incluso si es la ventana de nivel superior). Finalmente, el paso de medida resuelve todos los valores de Auto a las restricciones del padre y todos los elementos de Auto obtienen medidas reales (lo que puede obtener verificando ActualWidth y ActualHeight, una vez que el diseño se completa).
Está permitido pasar un tamaño a Medida que tiene al menos una dimensión infinita, para indicar que el panel puede intentar adecuar su tamaño para adaptarse a las medidas de su contenido. Cada elemento hijo que se mide fija su valor de tamaño deseado utilizando su tamaño natural. A continuación, durante el pase de organización, el panel normalmente se organiza utilizando ese tamaño.
Los elementos de texto como TextBlock tienen una ActualWidth calculada y ActualHeight en función de su cadena de texto y propiedades de texto, incluso si no se establece ningún valor Height o Width, y estas dimensiones deben respetarse por su lógica de panel. El recorte de texto es una experiencia de usuario en la interfaz particularmente mala.
Incluso si la implementación no usa las medidas de tamaño deseadas, es mejor llamar al método Measure en cada elemento secundario, ya que comportamientos internos y nativos son desencadenados al llamar a Measure. Para que un elemento participe en el diseño, se debe llamar a Measure en cada elemento secundario durante el paso de medida y se debe llamar al método Arrange durante el paso de colocación. Al llamar a estos métodos, se establecen marcas internas en el objeto y se rellenan valores (como la propiedad DesiredSize) que la lógica de diseño del sistema necesita cuando compila el árbol visual y renderiza la interfaz de usuario.
El valor que devuelve MeasureOverride se basa en la lógica del panel que interpreta el DesiredSize u otras consideraciones de tamaño para cada uno de los elementos secundarios en Children cuando Measure es llamado sobre ellos. Qué hacer con los valores de DesiredSize de los elementos secundarios y cómo debe utilizar el valor devuelto de MeasureOverride depende de la interpretación de su propia lógica. Normalmente no sumas los valores sin modificación, ya que la entrada de MeasureOverride suele ser un tamaño disponible fijo sugerido por el padre del panel. Si se supera ese tamaño, el panel mismo podría recortarse. Normalmente, usted podría comparar el tamaño total de los elementos secundarios con el tamaño disponible del panel y realizaría ajustes si fuera necesario.
Sugerencias e instrucciones
- Idealmente, un panel personalizado debe ser adecuado para ser el primer objeto visual verdadero en una composición de la interfaz de usuario, quizás en un nivel inmediatamente bajo Page, UserControl u otro elemento que sea la raíz de la página XAML. En las implementaciones de MeasureOverride de , no devuelva de forma rutinaria el tamaño de de entrada sin examinar los valores. Si el valor devuelto Size tiene un valor de Infinity, esto puede producir excepciones en la lógica de diseño en tiempo de ejecución. Un valor de Infinity puede provenir de la ventana principal de la aplicación, que es desplazable y, por lo tanto, no tiene una altura máxima. Otro contenido desplazable puede tener el mismo comportamiento.
- Otro error común en las implementaciones de MeasureOverride es devolver un nuevo Size predeterminado con un valor de altura y ancho de 0. Puede comenzar con ese valor, e incluso podría ser el valor correcto si el panel determina que no se debe renderizar ninguno de los elementos secundarios. Sin embargo, un tamaño predeterminado da como resultado que el panel no se ajuste correctamente al tamaño establecido por su anfitrión. No solicita espacio en la interfaz de usuario y, por tanto, no obtiene espacio ni se muestra. Es posible que todo el código del panel funcione correctamente, pero todavía no verá el panel ni su contenido si se está componiendo con altura y anchura cero.
- Dentro de las invalidaciones, evite la tentación de convertir los elementos secundarios en FrameworkElement y utilizar propiedades que se calculan como resultado del diseño, especialmente ActualWidth y ActualHeight. En los escenarios más comunes, puede basar la lógica en el valor de DesiredSize del elemento secundario
y no necesitará ninguno de los oHeight propiedades relacionadas de un elemento secundario. En los casos especializados, donde conoce el tipo de elemento y tiene información adicional, por ejemplo, el tamaño natural de un archivo de imagen, puede usar la información especializada del elemento porque no es un valor que los sistemas de diseño modifican activamente. La inclusión de propiedades calculadas por el diseño como parte de la lógica de diseño aumenta considerablemente el riesgo de definir un bucle de diseño no intencional. Estos bucles provocan una condición en la que no se puede crear un diseño válido y el sistema puede producir una excepción LayoutCycleException si el bucle no se puede recuperar.Width - Los paneles normalmente dividen su espacio disponible entre varios elementos secundarios, aunque la forma en que el espacio se divide varía. Por ejemplo, Grid implementa la lógica de diseño que usa sus valores de RowDefinition y ColumnDefinition para dividir el espacio en las celdas de Grid, admitiendo tanto tamaños en estrellas como en píxeles. Si son valores de píxeles, el tamaño disponible para cada hijo ya se conoce, por lo que eso es lo que se utiliza como tamaño de entrada para una cuadrícula estilo Measure.
- Los propios paneles pueden introducir espacio reservado para el relleno entre elementos. Si haces esto, asegúrate de exponer las medidas como una propiedad distinta de los márgenes o cualquier propiedad de relleno .
- Los elementos pueden tener valores para sus ActualWidth y ActualHeight propiedades basados en una ejecución de diseño previa. Si cambian los valores, el código de la interfaz de usuario de la aplicación puede colocar controladores para LayoutUpdated en elementos si hay lógica especial para ejecutarse, pero la lógica del panel normalmente no necesita comprobar los cambios con el control de eventos. El sistema de diseño ya está realizando las determinaciones de cuándo volver a ejecutar el diseño porque un valor de propiedad relevante para el diseño ha cambiado y las MeasureOverride de un panel o ArrangeOverride se llaman automáticamente en las circunstancias adecuadas.
DisposiciónSobrescribir
El método
No devuelva un tamaño de
Todas las implementaciones de ArrangeOverride de
Este es un esqueleto muy básico de un método ArrangeOverride :
protected override Size ArrangeOverride(Size finalSize)
{
//loop through each Child, call Arrange on each
foreach (UIElement child in Children)
{
Point anchorPoint = new Point(); //TODO more logic for topleft corner placement in your panel
// for this child, and based on finalSize or other internal state of your panel
child.Arrange(new Rect(anchorPoint, child.DesiredSize)); //OR, set a different Size
}
return finalSize; //OR, return a different Size, but that's rare
}
El paso de organización del diseño puede ocurrir sin ir precedido por un pase de medida. Sin embargo, esto solo ocurre cuando el sistema de diseño ha determinado que no han cambiado propiedades que habrían afectado a las medidas anteriores. Por ejemplo, si cambia una alineación, no es necesario volver a medir ese elemento determinado porque su DesiredSize no cambiaría cuando cambie su elección de alineación. Por otro lado, si ActualHeight cambia en cualquier elemento de un diseño, se necesita un nuevo paso de medida. El sistema de diseño detecta automáticamente los cambios de medida verdaderos e invoca de nuevo el pase de medida y, a continuación, ejecuta otro pase de organización.
La entrada para Arrange tiene un valor de tipo Rect. La forma más común de construir este Rect es usar el constructor que tiene como entrada un Punto y una entrada de Tamaño . El punto es el punto en el que se debe colocar la esquina superior izquierda de la caja delimitadora del elemento. Size es las dimensiones que se usan para representar ese elemento en particular. A menudo usas el DesiredSize para ese elemento como este valor de Size, ya que establecer el DesiredSize para todos los elementos implicados en el diseño era el propósito del paso de medida del diseño. (El paso de medida determina todo el tamaño de los elementos de forma iterativa para que el sistema de diseño pueda optimizar cómo se colocan los elementos una vez que llegue al paso de organización).
Lo que normalmente varía entre ArrangeOverride implementaciones es la lógica por la que el panel determina el componente de punto de cómo organiza cada hijo. Un panel de posicionamiento absoluto, como Canvas, usa la información de colocación explícita que obtiene de cada elemento mediante los valores de Canvas.Left y Canvas.Top. Un panel de división de espacio, como Grid , tendría operaciones matemáticas que dividieron el espacio disponible en celdas y cada celda tendría un valor x-y para donde se debe colocar y organizar su contenido. Un panel adaptable como StackPanel podría expandirse para ajustarse al contenido en su dimensión de orientación.
Todavía hay influencias de posicionamiento adicionales en los elementos del diseño, más allá de lo que controlas y pasas directamente a Arrange. Estos proceden de la ejecución nativa interna de Arrange que es común a todos los tipos derivados de FrameworkElement y que se complementa con otros tipos, como los elementos de texto. Por ejemplo, los elementos pueden tener margen y alineación, y algunos pueden tener relleno. Estas propiedades suelen interactuar. Para obtener más información, consulta alineación, margen y relleno.
Paneles y controles
Evite colocar la funcionalidad en un panel personalizado que en su lugar se debe compilar como un control personalizado. La función de un panel es presentar cualquier contenido de los elementos secundarios que existan dentro de él, como parte de un diseño que se realiza automáticamente. El panel puede agregar decoraciones al contenido (similar a cómo un borde agrega el borde alrededor del elemento que presenta) o realizar otros ajustes relacionados con el diseño, como relleno. Pero eso es lo que debe ir al extender la salida del árbol visual más allá de la generación de informes y el uso de información de los elementos secundarios.
Si hay alguna interacción accesible para el usuario, debe escribir un control personalizado, no un panel. Por ejemplo, un panel no debe agregar ventanillas de desplazamiento al contenido que presenta, incluso si el objetivo es evitar el recorte, ya que las barras de desplazamiento, los pulgares, etc. son elementos de control interactivos. (Es posible que el contenido tenga barras de desplazamiento después de todo, pero debe dejárselo a la lógica del elemento secundario. No lo fuerce añadiendo desplazamiento como una operación de diseño). Puede crear un control y también escribir un panel personalizado que desempeñe un papel importante en el árbol visual de dicho control, a la hora de mostrar contenido en el mismo. Pero el control y el panel deben ser objetos de código distintos.
Una razón por la que la distinción entre el control y el panel es importante es debido a la automatización de la interfaz de usuario de Microsoft y la accesibilidad. Los paneles proporcionan un comportamiento de diseño visual, no un comportamiento lógico. La apariencia visual de un elemento de interfaz de usuario no es un aspecto de la interfaz de usuario que suele ser importante para los escenarios de accesibilidad. La accesibilidad consiste en exponer las partes de una aplicación que son lógicamente importantes para comprender una interfaz de usuario. Cuando se requiere interacción, los controles deben exponer las posibilidades de interacción a la infraestructura de Automatización de la interfaz de usuario. Para obtener más información, consulte pares de automatización personalizada.
Otra API de diseño
Existen algunas otras API que son parte del sistema de diseño, pero no están declaradas por Panel. Puede usarlos en una implementación de panel o en un control personalizado que use paneles.
- UpdateLayout, InvalidateMeasurey InvalidateArrange son métodos que inician un paso de disposición. InvalidateArrange podría no iniciar un proceso de medición, pero los otros dos sí lo hacen. Nunca llame a estos métodos desde un método de diseño superpuesto, porque seguro que provocarían un bucle de diseño. Normalmente, el código de control no necesita llamarlos. La mayoría de los aspectos del diseño se desencadenan automáticamente mediante la detección de cambios en las propiedades de diseño definidas por el marco, como Width , etc.
- LayoutUpdated es un evento que se desencadena cuando ha cambiado algún aspecto del diseño del elemento. Esto no es específico de los paneles; FrameworkElement define el evento.
- SizeChanged es un evento que se desencadena solo después de que se finalicen los pasos de diseño e indica que ActualHeight o ActualWidth han cambiado como resultado. Este es otro evento FrameworkElement. Hay casos en los que se activa LayoutUpdated, pero SizeChanged no. Por ejemplo, el contenido interno podría reorganizarse, pero el tamaño del elemento no cambió.
Temas relacionados
Referencia
Conceptos