BoxPanel, un ejemplo de panel personalizadoBoxPanel, an example custom panel

Aprende a escribir código para una clase Panel personalizada, con la implementación de métodos ArrangeOverride y MeasureOverride, y el uso de la propiedad Children.Learn to write code for a custom Panel class, implementing ArrangeOverride and MeasureOverride methods, and using the Children property.

API importantes: Panel, ArrangeOverride,MeasureOverrideImportant APIs: Panel, ArrangeOverride,MeasureOverride

En el código de ejemplo se muestra la implementación de un panel personalizado, pero no nos detenemos demasiado en explicar los conceptos de diseño que influyen en la forma en que un panel se puede personalizar para distintos escenarios de diseño.The example code shows a custom panel implementation, but we don't devote a lot of time explaining the layout concepts that influence how you can customize a panel for different layout scenarios. Si quieres más información sobre estos conceptos de diseño y su aplicación a escenarios concretos, consulta Introducción a los paneles personalizados de XAML.If you want more info about these layout concepts and how they might apply to your particular layout scenario, see XAML custom panels overview.

Un panel es un objeto que ofrece un comportamiento de diseño para los elementos secundarios que contiene. Este comportamiento tiene lugar cuando se ejecuta el sistema de diseño XAML y se representa la interfaz de usuario de tu aplicación.A panel is an object that provides a layout behavior for child elements it contains, when the XAML layout system runs and your app UI is rendered. Se pueden definir paneles personalizados para el diseño XAML derivando una clase personalizada de la clase Panel.You can define custom panels for XAML layout by deriving a custom class from the Panel class. De igual modo, el comportamiento del panel se proporciona reemplazando los métodos ArrangeOverride y MeasureOverride y suministrando una lógica que mida y organice los elementos secundarios.You provide behavior for your panel by overriding the ArrangeOverride and MeasureOverride methods, supplying logic that measures and arranges the child elements. Este ejemplo deriva de Panel.This example derives from Panel. Cuando se parte de Panel, los métodos ArrangeOverride y MeasureOverride carecen de un comportamiento de inicio.When you start from Panel, ArrangeOverride and MeasureOverride methods don't have a starting behavior. Tu código es el que proporciona la puerta a través de la cual los elementos secundarios se dan a conocer al sistema de diseño XAML y se representan en la interfaz de usuario.Your code is providing the gateway by which child elements become known to the XAML layout system and get rendered in the UI. Por eso, es sumamente importante que el código tenga en cuenta todos los elementos secundarios y siga los patrones que el sistema de diseño espera.So, it's really important that your code accounts for all child elements and follows the patterns the layout system expects.

Escenario de diseñoYour layout scenario

Cuando defines un panel personalizado, estás definiendo un escenario de diseño.When you define a custom panel, you're defining a layout scenario.

Un escenario de diseño se expresa del modo siguiente:A layout scenario is expressed through:

  • Con el comportamiento del panel cuando tiene elementos secundarios.What the panel will do when it has child elements
  • Cuando el panel tiene limitaciones en su espacio propio.When the panel has constraints on its own space
  • La determinación por parte de la lógica del panel de todas las medidas, posiciones de colocación y tamaños que finalmente se convertirán en un diseño de interfaz de usuario representada de elementos secundarios.How the logic of the panel determines all the measurements, placement, positions, and sizings that eventually result in a rendered UI layout of children

Teniendo esto en mente, el BoxPanel que mostramos aquí es para un escenario en particular.With that in mind, the BoxPanel shown here is for a particular scenario. Como queremos dar prioridad al código en este ejemplo, todavía no vamos a explicar el escenario en detalle, sino que nos centraremos en los pasos necesarios y en los patrones de codificación.In the interest of keeping the code foremost in this example, we won't explain the scenario in detail yet, and instead concentrate on the steps needed and the coding patterns. Si primero quieres más información sobre el escenario, ve directamente a "El escenario para BoxPanel" y, luego, regresa al código.If you want to know more about the scenario first, skip ahead to "The scenario for BoxPanel", and then come back to the code.

Derivar de Panel para empezarStart by deriving from Panel

Empezaremos derivando una clase personalizada Panel.Start by deriving a custom class from Panel. Probablemente, la forma más sencilla de hacerlo es definir un archivo de código independiente para esta clase. Para hacerlo, usamos las opciones del menú contextual Agregar | Nuevo elemento | Clase de un proyecto en el Explorador de soluciones de Microsoft Visual Studio.Probably the easiest way to do this is to define a separate code file for this class, using the Add | New Item | Class context menu options for a project from the Solution Explorer in Microsoft Visual Studio. Asigna el nombre BoxPanel a la clase (y al archivo).Name the class (and file) BoxPanel.

El archivo de plantilla de una clase no comienza con una gran cantidad de instrucciones using, ya que no está destinado específicamente a aplicaciones de Windows.The template file for a class doesn't start with many using statements because it's not specifically for Windows apps. Por lo tanto, agrega primero las instrucciones using.So first, add using statements. El archivo de plantilla también empieza con algunas instrucciones using que probablemente no necesitas y que se pueden eliminar.The template file also starts with a few using statements that you probably don't need, and can be deleted. A continuación te sugerimos una lista de instrucciones using que pueden resolver tipos que necesitarás en un código de panel personalizado típico:Here's a suggested list of using statements that can resolve types you'll need for typical custom panel code:

using System;
using System.Collections.Generic; // if you need to cast IEnumerable for iteration, or define your own collection properties
using Windows.Foundation; // Point, Size, and Rect
using Windows.UI.Xaml; // DependencyObject, UIElement, and FrameworkElement
using Windows.UI.Xaml.Controls; // Panel
using Windows.UI.Xaml.Media; // if you need Brushes or other utilities

Ahora que ya puedes resolver Panel, conviértela en la clase base de BoxPanelNow that you can resolve Panel, make it the base class of BoxPanel. Además, haz que BoxPanel sea público:Also, make BoxPanel public:

public class BoxPanel : Panel
{
}

En el nivel de clase, define algunos valores int y double que vayan a compartir algunas de las funciones de tu lógica, pero que no sea preciso exponer como API pública.At the class level, define some int and double values that will be shared by several of your logic functions, but which won't need to be exposed as public API. En el ejemplo, son estos: maxrc, rowcount, colcount, cellwidth, cellheight, maxcellheight, aspectratio.In the example, these are named: maxrc, rowcount, colcount, cellwidth, cellheight, maxcellheight, aspectratio.

Después de hacerlo, el archivo de código completo tiene el siguiente aspecto (quitando los comentarios sobre using, ahora que ya sabemos por qué estaban ahí):After you've done this, the complete code file looks like this (removing comments on using, now that you know why we have them):

using System;
using System.Collections.Generic;
using Windows.Foundation;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media;

public class BoxPanel : Panel 
{
    int maxrc, rowcount, colcount;
    double cellwidth, cellheight, maxcellheight, aspectratio;
}

A partir de aquí, te enseñaremos una definición de miembro cada vez, ya sea una invalidación de método o algo auxiliar, como una propiedad de dependencia.From here on out, we'll be showing you one member definition at a time, be that a method override or something supporting such as a dependency property. Estos elementos se pueden agregar al esqueleto anterior en cualquier orden, y no volveremos a mostrar las instrucciones using o la definición del ámbito de la clase en los fragmentos de código hasta que mostremos el código final.You can add these to the skeleton above in any order, and we won't be showing the using statements or the definition of the class scope again in the snippets until we show the final code.

MeasureOverrideMeasureOverride

protected override Size MeasureOverride(Size availableSize)
{
    Size returnSize;
    // Determine the square that can contain this number of items.
    maxrc = (int)Math.Ceiling(Math.Sqrt(Children.Count));
    // Get an aspect ratio from availableSize, decides whether to trim row or column.
    aspectratio = availableSize.Width / availableSize.Height;

    // Now trim this square down to a rect, many times an entire row or column can be omitted.
    if (aspectratio > 1)
    {
        rowcount = maxrc;
        colcount = (maxrc > 2 && Children.Count < maxrc * (maxrc - 1)) ? maxrc - 1 : maxrc;
    } 
    else 
    {
        rowcount = (maxrc > 2 && Children.Count < maxrc * (maxrc - 1)) ? maxrc - 1 : maxrc;
        colcount = maxrc;
    }

    // Now that we have a column count, divide available horizontal, that's our cell width.
    cellwidth = (int)Math.Floor(availableSize.Width / colcount);
    // Next get a cell height, same logic of dividing available vertical by rowcount.
    cellheight = Double.IsInfinity(availableSize.Height) ? Double.PositiveInfinity : availableSize.Height / rowcount;
           
    foreach (UIElement child in Children)
    {
        child.Measure(new Size(cellwidth, cellheight));
        maxcellheight = (child.DesiredSize.Height > maxcellheight) ? child.DesiredSize.Height : maxcellheight;
    }
    return LimitUnboundedSize(availableSize);
}

El patrón necesario de una implementación de MeasureOverride es el bucle que pasa por cada elemento de Panel.Children.The necessary pattern of a MeasureOverride implementation is the loop through each element in Panel.Children. Llama siempre al método Measure en cada uno de estos elementos.Always call the Measure method on each of these elements. Measure tiene un parámetro de tipo Size.Measure has a parameter of type Size. Lo que se estás pasando aquí es el tamaño que el panel va a tener disponible para el elemento secundario en cuestión.What you're passing here is the size that your panel is committing to have available for that particular child element. Por lo tanto, antes de poder efectuar el bucle y empezar a llamar a Measure, necesitaremos saber la cantidad de espacio que cada celda puede dedicar.So, before you can do the loop and start calling Measure, you need to know how much space each cell can devote. En el propio método MeasureOverride tenemos el valor availableSize.From the MeasureOverride method itself, you have the availableSize value. Se trata del tamaño que el elemento principal del panel usó cuando llamó a Measure, que era causante de que este MeasureOverride se llamara en primer lugar.That is the size that the panel's parent used when it called Measure, which was the trigger for this MeasureOverride being called in the first place. Así, una lógica típica consistiría en concebir un esquema en el que cada elemento secundario divida el espacio de todo el availableSize del panel.So a typical logic is to devise a scheme whereby each child element divides the space of the panel's overall availableSize. Luego, cada división de tamaño se pasaría a Measure en cada elemento secundario.You then pass each division of size to Measure of each child element.

La forma en la que BoxPanel divide el tamaño es bastante sencilla: divide su espacio en una serie de cuadros que se controla en gran medida mediante el número de elementos.How BoxPanel divides size is fairly simple: it divides its space into a number of boxes that's largely controlled by the number of items. El tamaño de los cuadros se establece a partir del recuento de filas y columnas y del tamaño disponible.Boxes are sized based on row and column count and the available size. Hay veces en las que una fila o una columna de un cuadrado no es necesaria y se desecha, de modo que el panel pasa a ser más un rectángulo que un cuadrado en cuanto a su relación fila:columna.Sometimes one row or column from a square isn't needed, so it's dropped and the panel becomes a rectangle rather than square in terms of its row : column ratio. Para más información sobre cómo se ha llegado hasta esta lógica, ve a "El escenario para BoxPanel".For more info about how this logic was arrived at, skip ahead to "The scenario for BoxPanel".

¿Qué es lo que hace el paso de medición?So what does the measure pass do? Establece el valor de la propiedad de solo lectura DesiredSize en cada elemento en el que se haya llamado a Measure.It sets a value for the read-only DesiredSize property on each element where Measure was called. Tener un valor de DesiredSize posiblemente sea importante al llegar al paso de organización, ya que DesiredSize indica cuál puede o debe ser el tamaño al organizar y en la representación final.Having a DesiredSize value is possibly important once you get to the arrange pass, because the DesiredSize communicates what the size can or should be when arranging and in the final rendering. Incluso si no usas DesiredSize en tu lógica, el sistema seguirá necesitándolo.Even if you don't use DesiredSize in your own logic, the system still needs it.

Este panel se puede usar cuando el componente de altura de availableSize no esté enlazado.It's possible for this panel to be used when the height component of availableSize is unbounded. Si esto es así, el panel no tiene una altura conocida que dividir.If that's true, the panel doesn't have a known height to divide. En este caso, la lógica del paso de medición informa a cada elemento secundario de que todavía carece de una altura enlazada,In this case, the logic for the measure pass informs each child that it doesn't have a bounded height, yet. y lo hace pasando un elemento Size a la llamada de Measure de los elementos secundarios en los que Size.Height es infinito.It does so by passing a Size to the Measure call for children where Size.Height is infinite. Esto puede hacerse.That's legal. Cuando se llama a Measure, la lógica consiste en que DesiredSize se establece en el mínimo de lo siguiente: lo que se pasó a Measure, o bien el tamaño natural de dicho elemento de factores como Height y Width expresamente definidos.When Measure is called, the logic is that the DesiredSize is set as the minimum of these: what was passed to Measure, or that element's natural size from factors such as explicitly-set Height and Width.

Nota

La lógica interna de StackPanel presenta el mismo comportamiento: StackPanel pasa un valor de dimensión infinito a Measure en los elementos secundarios, lo que indica que no hay ninguna limitación en ellos en cuanto a dimensión de orientación.The internal logic of StackPanel also has this behavior: StackPanel passes an infinite dimension value to Measure on children, indicating that there is no constraint on children in the orientation dimension. Normalmente, StackPanel establece su tamaño dinámicamente para dar cabida a todos los elementos secundarios de una pila que crece en esa dimensión.StackPanel typically sizes itself dynamically, to accommodate all children in a stack that grows in that dimension.

Sin embargo, el panel en sí no puede devolver un objeto Size con un valor infinito de MeasureOverride; esto generaría una excepción durante el diseño.However, the panel itself can't return a Size with an infinite value from MeasureOverride; that throws an exception during layout. Por lo tanto, parte de la lógica irá dirigida a averiguar la altura máxima que cada elemento secundario necesita para, luego, usar esa altura como altura de celda en caso de que esta no se haya obtenido ya de las propias limitaciones de tamaño del panel.So, part of the logic is to find out the maximum height that any child requests, and use that height as the cell height in case that isn't coming from the panel's own size constraints already. Aquí te mostramos la función auxiliar LimitUnboundedSize a la que se hizo referencia en el código anterior, que toma la altura de celda máxima y la usa para dar al panel una altura finita que devolver, al tiempo que garantiza que cellheight sea un número finito antes de que se inicie el paso de organización:Here's the helper function LimitUnboundedSize that was referenced in previous code, which then takes that maximum cell height and uses it to give the panel a finite height to return, as well as assuring that cellheight is a finite number before the arrange pass is initiated:

// This method is called only if one of the availableSize dimensions of measure is infinite.
// That can happen to height if the panel is close to the root of main app window.
// In this case, base the height of a cell on the max height from desired size
// and base the height of the panel on that number times the #rows.

Size LimitUnboundedSize(Size input)
{
    if (Double.IsInfinity(input.Height))
    {
        input.Height = maxcellheight * colcount;
        cellheight = maxcellheight;
    }
    return input;
}

ArrangeOverrideArrangeOverride

protected override Size ArrangeOverride(Size finalSize)
{
     int count = 1
     double x, y;
     foreach (UIElement child in Children)
     {
          x = (count - 1) % colcount * cellwidth;
          y = ((int)(count - 1) / colcount) * cellheight;
          Point anchorPoint = new Point(x, y);
          child.Arrange(new Rect(anchorPoint, child.DesiredSize));
          count++;
     }
     return finalSize;
}

El patrón necesario de una implementación de ArrangeOverride es el bucle a través de cada elemento en Panel.Children.The necessary pattern of an ArrangeOverride implementation is the loop through each element in Panel.Children. Llama siempre al método Arrange en cada uno de estos elementos.Always call the Arrange method on each of these elements.

Fíjate en que no hay tantos cálculos como en MeasureOverride. Es lo normal.Note how there aren't as many calculations as in MeasureOverride; that's typical. El tamaño de los elementos secundarios ya se conoce por la propia lógica de MeasureOverride del panel, o bien por el valor de DesiredSize de cada elemento secundario que se estableció durante el paso de medición.The size of children is already known from the panel's own MeasureOverride logic, or from the DesiredSize value of each child set during the measure pass. No obstante, todavía nos queda decidir la ubicación en la que cada elemento secundario va a aparecer en el panel.However, we still need to decide the location within the panel where each child will appear. En un panel normal, los elementos secundarios deben representarse en una posición diferente.In a typical panel, each child should render at a different position. Un panel que crea elementos superpuestos no es lo más conveniente en un escenario normal (si bien no es descartable que puedan crearse paneles que se solapen a propósito, si es realmente lo que se pretende en el escenario).A panel that creates overlapping elements isn't desirable for typical scenarios (although it's not out of the question to create panels that have purposeful overlaps, if that's really your intended scenario).

Este panel se organiza siguiendo el concepto de filas y columnas.This panel arranges by the concept of rows and columns. El número de filas y columnas ya está calculado (era necesario para la medición),The number of rows and columns was already calculated (it was necessary for measurement). así que ahora la forma de las filas y columnas, además de los tamaños conocidos de cada celda, contribuyen a la lógica para definir una posición de representación (anchorPoint) para cada elemento del panel.So now the shape of the rows and columns plus the known sizes of each cell contribute to the logic of defining a rendering position (the anchorPoint) for each element that this panel contains. Ese Point, junto con el valor de Size que ya conocemos de la medición, se usan como los dos componentes que construyen un Rect.That Point, along with the Size already known from measure, are used as the two components that construct a Rect. Rect es el tipo de entrada de Arrange.Rect is the input type for Arrange.

A veces, los paneles necesitan recortar su contenido.Panels sometimes need to clip their content. De hacerlo, el tamaño recortado es aquel que está presente en DesiredSize, dado que la lógica de Measure lo establece como el mínimo de lo que se ha pasado a Measure, o cualquier otro factor de tamaño natural.If they do, the clipped size is the size that's present in DesiredSize, because the Measure logic sets it as the minimum of what was passed to Measure, or other natural size factors. Lo normal, pues, es que no sea necesario comprobar expresamente los recortes de tamaño durante el método Arrange; el recorte sencillamente se producirá en función del pase de DesiredSize a cada llamada de Arrange.So you don't typically need to specifically check for clipping during Arrange; the clipping just happens based on passing the DesiredSize through to each Arrange call.

No siempre hay que hacer un recuento mientras se avanza por el bucle, si toda la información necesaria para definir la posición de representación se conoce por otros métodos.You don't always need a count while going through the loop if all the info you need for defining the rendering position is known by other means. Por ejemplo, en la lógica de diseño de Canvas, la posición de la colección Children es irrelevante.For example, in Canvas layout logic, the position in the Children collection doesn't matter. Toda la información necesaria para ubicar cada elemento en un Canvas se conoce con la lectura de los valores Canvas.Left y Canvas.Top de elementos secundarios como parte de la lógica de organización.All the info needed to position each element in a Canvas is known by reading Canvas.Left and Canvas.Top values of children as part of the arrange logic. Resulta que la lógica BoxPanel necesita un recuento que comparar con colcount, de modo que se sepa cuándo empezar una nueva fila y desplazar el valor de y.The BoxPanel logic happens to need a count to compare to the colcount so it's known when to begin a new row and offset the y value.

Es normal que el objeto finalSize de entrada y el objeto Size que se devuelven de una implementación de ArrangeOverride sean iguales.It's typical that the input finalSize and the Size you return from a ArrangeOverride implementation are the same. Para obtener más información sobre los motivos de esto, consulta la sección "ArrangeOverride" en Introducción a los paneles personalizados de XAML.For more info about why, see "ArrangeOverride" section of XAML custom panels overview.

Un ajuste: controlar el recuento de filas y columnasA refinement: controlling the row vs. column count

Este panel se podría compilar y utilizar tal cual está ahora.You could compile and use this panel just as it is now. Sin embargo, le vamos a agregar un ajuste más.However, we'll add one more refinement. En el código que acabas de ver, la lógica coloca la fila o columna extra en el lado más largo dentro de la relación de aspecto.In the code just shown, the logic puts the extra row or column on the side that's longest in aspect ratio. Pero, si quieres que haya un mayor control de las formas de las celdas, probablemente lo más conveniente sea decantarse por un conjunto de celdas de 4x3 en lugar de 3x4, incluso cuando la relación de aspecto del panel sea “vertical”.But for greater control over the shapes of cells, it might be desirable to choose a 4x3 set of cells instead of 3x4 even if the panel's own aspect ratio is "portrait." Por lo tanto, agregaremos una propiedad de dependencia opcional que el usuario del panel puede definir para controlar este comportamiento.So we'll add an optional dependency property that the panel consumer can set to control that behavior. A continuación te mostramos la definición de la propiedad de dependencia, que es muy básica:Here's the dependency property definition, which is very basic:

public static readonly DependencyProperty UseOppositeRCRatioProperty =
   DependencyProperty.Register("UseOppositeRCRatio", typeof(bool), typeof(BoxPanel), null);

public bool UseSquareCells
{
    get { return (bool)GetValue(UseOppositeRCRatioProperty); }
    set { SetValue(UseOppositeRCRatioProperty, value); }
}

Y así es como el uso de UseOppositeRCRatio repercute en la lógica de medición.And here's how using UseOppositeRCRatio impacts the measure logic. Realmente, lo único que hace es cambiar el modo en que rowcount y colcount se derivan de maxrc y la verdadera relación de aspecto y, a causa de esto, se producen las correspondientes diferencias de tamaño en cada celda.Really all it's doing is changing how rowcount and colcount are derived from maxrc and the true aspect ratio, and there are corresponding size differences for each cell because of that. Cuando UseOppositeRCRatio es true, invierte el valor de la verdadera relación de aspecto antes de usarla para los recuentos de filas y columnas.When UseOppositeRCRatio is true, it inverts the value of the true aspect ratio before using it for row and column counts.

if (UseOppositeRCRatio) { aspectratio = 1 / aspectratio;}

El escenario para BoxPanelThe scenario for BoxPanel

El escenario particular para BoxPanel es un panel en el que uno de los principales factores determinantes de cómo se divide el espacio consiste en conocer el número de elementos secundarios y dividir el espacio disponible existente del panel.The particular scenario for BoxPanel is that it's a panel where one of the main determinants of how to divide space is by knowing the number of child items, and dividing the known available space for the panel. La forma de los paneles es rectangular por naturaleza.Panels are innately rectangle shapes. Muchos paneles funcionan dividiendo ese espacio rectangular en más rectángulos, que es lo que Grid hace para sus celdas.Many panels operate by dividing that rectangle space into further rectangles; that's what Grid does for its cells. En el caso de Grid, el tamaño de las celdas se establece por medio de los valores de ColumnDefinition y RowDefinition, mientras que los elementos declaran la celda exacta en la que se van a situar mediante las propiedades adjuntas Grid.Row y Grid.Column.In Grid's case, the size of the cells is set by ColumnDefinition and RowDefinition values, and elements declare the exact cell they go into with Grid.Row and Grid.Column attached properties. Para lograr un buen diseño a partir de un elemento Grid, normalmente es necesario conocer el número de elementos secundarios de antemano, de modo que haya suficientes celdas y cada elemento secundario defina sus propiedades adjuntas para caber en su propia celda.Getting good layout from a Grid usually requires knowing the number of child elements beforehand, so that there are enough cells and each child element sets its attached properties to fit into its own cell.

Pero, ¿y si el número de elementos secundarios es dinámico?But what if the number of children is dynamic? Es totalmente factible: el código de tu aplicación puede agregar elementos a colecciones, como respuesta a cualquier condición de tiempo de ejecución dinámica que consideres que sea lo suficientemente importante como para actualizar la interfaz de usuario.That's certainly possible; your app code can add items to collections, in response to any dynamic run-time condition you consider to be important enough to be worth updating your UI. Si usas enlaces de datos a objetos de negocios/colecciones de respaldo, la obtención de esas actualizaciones y la actualización de la interfaz de usuario se controla automáticamente, de modo que esta es a menudo la técnica preferida (consulta el tema de Introducción al enlace de datos).If you're using data binding to backing collections/business objects, getting such updates and updating the UI is handled automatically, so that's often the preferred technique (see Data binding in depth).

Sin embargo, no todos los escenarios de aplicaciones se prestan al enlace de datos.But not all app scenarios lend themselves to data binding. A veces, es necesario crear elementos de interfaz de usuario en el tiempo de ejecución y hacerlos visibles.Sometimes, you need to create new UI elements at runtime and make them visible. BoxPanel corresponde a este escenario.BoxPanel is for this scenario. Un número variable de elementos secundarios no es un problema para BoxPanel, dado que usa el recuento de elementos secundarios en sus cálculos y ajusta los elementos secundarios tanto nuevos como existentes en un nuevo diseño para que todos tengan cabida.A changing number of child items is no problem for BoxPanel because it's using the child count in calculations, and adjusts both the existing and new child elements into a new layout so they all fit.

Un escenario avanzado para extender más aún BoxPanel (no se muestra aquí) sería incluir los elementos secundarios dinámicos y usar un DesiredSize de un elemento secundario como factor principal para asignar el tamaño de las celdas individuales.An advanced scenario for extending BoxPanel further (not shown here) could both accommodate dynamic children and use a child's DesiredSize as a stronger factor for the sizing of individual cells. Este escenario podría usar tamaños de fila o columna variables o formas que no sean de cuadrícula para que el espacio "desperdiciado" sea menor.This scenario might use varying row or column sizes or non-grid shapes so that there's less "wasted" space. Esto requiere una estrategia que permita contener en un solo rectángulo varios rectángulos de diversos tamaños y relaciones de aspecto para lograr el tamaño mínimo y un resultado estético.This requires a strategy for how multiple rectangles of various sizes and aspect ratios can all fit into a containing rectangle both for aesthetics and smallest size. BoxPanel no hace eso, sino que usa una técnica más sencilla para dividir el espacio.BoxPanel doesn't do that; it's using a simpler technique for dividing space. La técnica de BoxPanel consiste en averiguar el mínimo cuadrado que sea mayor que el recuento de elementos secundarios.BoxPanel's technique is to determine the least square number that's greater than the child count. Así, por ejemplo, nueve elementos encajarían en un cuadrado de 3x3,For example, 9 items would fit in a 3x3 square. 10 elementos necesitan un cuadrado de 4x4.10 items require a 4x4 square. No obstante, con frecuencia se pueden ajustar elementos y, al mismo tiempo, quitar una fila o una columna del cuadrado inicial para ahorrar espacio.However, you can often fit items while still removing one row or column of the starting square, to save space. En el ejemplo del recuento=10, esto encaja en un rectángulo de 4x3 o de 3x4.In the count=10 example, that fits in a 4x3 or 3x4 rectangle.

Te estarás preguntando por qué el panel no elige 5x2 para diez elementos, ya que así el número de elementos encajaría a la perfección.You might wonder why the panel wouldn't instead choose 5x2 for 10 items, because that fits the item number neatly. Pero, en la práctica, los paneles tienen forma de rectángulos que rara vez presentan una relación de aspecto con una orientación muy marcada.However, in practice, panels are sized as rectangles that seldom have a strongly oriented aspect ratio. La técnica de los mínimos cuadrados es una forma de influir en la lógica de tamaño para que funcione correctamente con las formas de diseño típicas y no fomentar los cambios de tamaño cuando las formas de celda presentan relaciones de aspecto extrañas.The least-squares technique is a way to bias the sizing logic to work well with typical layout shapes and not encourage sizing where the cell shapes get odd aspect ratios.

ReferenciaReference

ConceptosConcepts