Información general sobre elementos Panel

Actualización: noviembre 2007

Los elementos Panel son componentes que controlan la representación de los elementos: su tamaño y dimensiones, su posición y la disposición de su contenido secundario. Windows Presentation Foundation (WPF) proporciona varios elementos Panel predefinidos, así como la posibilidad de construir elementos Panel personalizados.

Este tema contiene las secciones siguientes.

  • La clase Panel

  • Miembros comunes de los elementos Panel

  • Elementos Panel derivados

  • Paneles de interfaz de usuario

  • Elementos Panel anidados

  • Elementos Panel personalizados

  • Compatibilidad para la localización y globalización

  • Temas relacionados

La clase Panel

Panel es la clase base para todos los elementos que proporcionan la compatibilidad de diseño en Windows Presentation Foundation (WPF). Los elementos Panel derivados se utilizan para colocar y organizar UIElements en Lenguaje de marcado de aplicaciones extensible (XAML) y en el código.

WPF incluye un conjunto completo de implementaciones de panel derivadas que permiten numerosos diseños complejos. Estas clases derivadas exponen propiedades y métodos que habilitan la mayoría de los escenarios de interfaz de usuario (UI) estándar. Los programadores que no encuentren un comportamiento de disposición secundario acorde con sus necesidades pueden crear diseños nuevos que invaliden los métodos ArrangeOverride y MeasureOverride. Para obtener más información sobre los comportamientos de diseño personalizados, vea Elementos Panel personalizados.

XAMLPad

XAMLPad es una herramienta que facilita el desarrollo de interfaces de usuario basadas en Lenguaje de marcado de aplicaciones extensible (XAML) analizando el marcado en tiempo real y mostrando los resultados en una ventana. Esta herramienta es muy útil para experimentar con el diseño. Es posible que desee tener la aplicación abierta para ver los resultados de los diversos ejemplos de Lenguaje de marcado de aplicaciones extensible (XAML) incluidos en este tema. Para obtener más información, vea XAMLPad.

Miembros comunes de los elementos Panel

Todos los elementos Panel son compatibles con las propiedades básicas de posición y dimensionamiento definidas por FrameworkElement, como son Height, Width, HorizontalAlignment, VerticalAlignment, Margin y LayoutTransform. Para obtener información adicional sobre las propiedades de posición definidas por FrameworkElement, vea Información general sobre alineación, márgenes y relleno.

Panel expone propiedades adicionales que son de importancia crítica para entender y utilizar el diseño. La propiedad Background se utiliza para rellenar el área existente entre los límites de un elemento de panel derivado con un objeto Brush. Children representa la colección secundaria de UIElements que forman el elemento Panel. InternalChildren representa el contenido de la colección Children junto con los miembros generados mediante el enlace de datos. Ambos se componen de una colección UIElementCollection de elementos secundarios hospedados en el elemento Panel primario.

El panel también expone una propiedad ZIndex que se puede utilizar para lograr un orden dispuesto en capas en un elemento Panel derivado. Los miembros de la colección Children de un panel cuyo valor de ZIndex es mayor aparecen delante de los que tienen un valor de ZIndex menor. Esto es especialmente útil para los paneles como Canvas y Grid, que permiten a los elementos secundarios compartir el mismo espacio de coordenadas.

Panel también define el método OnRender, que se puede utilizar para invalidar el comportamiento de presentación predeterminado de un elemento Panel.

Propiedades asociadas

Los elementos de panel derivados utilizan mucho las propiedades asociadas. Una propiedad asociada es una forma especializada de propiedad de dependencia que no tiene el "contenedor" de propiedades de common language runtime (CLR) convencional. Las propiedades asociadas tienen una sintaxis especializada en Lenguaje de marcado de aplicaciones extensible (XAML), que se puede ver en algunos de los ejemplos que siguen.

Uno de los propósitos de una propiedad asociada es permitir a los elementos secundarios almacenar valores únicos de una propiedad que en realidad está definida por un elemento principal. Una aplicación de esta funcionalidad consiste en tener elementos secundarios que informen al elemento primario sobre cómo desean ser presentados en la interfaz de usuario (UI), lo que resulta sumamente útil para el diseño de la aplicación. Para obtener más información, vea Información general sobre propiedades asociadas.

Elementos Panel derivados

Muchos objetos se derivan de Panel, pero no todos ellos están pensados para usarlos como proveedores de diseño raíz. Existen seis clases de panel definidas (Canvas, DockPanel, Grid, StackPanel, VirtualizingStackPanel y WrapPanel) que están diseñadas específicamente para crear la interfaz de usuario de las aplicaciones.

Cada elemento de panel encapsula su propia funcionalidad especial, como se muestra en la tabla siguiente.

Nombre del elemento

¿Es un panel de interfaz de usuario?

Descripción

Canvas

Define un área en la que pueden colocarse explícitamente los elementos secundarios utilizando las coordenadas relativas al área del control Canvas.

DockPanel

Define un área en la que se pueden organizar horizontal o verticalmente los elementos secundarios, uno respecto al otro.

Grid

Define un área de cuadrícula flexible que se compone de columnas y filas. Los elementos secundarios de un control Grid se pueden colocar con precisión utilizando la propiedad Margin.

StackPanel

Organiza los elementos secundarios en una única línea que se puede orientar horizontal o verticalmente.

TabPanel

No

Controla el diseño de los botones de las fichas de un control TabControl.

ToolBarOverflowPanel

No

Organiza el contenido dentro de un control ToolBar.

UniformGrid

No

UniformGrid se utiliza para organizar los elementos secundarios en una cuadrícula cuyas celdas tienen todas el mismo tamaño.

VirtualizingPanel

No

Proporciona una clase base para los paneles que pueden "virtualizar" su colección de elementos secundarios.

VirtualizingStackPanel

Organiza y virtualiza el contenido en una sola línea orientada horizontal o verticalmente.

WrapPanel

WrapPanel organiza los elementos secundarios secuencialmente de izquierda a derecha y traslada el contenido a la línea siguiente cuando alcanza el borde del cuadro contenedor. La clasificación siguiente se realiza secuencialmente de arriba abajo o de izquierda a derecha, en función del valor de la propiedad Orientation.

Paneles de interfaz de usuario

Existen seis clases de paneles disponibles en WPF que están optimizadas para admitir los escenarios de la interfaz de usuario: Canvas, DockPanel, Grid, StackPanel, VirtualizingStackPanel y WrapPanel. Estos elementos de panel son fáciles utilizar, versátiles y suficientemente extensibles para la mayoría de las aplicaciones.

Cada elemento Panel derivado trata las restricciones de tamaño de forma distinta. La comprensión del modo en que un elemento Panel controla las restricciones en la dirección horizontal o vertical puede hacer que el diseño sea más predecible.

Nombre del panel

Dimensión x

Dimensión y

Canvas

Restringida al contenido

Restringida al contenido

DockPanel

Restringida

Restringida

StackPanel (Orientación vertical)

Restringida

Restringida al contenido

StackPanel (Orientación horizontal)

Restringida al contenido

Restringida

Grid

Restringida

Restringida, excepto en los casos en que Auto se aplica a las filas y las columnas

WrapPanel

Restringida al contenido

Restringida al contenido

A continuación se ofrecen descripciones más detalladas y ejemplos del uso de cada uno de estos elementos.

Canvas

El elemento Canvas permite la colocación del contenido de acuerdo con unas coordenadas x e y absolutas. Los elementos se pueden dibujar en una ubicación única; o, si ocupan las mismas coordenadas, el orden en que aparecen en el marcado determina el orden en el que se dibujan.

Canvas proporciona la compatibilidad de diseño más flexible de todos los elementos Panel. Las propiedades Height y Width se utilizan para definir el área del lienzo, y a los elementos que contiene se les asignan coordenadas absolutas relativas al área del elemento Canvas primario. Cuatro propiedades asociadas, Left, Top, Right y Bottom, permiten obtener un control muy preciso de la posición de los objetos dentro de un elemento Canvas, y gracias a ellas el programador puede colocar y organizar con precisión los elementos en la pantalla.

ClipToBounds dentro de un lienzo

Canvas puede colocar los elementos secundarios en cualquier posición de la pantalla, incluso en coordenadas situadas fuera de sus propias dimensiones, definidas mediante Height y Width. Además, el tamaño de sus propios elementos secundarios no afecta a Canvas. Como resultado, es posible que un elemento secundario se dibuje sobre otros elementos situados fuera del rectángulo delimitador del elemento Canvas primario. El comportamiento predeterminado de un elemento Canvas es permitir que se dibujen elementos secundarios fuera de los límites del elemento Canvas primario. Si no desea que esto ocurra, puede establecer la propiedad ClipToBounds en true. Esto hace que Canvas recorte a su propio tamaño. Canvas es el único elemento de diseño que permite dibujar los elementos secundarios fuera sus límites.

Este comportamiento se muestra gráficamente en Ejemplo Width Properties Comparison.

Definir y utilizar un lienzo

Es posible crear instancias de un elemento Canvas simplemente con Lenguaje de marcado de aplicaciones extensible (XAML) o mediante código. En el ejemplo siguiente se muestra cómo utilizar Canvas para colocar el contenido de forma absoluta. Este código genera tres cuadrados de 100 píxeles. El primer cuadrado es rojo y su posición superior izquierda (x, y ) se especifica como (0, 0). El segundo cuadrado es verde y su posición superior izquierda es (100, 100), que es inmediatamente debajo y a la derecha del primer cuadrado. El tercer cuadrado es azul y su posición superior izquierda es (50, 50), por lo que cubre el cuadrante inferior derecho del primer cuadrado y el cuadrante superior izquierdo del segundo. Dado que el tercer cuadrado se coloca en último lugar, parece que está encima de los otros dos cuadrados; es decir, las partes que se superponen adoptan el color del tercer cuadrado.

WindowTitle = "Canvas Sample"
'Create a Canvas as the root Panel
Dim myParentCanvas As New Canvas()
myParentCanvas.Width = 400
myParentCanvas.Height = 400

' Define child Canvas elements
Dim myCanvas1 As New Canvas()
myCanvas1.Background = Brushes.Red
myCanvas1.Height = 100
myCanvas1.Width = 100
Canvas.SetTop(myCanvas1, 0)
Canvas.SetLeft(myCanvas1, 0)

Dim myCanvas2 As New Canvas()
myCanvas2.Background = Brushes.Green
myCanvas2.Height = 100
myCanvas2.Width = 100
Canvas.SetTop(myCanvas2, 100)
Canvas.SetLeft(myCanvas2, 100)

Dim myCanvas3 As New Canvas()
myCanvas3.Background = Brushes.Blue
myCanvas3.Height = 100
myCanvas3.Width = 100
Canvas.SetTop(myCanvas3, 50)
Canvas.SetLeft(myCanvas3, 50)

' Add child elements to the Canvas' Children collection
myParentCanvas.Children.Add(myCanvas1)
myParentCanvas.Children.Add(myCanvas2)
myParentCanvas.Children.Add(myCanvas3)

' Add the parent Canvas as the Content of the Window Object
Me.Content = myParentCanvas
// Create the application's main window
mainWindow = new Window ();
mainWindow.Title = "Canvas Sample";

// Create the Canvas
myParentCanvas = new Canvas();
myParentCanvas.Width = 400;
myParentCanvas.Height = 400;

// Define child Canvas elements
myCanvas1 = new Canvas();
myCanvas1.Background = Brushes.Red;
myCanvas1.Height = 100;
myCanvas1.Width = 100;
Canvas.SetTop(myCanvas1, 0);
Canvas.SetLeft(myCanvas1, 0);

myCanvas2 = new Canvas();
myCanvas2.Background = Brushes.Green;
myCanvas2.Height = 100;
myCanvas2.Width = 100;
Canvas.SetTop(myCanvas2, 100);
Canvas.SetLeft(myCanvas2, 100);

myCanvas3 = new Canvas();
myCanvas3.Background = Brushes.Blue;
myCanvas3.Height = 100;
myCanvas3.Width = 100;
Canvas.SetTop(myCanvas3, 50);
Canvas.SetLeft(myCanvas3, 50);

// Add child elements to the Canvas' Children collection
myParentCanvas.Children.Add(myCanvas1);
myParentCanvas.Children.Add(myCanvas2);
myParentCanvas.Children.Add(myCanvas3);

// Add the parent Canvas as the Content of the Window Object
mainWindow.Content = myParentCanvas;
mainWindow.Show ();

<Page WindowTitle="Canvas Sample" xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation">
  <Canvas Height="400" Width="400">
    <Canvas Height="100" Width="100" Top="0" Left="0" Background="Red"/>
    <Canvas Height="100" Width="100" Top="100" Left="100" Background="Green"/>
    <Canvas Height="100" Width="100" Top="50" Left="50" Background="Blue"/>
  </Canvas>
</Page>

La aplicación compilada produce una nueva interfaz de usuario con esta apariencia.

Elemento Canvas típico.

DockPanel

El elemento DockPanel utiliza la propiedad asociada Dock para colocar el contenido a lo largo de los bordes de un contenedor. Cuando Dock se establece en Top o Bottom, coloca los elementos secundarios por encima o por debajo de los demás. Cuando Dock se establece en Left o Right, coloca los elementos secundarios a la izquierda o a la derecha de los demás. La propiedad LastChildFill determina la posición del último elemento agregado como un elemento secundario de un elemento DockPanel.

Puede utilizar DockPanel para colocar un grupo de controles relacionados, como un conjunto de botones. También puede utilizarlo para crear una interfaz de usuario "con paneles", similar a la de Microsoft Outlook.

Ajustar el tamaño al contenido

Si no se especifican las propiedades Height y Width, DockPanel se ajusta a su contenido. Su tamaño puede aumentar o disminuir de acuerdo con el tamaño de sus elementos secundarios. Sin embargo, cuando se especifican estas propiedades y ya no hay sitio para el siguiente elemento secundario especificado, DockPanel no muestra ese elemento secundario ni los elementos secundarios subsiguientes, y no mide los elementos secundarios subsiguientes.

LastChildFill

De forma predeterminada, el último elemento secundario de un elemento DockPanel "rellenará" el espacio restante que quede sin asignar. Si no desea que esto ocurra, establezca la propiedad LastChildFill en false.

Definir y utilizar un elemento DockPanel

En el ejemplo siguiente se muestra cómo dividir el espacio mediante DockPanel. Se agregan cinco elementos Border como elementos secundarios de un elemento DockPanel primario. Cada uno de ellos utiliza una propiedad de posición diferente de un elemento DockPanel para dividir el espacio. El último elemento "rellena" el espacio que queda sin asignar.

WindowTitle = "DockPanel Sample"
'Create a DockPanel as the root Panel
Dim myDockPanel As New DockPanel()
myDockPanel.LastChildFill = True

' Define the child content
Dim myBorder1 As New Border()
myBorder1.Height = 25
myBorder1.Background = Brushes.SkyBlue
myBorder1.BorderBrush = Brushes.Black
myBorder1.BorderThickness = New Thickness(1)
DockPanel.SetDock(myBorder1, Dock.Top)
Dim myTextBlock1 As New TextBlock()
myTextBlock1.Foreground = Brushes.Black
myTextBlock1.Text = "Dock = Top"
myBorder1.Child = myTextBlock1

Dim myBorder2 As New Border()
myBorder2.Height = 25
myBorder2.Background = Brushes.SkyBlue
myBorder2.BorderBrush = Brushes.Black
myBorder2.BorderThickness = New Thickness(1)
DockPanel.SetDock(myBorder2, Dock.Top)
Dim myTextBlock2 As New TextBlock()
myTextBlock2.Foreground = Brushes.Black
myTextBlock2.Text = "Dock = Top"
myBorder2.Child = myTextBlock2

Dim myBorder3 As New Border()
myBorder3.Height = 25
myBorder3.Background = Brushes.LemonChiffon
myBorder3.BorderBrush = Brushes.Black
myBorder3.BorderThickness = New Thickness(1)
DockPanel.SetDock(myBorder3, Dock.Bottom)
Dim myTextBlock3 As New TextBlock()
myTextBlock3.Foreground = Brushes.Black
myTextBlock3.Text = "Dock = Bottom"
myBorder3.Child = myTextBlock3

Dim myBorder4 As New Border()
myBorder4.Width = 200
myBorder4.Background = Brushes.PaleGreen
myBorder4.BorderBrush = Brushes.Black
myBorder4.BorderThickness = New Thickness(1)
DockPanel.SetDock(myBorder4, Dock.Left)
Dim myTextBlock4 As New TextBlock()
myTextBlock4.Foreground = Brushes.Black
myTextBlock4.Text = "Dock = Left"
myBorder4.Child = myTextBlock4

Dim myBorder5 As New Border()
myBorder5.Background = Brushes.White
myBorder5.BorderBrush = Brushes.Black
myBorder5.BorderThickness = New Thickness(1)
Dim myTextBlock5 As New TextBlock()
myTextBlock5.Foreground = Brushes.Black
myTextBlock5.Text = "This content will Fill the remaining space"
myBorder5.Child = myTextBlock5

' Add child elements to the DockPanel Children collection
myDockPanel.Children.Add(myBorder1)
myDockPanel.Children.Add(myBorder2)
myDockPanel.Children.Add(myBorder3)
myDockPanel.Children.Add(myBorder4)
myDockPanel.Children.Add(myBorder5)
Me.Content = myDockPanel
// Create the application's main window
mainWindow = new Window ();
mainWindow.Title = "DockPanel Sample";

// Create the DockPanel
DockPanel myDockPanel = new DockPanel();
myDockPanel.LastChildFill = true;

// Define the child content
Border myBorder1 = new Border();
myBorder1.Height = 25;
myBorder1.Background = Brushes.SkyBlue;
myBorder1.BorderBrush = Brushes.Black;
myBorder1.BorderThickness = new Thickness(1);
DockPanel.SetDock(myBorder1, Dock.Top);
TextBlock myTextBlock1 = new TextBlock();
myTextBlock1.Foreground = Brushes.Black;
myTextBlock1.Text = "Dock = Top";
myBorder1.Child = myTextBlock1;

Border myBorder2 = new Border();
myBorder2.Height = 25;
myBorder2.Background = Brushes.SkyBlue;
myBorder2.BorderBrush = Brushes.Black;
myBorder2.BorderThickness = new Thickness(1);
DockPanel.SetDock(myBorder2, Dock.Top);
TextBlock myTextBlock2 = new TextBlock();
myTextBlock2.Foreground = Brushes.Black;
myTextBlock2.Text = "Dock = Top";
myBorder2.Child = myTextBlock2;

Border myBorder3 = new Border();
myBorder3.Height = 25;
myBorder3.Background = Brushes.LemonChiffon;
myBorder3.BorderBrush = Brushes.Black;
myBorder3.BorderThickness = new Thickness(1);
DockPanel.SetDock(myBorder3, Dock.Bottom);
TextBlock myTextBlock3 = new TextBlock();
myTextBlock3.Foreground = Brushes.Black;
myTextBlock3.Text = "Dock = Bottom";
myBorder3.Child = myTextBlock3;

Border myBorder4 = new Border();
myBorder4.Width = 200;
myBorder4.Background = Brushes.PaleGreen;
myBorder4.BorderBrush = Brushes.Black;
myBorder4.BorderThickness = new Thickness(1);
DockPanel.SetDock(myBorder4, Dock.Left);
TextBlock myTextBlock4 = new TextBlock();
myTextBlock4.Foreground = Brushes.Black;
myTextBlock4.Text = "Dock = Left";
myBorder4.Child = myTextBlock4;

Border myBorder5 = new Border();
myBorder5.Background = Brushes.White;
myBorder5.BorderBrush = Brushes.Black;
myBorder5.BorderThickness = new Thickness(1);
TextBlock myTextBlock5 = new TextBlock();
myTextBlock5.Foreground = Brushes.Black;
myTextBlock5.Text = "This content will Fill the remaining space";
myBorder5.Child = myTextBlock5;


// Add child elements to the DockPanel Children collection
myDockPanel.Children.Add(myBorder1);
myDockPanel.Children.Add(myBorder2);
myDockPanel.Children.Add(myBorder3);
myDockPanel.Children.Add(myBorder4);
myDockPanel.Children.Add(myBorder5);

// Add the parent Canvas as the Content of the Window Object
mainWindow.Content = myDockPanel;
mainWindow.Show ();

<Page xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" WindowTitle="DockPanel Sample">
  <DockPanel LastChildFill="True">
    <Border Height="25" Background="SkyBlue" BorderBrush="Black" BorderThickness="1" DockPanel.Dock="Top">
      <TextBlock Foreground="Black">Dock = "Top"</TextBlock>
    </Border>
    <Border Height="25" Background="SkyBlue" BorderBrush="Black" BorderThickness="1" DockPanel.Dock="Top">
      <TextBlock Foreground="Black">Dock = "Top"</TextBlock>
    </Border>
    <Border Height="25" Background="LemonChiffon" BorderBrush="Black" BorderThickness="1" DockPanel.Dock="Bottom">
      <TextBlock Foreground="Black">Dock = "Bottom"</TextBlock>
    </Border>
    <Border Width="200" Background="PaleGreen" BorderBrush="Black" BorderThickness="1" DockPanel.Dock="Left">
      <TextBlock Foreground="Black">Dock = "Left"</TextBlock>
    </Border>
    <Border Background="White" BorderBrush="Black" BorderThickness="1">
      <TextBlock Foreground="Black">This content will "Fill" the remaining space</TextBlock>
    </Border>
  </DockPanel>
</Page>

La aplicación compilada produce una nueva interfaz de usuario con esta apariencia.

Escenario DockPanel típico.

Grid

El elemento Grid combina la funcionalidad de una posición absoluta y de un control de datos tabular. Un elemento Grid le permite colocar con facilidad elementos y aplicarles estilo. Con Grid podrá definir agrupaciones flexibles de filas y columnas e incluso dispone de un mecanismo para compartir información de tamaño entre varios elementos Grid.

¿En qué se diferencian Grid y Table?

Table y Grid comparten funcionalidades comunes, pero cada elemento es más apropiado para determinados escenarios. Table se ha diseñado para su uso en contenido dinámico (vea Información general sobre documentos dinámicos para obtener más información sobre el contenido dinámico). Las cuadrículas son más apropiadas para formularios (básicamente, en cualquier lugar excepto en el contenido dinámico). Dentro de un FlowDocument, una Table admite los comportamientos del contenido dinámico, como la paginación, el ajuste dinámico de columnas y la selección de contenido, mientras que Grid no. Por su parte, Grid es más apropiado fuera de un FlowDocument por numerosas razones; una de ellas es que Grid agrega elementos basándose en un índice de fila y columna, y Table no. El elemento Grid permite la disposición en capas del contenido secundario, lo que permite que haya más de un elemento dentro de una sola "celda". Table no admite la disposición en capas. Los elementos secundarios de Grid se pueden colocar de manera absoluta en relación con el área de los límites de la "celda". Table no admite esta característica. Por último, Grid es más ligero que Table.

Comportamiento del ajuste de tamaño de las columnas y las filas

Las columnas y las filas definidas dentro de un elemento Grid pueden sacar partido del ajuste de tamaño mediante Star para distribuir el espacio restante de forma proporcional. Cuando se selecciona Star como el alto o el ancho de una fila o una columna, dicha columna o fila recibe una proporción ponderada del espacio restante disponible. Por el contrario, Auto distribuye el espacio de manera uniforme basándose en el tamaño del contenido de una columna o una fila. Este valor se expresa como * o 2* al utilizar Lenguaje de marcado de aplicaciones extensible (XAML). En el primer caso, la fila o la columna recibiría una vez el espacio disponible, en el segundo caso, dos veces, y así sucesivamente. Para obtener información adicional sobre el ajuste de tamaño mediante asterisco, vea el ejemplo de Ejemplo Use Star Sizing. Combinando esta técnica para distribuir proporcionalmente el espacio con un valor de Stretch para HorizontalAlignment y VerticalAlignment, es posible dividir el espacio de diseño en un porcentaje del espacio de pantalla. Grid es el único panel de diseño que puede distribuir el espacio de esta manera.

Definir y utilizar una cuadrícula

En el ejemplo siguiente se muestra cómo generar una interfaz de usuario similar a la del cuadro de diálogo Ejecutar del menú Inicio de Windows.

'Create a Grid as the root Panel element.
Dim myGrid As New Grid()
myGrid.Height = 165
myGrid.Width = 425
myGrid.Background = Brushes.Gainsboro
myGrid.ShowGridLines = True
myGrid.HorizontalAlignment = Windows.HorizontalAlignment.Left
myGrid.VerticalAlignment = Windows.VerticalAlignment.Top

' Define and Add the Rows and Columns.
Dim colDef1 As New ColumnDefinition
colDef1.Width = New GridLength(1, GridUnitType.Auto)
Dim colDef2 As New ColumnDefinition
colDef2.Width = New GridLength(1, GridUnitType.Star)
Dim colDef3 As New ColumnDefinition
colDef3.Width = New GridLength(1, GridUnitType.Star)
Dim colDef4 As New ColumnDefinition
colDef4.Width = New GridLength(1, GridUnitType.Star)
Dim colDef5 As New ColumnDefinition
colDef5.Width = New GridLength(1, GridUnitType.Star)
myGrid.ColumnDefinitions.Add(colDef1)
myGrid.ColumnDefinitions.Add(colDef2)
myGrid.ColumnDefinitions.Add(colDef3)
myGrid.ColumnDefinitions.Add(colDef4)
myGrid.ColumnDefinitions.Add(colDef5)

Dim rowDef1 As New RowDefinition
rowDef1.Height = New GridLength(1, GridUnitType.Auto)
Dim rowDef2 As New RowDefinition
rowDef2.Height = New GridLength(1, GridUnitType.Auto)
Dim rowDef3 As New Controls.RowDefinition
rowDef3.Height = New GridLength(1, GridUnitType.Star)
Dim rowDef4 As New RowDefinition
rowDef4.Height = New GridLength(1, GridUnitType.Auto)
myGrid.RowDefinitions.Add(rowDef1)
myGrid.RowDefinitions.Add(rowDef2)
myGrid.RowDefinitions.Add(rowDef3)
myGrid.RowDefinitions.Add(rowDef4)

' Add the Image.
Dim img1 As New Image
img1.Source = New System.Windows.Media.Imaging.BitmapImage(New Uri("runicon.png", UriKind.Relative))
Grid.SetRow(img1, 0)
Grid.SetColumn(img1, 0)
myGrid.Children.Add(img1)

' Add the main application dialog.
Dim txt1 As New TextBlock
txt1.Text = "Type the name of a program, document, or Internet resource, and Windows will open it for you."
txt1.TextWrapping = TextWrapping.Wrap
Grid.SetColumnSpan(txt1, 4)
Grid.SetRow(txt1, 0)
Grid.SetColumn(txt1, 1)
myGrid.Children.Add(txt1)

' Add the second TextBlock Cell to the Grid.
Dim txt2 As New TextBlock
txt2.Text = "Open:"
Grid.SetRow(txt2, 1)
Grid.SetColumn(txt2, 0)
myGrid.Children.Add(txt2)

' Add the TextBox control.
Dim tb1 As New TextBox
Grid.SetRow(tb1, 1)
Grid.SetColumn(tb1, 1)
Grid.SetColumnSpan(tb1, 5)
myGrid.Children.Add(tb1)

' Add the Button controls.
Dim button1 As New Button
Dim button2 As New Button
Dim button3 As New Button
button1.Content = "OK"
button1.Margin = New Thickness(10, 0, 10, 15)
button2.Content = "Cancel"
button2.Margin = New Thickness(10, 0, 10, 15)
button3.Content = "Browse ..."
button3.Margin = New Thickness(10, 0, 10, 15)

Grid.SetRow(button1, 3)
Grid.SetColumn(button1, 2)
Grid.SetRow(button2, 3)
Grid.SetColumn(button2, 3)
Grid.SetRow(button3, 3)
Grid.SetColumn(button3, 4)
myGrid.Children.Add(button1)
myGrid.Children.Add(button2)
myGrid.Children.Add(button3)

Me.Content = myGrid
// Create the Grid.
grid1 = new Grid ();
grid1.Background = Brushes.Gainsboro;
grid1.HorizontalAlignment = HorizontalAlignment.Left;
grid1.VerticalAlignment = VerticalAlignment.Top;
grid1.ShowGridLines = true;
grid1.Width = 425;
grid1.Height = 165;

// Define the Columns.
colDef1 = new ColumnDefinition();
colDef1.Width = new GridLength(1, GridUnitType.Auto);
colDef2 = new ColumnDefinition();
colDef2.Width = new GridLength(1, GridUnitType.Star);
colDef3 = new ColumnDefinition();
colDef3.Width = new GridLength(1, GridUnitType.Star);
colDef4 = new ColumnDefinition();
colDef4.Width = new GridLength(1, GridUnitType.Star);
colDef5 = new ColumnDefinition();
colDef5.Width = new GridLength(1, GridUnitType.Star);
grid1.ColumnDefinitions.Add(colDef1);
grid1.ColumnDefinitions.Add(colDef2);
grid1.ColumnDefinitions.Add(colDef3);
grid1.ColumnDefinitions.Add(colDef4);
grid1.ColumnDefinitions.Add(colDef5);

// Define the Rows.
rowDef1 = new RowDefinition();
rowDef1.Height = new GridLength(1, GridUnitType.Auto);
rowDef2 = new RowDefinition();
rowDef2.Height = new GridLength(1, GridUnitType.Auto);
rowDef3 = new RowDefinition();
rowDef3.Height = new GridLength(1, GridUnitType.Star);
rowDef4 = new RowDefinition();
rowDef4.Height = new GridLength(1, GridUnitType.Auto);
grid1.RowDefinitions.Add(rowDef1);
grid1.RowDefinitions.Add(rowDef2);
grid1.RowDefinitions.Add(rowDef3);
grid1.RowDefinitions.Add(rowDef4);

// Add the Image.
img1 = new Image();
img1.Source = new System.Windows.Media.Imaging.BitmapImage(new Uri("runicon.png", UriKind.Relative));
Grid.SetRow(img1, 0);
Grid.SetColumn(img1, 0);

// Add the main application dialog.
txt1 = new TextBlock();
txt1.Text = "Type the name of a program, folder, document, or Internet resource, and Windows will open it for you.";
txt1.TextWrapping = TextWrapping.Wrap;
Grid.SetColumnSpan(txt1, 4);
Grid.SetRow(txt1, 0);
Grid.SetColumn(txt1, 1);

// Add the second text cell to the Grid.
txt2 = new TextBlock();
txt2.Text = "Open:";
Grid.SetRow(txt2, 1);
Grid.SetColumn(txt2, 0);

// Add the TextBox control.
tb1 = new TextBox();
Grid.SetRow(tb1, 1);
Grid.SetColumn(tb1, 1);
Grid.SetColumnSpan(tb1, 5);

// Add the buttons.
button1 = new Button();
button2 = new Button();
button3 = new Button();
button1.Content = "OK";
button2.Content = "Cancel";
button3.Content = "Browse ...";
Grid.SetRow(button1, 3);
Grid.SetColumn(button1, 2);
button1.Margin = new Thickness(10, 0, 10, 15);
button2.Margin = new Thickness(10, 0, 10, 15);
button3.Margin = new Thickness(10, 0, 10, 15);
Grid.SetRow(button2, 3);
Grid.SetColumn(button2, 3);
Grid.SetRow(button3, 3);
Grid.SetColumn(button3, 4);

grid1.Children.Add(img1);
grid1.Children.Add(txt1);
grid1.Children.Add(txt2);
grid1.Children.Add(tb1);
grid1.Children.Add(button1);
grid1.Children.Add(button2);
grid1.Children.Add(button3);

mainWindow.Content = grid1;
<Page xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" 
      WindowTitle="Grid Run Dialog Sample" 
      WindowWidth="425" 
      WindowHeight="225">
  <Grid Background="#DCDCDC"
        Width="425"
        Height="165"
        HorizontalAlignment="Left"
        VerticalAlignment="Top"
        ShowGridLines="True">
    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto" />
      <ColumnDefinition Width="*" />
      <ColumnDefinition Width="*"/>
      <ColumnDefinition Width="*"/>
      <ColumnDefinition Width="*"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
      <RowDefinition Height="*" />
      <RowDefinition Height="Auto" />
    </Grid.RowDefinitions>

    <Image Grid.Column="0" Grid.Row="0" Source="RunIcon.png" />
    <TextBlock Grid.Column="1" Grid.ColumnSpan="4" Grid.Row="0" TextWrapping="Wrap">
      Type the name of a program, folder, document, or
      Internet resource, and Windows will open it for you.
    </TextBlock>
    <TextBlock Grid.Column="0" Grid.Row="1">Open:</TextBlock>
    <TextBox Grid.Row="1" Grid.Column="1" Grid.ColumnSpan="5" />
    <Button Margin="10, 0, 10, 15" Grid.Row="3" Grid.Column="2">OK</Button>
    <Button Margin="10, 0, 10, 15" Grid.Row="3" Grid.Column="3">Cancel</Button>
    <Button Margin="10, 0, 10, 15" Grid.Row="3" Grid.Column="4">Browse ...</Button>
  </Grid>
</Page>

La aplicación compilada produce una nueva interfaz de usuario con esta apariencia.

Elemento de cuadrícula típico.

StackPanel

Un elemento StackPanel le permite "apilar" los elementos en una dirección asignada. De forma predeterminada, los elementos se apilan en dirección vertical. Se puede utilizar la propiedad Orientation para controlar el flujo del contenido.

StackPanel frente a DockPanel

Aunque DockPanel también puede "apilar" elementos secundarios, DockPanel y StackPanel no generan resultados análogos en algunos escenarios de uso. Por ejemplo, el orden en el que se colocan los elementos secundarios puede afectar a su tamaño en un elemento DockPanel pero no en un elemento StackPanel. Esto se debe a que StackPanel mide en la dirección del apilado en PositiveInfinity, mientras que DockPanel mide solo el tamaño disponible.

En el ejemplo siguiente se muestra esta diferencia fundamental.

'Add root Grid
Dim myGrid As New Grid
myGrid.Width = 175
myGrid.Height = 150
Dim myRowDef1 As New RowDefinition
Dim myRowDef2 As New RowDefinition
myGrid.RowDefinitions.Add(myRowDef1)
myGrid.RowDefinitions.Add(myRowDef2)

'Define the DockPanel
Dim myDockPanel As New DockPanel
Grid.SetRow(myDockPanel, 0)

'Define an Image and Source.
Dim myImage As New Image
Dim bi As New BitmapImage
bi.BeginInit()
bi.UriSource = New Uri("smiley_stackpanel.png", UriKind.Relative)
bi.EndInit()
myImage.Source = bi

Dim myImage2 As New Image
Dim bi2 As New BitmapImage
bi2.BeginInit()
bi2.UriSource = New Uri("smiley_stackpanel.png", UriKind.Relative)
bi2.EndInit()
myImage2.Source = bi2

Dim myImage3 As New Image
Dim bi3 As New BitmapImage
bi3.BeginInit()
bi3.UriSource = New Uri("smiley_stackpanel.PNG", UriKind.Relative)
bi3.EndInit()
myImage3.Stretch = Stretch.Fill
myImage3.Source = bi3

'Add the images to the parent DockPanel.
myDockPanel.Children.Add(myImage)
myDockPanel.Children.Add(myImage2)
myDockPanel.Children.Add(myImage3)

'Define a StackPanel.
Dim myStackPanel As New StackPanel
myStackPanel.Orientation = Orientation.Horizontal
Grid.SetRow(myStackPanel, 1)

Dim myImage4 As New Image
Dim bi4 As New BitmapImage
bi4.BeginInit()
bi4.UriSource = New Uri("smiley_stackpanel.png", UriKind.Relative)
bi4.EndInit()
myImage4.Source = bi4

Dim myImage5 As New Image
Dim bi5 As New BitmapImage
bi5.BeginInit()
bi5.UriSource = New Uri("smiley_stackpanel.png", UriKind.Relative)
bi5.EndInit()
myImage5.Source = bi5

Dim myImage6 As New Image
Dim bi6 As New BitmapImage
bi6.BeginInit()
bi6.UriSource = New Uri("smiley_stackpanel.PNG", UriKind.Relative)
bi6.EndInit()
myImage6.Stretch = Stretch.Fill
myImage6.Source = bi6

'Add the images to the parent StackPanel.
myStackPanel.Children.Add(myImage4)
myStackPanel.Children.Add(myImage5)
myStackPanel.Children.Add(myImage6)

'Add the layout panels as children of the Grid
myGrid.Children.Add(myDockPanel)
myGrid.Children.Add(myStackPanel)

// Create the application's main window
mainWindow = new Window ();
mainWindow.Title = "StackPanel vs. DockPanel";

// Add root Grid
myGrid = new Grid();
myGrid.Width = 175;
myGrid.Height = 150;
RowDefinition myRowDef1 = new RowDefinition();
RowDefinition myRowDef2 = new RowDefinition();
myGrid.RowDefinitions.Add(myRowDef1);
myGrid.RowDefinitions.Add(myRowDef2);

// Define the DockPanel
myDockPanel = new DockPanel();
Grid.SetRow(myDockPanel, 0);

//Define an Image and Source
Image myImage = new Image();
BitmapImage bi = new BitmapImage();
bi.BeginInit();
bi.UriSource = new Uri("smiley_stackpanel.png", UriKind.Relative);
bi.EndInit();
myImage.Source = bi;

Image myImage2 = new Image();
BitmapImage bi2 = new BitmapImage();
bi2.BeginInit();
bi2.UriSource = new Uri("smiley_stackpanel.png", UriKind.Relative);
bi2.EndInit();
myImage2.Source = bi2;

Image myImage3 = new Image();
BitmapImage bi3 = new BitmapImage();
bi3.BeginInit();
bi3.UriSource = new Uri("smiley_stackpanel.PNG", UriKind.Relative);
bi3.EndInit();
myImage3.Stretch = Stretch.Fill;
myImage3.Source = bi3;

// Add the images to the parent DockPanel
myDockPanel.Children.Add(myImage);
myDockPanel.Children.Add(myImage2);
myDockPanel.Children.Add(myImage3);

//Define a StackPanel
myStackPanel = new StackPanel();
myStackPanel.Orientation = Orientation.Horizontal;
Grid.SetRow(myStackPanel, 1);

Image myImage4 = new Image();
BitmapImage bi4 = new BitmapImage();
bi4.BeginInit();
bi4.UriSource = new Uri("smiley_stackpanel.png", UriKind.Relative);
bi4.EndInit();
myImage4.Source = bi4;

Image myImage5 = new Image();
BitmapImage bi5 = new BitmapImage();
bi5.BeginInit();
bi5.UriSource = new Uri("smiley_stackpanel.png", UriKind.Relative);
bi5.EndInit();
myImage5.Source = bi5;

Image myImage6 = new Image();
BitmapImage bi6 = new BitmapImage();
bi6.BeginInit();
bi6.UriSource = new Uri("smiley_stackpanel.PNG", UriKind.Relative);
bi6.EndInit();
myImage6.Stretch = Stretch.Fill;
myImage6.Source = bi6;

// Add the images to the parent StackPanel
myStackPanel.Children.Add(myImage4);
myStackPanel.Children.Add(myImage5);
myStackPanel.Children.Add(myImage6);

// Add the layout panels as children of the Grid
myGrid.Children.Add(myDockPanel);
myGrid.Children.Add(myStackPanel);

// Add the Grid as the Content of the Parent Window Object
mainWindow.Content = myGrid;
mainWindow.Show ();

<Page xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
      WindowTitle="StackPanel vs. DockPanel">
  <Grid Width="175" Height="150">
    <Grid.ColumnDefinitions>
      <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
      <RowDefinition />
      <RowDefinition />
    </Grid.RowDefinitions>

    <DockPanel Grid.Column="0" Grid.Row="0">
      <Image Source="smiley_stackpanel.png" />
      <Image Source="smiley_stackpanel.png" />
      <Image Source="smiley_stackpanel.png" Stretch="Fill"/>
    </DockPanel>

    <StackPanel Grid.Column="0" Grid.Row="1"  Orientation="Horizontal">
      <Image Source="smiley_stackpanel.png" />
      <Image Source="smiley_stackpanel.png" />
      <Image Source="smiley_stackpanel.png" Stretch="Fill"/>
    </StackPanel>
    </Grid>
</Page>

La diferencia en cuanto al comportamiento de representación se puede ver en esta imagen.

Captura de pantalla: Captura de pantalla de StackPanel frente a DockPanel

Definir y utilizar un elemento StackPanel

En el ejemplo siguiente se muestra cómo utilizar un elemento StackPanel para crear un conjunto de botones colocados verticalmente. Para colocarlos horizontalmente, establezca la propiedad Orientation en Horizontal.

WindowTitle = "StackPanel Sample"
' Define the StackPanel
Dim myStackPanel As New StackPanel()
myStackPanel.HorizontalAlignment = Windows.HorizontalAlignment.Left
myStackPanel.VerticalAlignment = Windows.VerticalAlignment.Top

' Define child content
Dim myButton1 As New Button()
myButton1.Content = "Button 1"
Dim myButton2 As New Button()
myButton2.Content = "Button 2"
Dim myButton3 As New Button()
myButton3.Content = "Button 3"

' Add child elements to the parent StackPanel
myStackPanel.Children.Add(myButton1)
myStackPanel.Children.Add(myButton2)
myStackPanel.Children.Add(myButton3)

Me.Content = myStackPanel
// Create the application's main window
mainWindow = new Window ();
mainWindow.Title = "StackPanel Sample";

// Define the StackPanel
myStackPanel = new StackPanel();
myStackPanel.HorizontalAlignment = HorizontalAlignment.Left;
myStackPanel.VerticalAlignment = VerticalAlignment.Top;

// Define child content
Button myButton1 = new Button();
myButton1.Content = "Button 1";
Button myButton2 = new Button();
myButton2.Content = "Button 2";
Button myButton3 = new Button();
myButton3.Content = "Button 3";

// Add child elements to the parent StackPanel
myStackPanel.Children.Add(myButton1);
myStackPanel.Children.Add(myButton2);
myStackPanel.Children.Add(myButton3);           

// Add the StackPanel as the Content of the Parent Window Object
mainWindow.Content = myStackPanel;
mainWindow.Show ();

<Page xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" WindowTitle="StackPanel Sample">
    <StackPanel HorizontalAlignment="Left"
                VerticalAlignment="Top">
        <Button>Button 1</Button>
        <Button>Button 2</Button>
        <Button>Button 3</Button>
    </StackPanel>
</Page>

La aplicación compilada produce una nueva interfaz de usuario con esta apariencia.

Elemento StackPanel típico.

VirtualizingStackPanel

WPF también proporciona una variación del elemento StackPanel que "virtualiza" automáticamente el contenido secundario enlazado a datos. En este contexto, el término "virtualizar" se refiere a una técnica por la que se genera un subconjunto de elementos UIElements a partir de un número mayor de elementos de datos en función de los elementos que están visibles en pantalla. Generar un gran número de elementos de interfaz de usuario cuando sólo pueden estar en pantalla algunos de ellos en un momento dado, requiere un uso intensivo, tanto de la memoria como del procesador. VirtualizingStackPanel (a través de una funcionalidad proporcionada por VirtualizingPanel) calcula los elementos visibles y trabaja con ItemContainerGenerator desde un ItemsControl (como ListBox o ListView) para crear únicamente los UIElements correspondientes a los elementos visibles.

El elemento VirtualizingStackPanel se establece automáticamente como el host de elementos para controles como ListBox. Al hospedar una colección enlazada a datos, el contenido se virtualiza automáticamente, siempre que el contenido esté dentro de los límites de un elemento ScrollViewer. Esto mejora notablemente el rendimiento cuando se hospedan muchos elementos secundarios.

El marcado siguiente muestra cómo utilizar un elemento VirtualizingStackPanel como un host de elementos. La propiedad asociadaIsVirtualizing debe estar establecida en True (valor predeterminado) para que se produzca la virtualización.

<StackPanel DataContext="{Binding Source={StaticResource Leagues}}">
    <TextBlock Text="{Binding XPath=@name}" FontFamily="Arial" FontSize="18" Foreground="Black"/>
        <ListBox VirtualizingStackPanel.IsVirtualizing="True" 
                 ItemsSource="{Binding XPath=Team}" 
                 ItemTemplate="{DynamicResource NameDataStyle}"/>      
</StackPanel>

WrapPanel

WrapPanel se utiliza para colocar de izquierda a derecha los elementos secundarios en posición secuencial, y traslada el contenido a la línea siguiente cuando alcanza el borde de su contenedor primario. El contenido se puede orientar horizontal o verticalmente. WrapPanel es útil para escenarios de interfaz de usuario (UI) de flujo sencillo. También se puede utilizar para aplicar un tamaño uniforme a todos sus elementos secundarios.

En el ejemplo siguiente se muestra cómo crear un elemento WrapPanel para mostrar controles Button que se ajustan cuando llegan al borde de su contenedor.

WindowTitle = "WrapPanel Sample"

' Instantiate a new WrapPanel and set properties
Dim myWrapPanel As New WrapPanel()
myWrapPanel.Background = Brushes.Azure
myWrapPanel.Orientation = Orientation.Horizontal
myWrapPanel.ItemHeight = 25

myWrapPanel.ItemWidth = 75
myWrapPanel.Width = 150
myWrapPanel.HorizontalAlignment = Windows.HorizontalAlignment.Left
myWrapPanel.VerticalAlignment = Windows.VerticalAlignment.Top

' Define 3 button elements. Each button is sized at width of 75, so the third button wraps to the next line.
Dim btn1 As New Button()
btn1.Content = "Button 1"
Dim btn2 As New Button()
btn2.Content = "Button 2"
Dim btn3 As New Button()
btn3.Content = "Button 3"

' Add the buttons to the parent WrapPanel using the Children.Add method.
myWrapPanel.Children.Add(btn1)
myWrapPanel.Children.Add(btn2)
myWrapPanel.Children.Add(btn3)

' Add the WrapPanel to the Page as Content
Me.Content = myWrapPanel

            // Create the application's main window
            mainWindow = new System.Windows.Window();
            mainWindow.Title = "WrapPanel Sample";


            // Instantiate a new WrapPanel and set properties
            myWrapPanel = new WrapPanel();
            myWrapPanel.Background = System.Windows.Media.Brushes.Azure;
            myWrapPanel.Orientation = Orientation.Horizontal;
            myWrapPanel.ItemHeight = 25;

            myWrapPanel.ItemWidth = 75;
            myWrapPanel.Width = 150;
            myWrapPanel.HorizontalAlignment = HorizontalAlignment.Left;
            myWrapPanel.VerticalAlignment = VerticalAlignment.Top;

            // Define 3 button elements. Each button is sized at width of 75, so the third button wraps to the next line.
            btn1 = new Button();
            btn1.Content = "Button 1";
            btn2 = new Button();
            btn2.Content = "Button 2";
            btn3 = new Button();
            btn3.Content = "Button 3";

            // Add the buttons to the parent WrapPanel using the Children.Add method.
            myWrapPanel.Children.Add(btn1);
            myWrapPanel.Children.Add(btn2);
            myWrapPanel.Children.Add(btn3);

            // Add the WrapPanel to the MainWindow as Content
            mainWindow.Content = myWrapPanel;
            mainWindow.Show();

<Page xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" WindowTitle="WrapPanel Sample">
  <Border HorizontalAlignment="Left" VerticalAlignment="Top" BorderBrush="Black" BorderThickness="2">
        <WrapPanel Background="LightBlue" Width="200" Height="100">
            <Button Width="200">Button 1</Button>
            <Button>Button 2</Button>
            <Button>Button 3</Button>
            <Button>Button 4</Button>
        </WrapPanel>
  </Border>    
</Page>

La aplicación compilada produce una nueva interfaz de usuario con esta apariencia.

Elemento WrapPanel típico.

Elementos Panel anidados

Los elementos Panel pueden anidarse unos dentro de otros para generar los diseños complejos. Esto puede resultar muy útil en situaciones en las que un elemento Panel es ideal para una parte de una interfaz de usuario, pero no puede satisfacer las necesidades de otra parte de la interfaz de usuario.

No existe ningún límite real a la cantidad de anidamiento que puede admitir una aplicación; sin embargo, en general conviene limitar la aplicación para que utilice sólo los paneles que son realmente necesarios para el diseño que se desea. En muchos casos, se puede utilizar un elemento Grid en lugar de paneles anidados debido a su flexibilidad como contenedor de diseño. Esto puede aumentar el rendimiento de una aplicación al dejar fuera del árbol los elementos innecesarios.

En el ejemplo siguiente se muestra cómo crear una interfaz de usuario que hace uso de elementos Panel anidados para lograr un diseño concreto. En este caso concreto, se usa un elemento DockPanel para proporcionar la estructura de la interfaz de usuario y elementos StackPanel anidados, un elemento Grid y un elemento Canvas para colocar con precisión los elementos secundarios dentro del elemento DockPanel primario.

Dim myDockPanel As New DockPanel()

Dim myBorder2 As New Border()
myBorder2.BorderThickness = New Thickness(1)
myBorder2.BorderBrush = Brushes.Black
DockPanel.SetDock(myBorder2, Dock.Left)
Dim myStackPanel As New StackPanel()
Dim myButton1 As New Button()
myButton1.Content = "Left Docked"
myButton1.Margin = New Thickness(5)
Dim myButton2 As New Button()
myButton2.Content = "StackPanel"
myButton2.Margin = New Thickness(5)
myStackPanel.Children.Add(myButton1)
myStackPanel.Children.Add(myButton2)
myBorder2.Child = myStackPanel

Dim myBorder3 As New Border()
myBorder3.BorderThickness = New Thickness(1)
myBorder3.BorderBrush = Brushes.Black
DockPanel.SetDock(myBorder3, Dock.Top)
Dim myGrid As New Grid()
myGrid.ShowGridLines = True
Dim myRowDef1 As New RowDefinition()
Dim myRowDef2 As New RowDefinition()
Dim myColDef1 As New ColumnDefinition()
Dim myColDef2 As New ColumnDefinition()
Dim myColDef3 As New ColumnDefinition()
myGrid.ColumnDefinitions.Add(myColDef1)
myGrid.ColumnDefinitions.Add(myColDef2)
myGrid.ColumnDefinitions.Add(myColDef3)
myGrid.RowDefinitions.Add(myRowDef1)
myGrid.RowDefinitions.Add(myRowDef2)
Dim myTextBlock1 As New TextBlock()
myTextBlock1.FontSize = 20
myTextBlock1.Margin = New Thickness(10)
myTextBlock1.Text = "Grid Element Docked at the Top"
Grid.SetRow(myTextBlock1, 0)
Grid.SetColumnSpan(myTextBlock1, 3)
Dim myButton3 As New Button()
myButton3.Margin = New Thickness(5)
myButton3.Content = "A Row"
Grid.SetColumn(myButton3, 0)
Grid.SetRow(myButton3, 1)
Dim myButton4 As New Button()
myButton4.Margin = New Thickness(5)
myButton4.Content = "of Button"
Grid.SetColumn(myButton4, 1)
Grid.SetRow(myButton4, 1)
Dim myButton5 As New Button()
myButton5.Margin = New Thickness(5)
myButton5.Content = "Elements"
Grid.SetColumn(myButton5, 2)
Grid.SetRow(myButton5, 1)
myGrid.Children.Add(myTextBlock1)
myGrid.Children.Add(myButton3)
myGrid.Children.Add(myButton4)
myGrid.Children.Add(myButton5)
myBorder3.Child = myGrid

Dim myBorder4 As New Border()
myBorder4.BorderBrush = Brushes.Black
myBorder4.BorderThickness = New Thickness(1)
DockPanel.SetDock(myBorder4, Dock.Bottom)
Dim myStackPanel2 As New StackPanel()
myStackPanel2.Orientation = Orientation.Horizontal
Dim myTextBlock2 As New TextBlock()
myTextBlock2.Text = "This StackPanel is Docked to the Bottom"
myTextBlock2.Margin = New Thickness(5)
myStackPanel2.Children.Add(myTextBlock2)
myBorder4.Child = myStackPanel2

Dim myBorder5 As New Border()
myBorder5.BorderBrush = Brushes.Black
myBorder5.BorderThickness = New Thickness(1)
Dim myCanvas As New Canvas()
myCanvas.ClipToBounds = True
Dim myTextBlock3 As New TextBlock()
myTextBlock3.Text = "Content in the Canvas will Fill the remaining space."
Canvas.SetTop(myTextBlock3, 50)
Canvas.SetLeft(myTextBlock3, 50)
Dim myEllipse As New Ellipse()
myEllipse.Height = 100
myEllipse.Width = 125
myEllipse.Fill = Brushes.CornflowerBlue
myEllipse.Stroke = Brushes.Aqua
Canvas.SetTop(myEllipse, 100)
Canvas.SetLeft(myEllipse, 150)
myCanvas.Children.Add(myTextBlock3)
myCanvas.Children.Add(myEllipse)
myBorder5.Child = myCanvas

myDockPanel.Children.Add(myBorder2)
myDockPanel.Children.Add(myBorder3)
myDockPanel.Children.Add(myBorder4)
myDockPanel.Children.Add(myBorder5)
// Define the DockPanel.
myDockPanel = new DockPanel();

// Add the Left Docked StackPanel
Border myBorder2 = new Border();
myBorder2.BorderThickness = new Thickness(1);
myBorder2.BorderBrush = Brushes.Black;
DockPanel.SetDock(myBorder2, Dock.Left);
StackPanel myStackPanel = new StackPanel();
Button myButton1 = new Button();
myButton1.Content = "Left Docked";
myButton1.Margin = new Thickness(5);
Button myButton2 = new Button();
myButton2.Content = "StackPanel";
myButton2.Margin = new Thickness(5);
myStackPanel.Children.Add(myButton1);
myStackPanel.Children.Add(myButton2);
myBorder2.Child = myStackPanel;

// Add the Top Docked Grid.
Border myBorder3 = new Border();
myBorder3.BorderThickness = new Thickness(1);
myBorder3.BorderBrush = Brushes.Black;
DockPanel.SetDock(myBorder3, Dock.Top);
Grid myGrid = new Grid();
myGrid.ShowGridLines = true;
RowDefinition myRowDef1 = new RowDefinition();
RowDefinition myRowDef2 = new RowDefinition();
ColumnDefinition myColDef1 = new ColumnDefinition();
ColumnDefinition myColDef2 = new ColumnDefinition();
ColumnDefinition myColDef3 = new ColumnDefinition();
myGrid.ColumnDefinitions.Add(myColDef1);
myGrid.ColumnDefinitions.Add(myColDef2);
myGrid.ColumnDefinitions.Add(myColDef3);
myGrid.RowDefinitions.Add(myRowDef1);
myGrid.RowDefinitions.Add(myRowDef2);
TextBlock myTextBlock1 = new TextBlock();
myTextBlock1.FontSize = 20;
myTextBlock1.Margin = new Thickness(10);
myTextBlock1.Text = "Grid Element Docked at the Top";
Grid.SetRow(myTextBlock1, 0);
Grid.SetColumnSpan(myTextBlock1, 3);
Button myButton3 = new Button();
myButton3.Margin = new Thickness(5);
myButton3.Content = "A Row";
Grid.SetColumn(myButton3, 0);
Grid.SetRow(myButton3, 1);
Button myButton4 = new Button();
myButton4.Margin = new Thickness(5);
myButton4.Content = "of Button";
Grid.SetColumn(myButton4, 1);
Grid.SetRow(myButton4, 1);
Button myButton5 = new Button();
myButton5.Margin = new Thickness(5);
myButton5.Content = "Elements";
Grid.SetColumn(myButton5, 2);
Grid.SetRow(myButton5, 1);
myGrid.Children.Add(myTextBlock1);
myGrid.Children.Add(myButton3);
myGrid.Children.Add(myButton4);
myGrid.Children.Add(myButton5);
myBorder3.Child = myGrid;

// Add the Bottom Docked StackPanel.
Border myBorder4 = new Border();
myBorder4.BorderBrush = Brushes.Black;
myBorder4.BorderThickness = new Thickness(1);
DockPanel.SetDock(myBorder4, Dock.Bottom);
StackPanel myStackPanel2 = new StackPanel();
myStackPanel2.Orientation = Orientation.Horizontal;
TextBlock myTextBlock2 = new TextBlock();
myTextBlock2.Text = "This StackPanel is Docked to the Bottom";
myTextBlock2.Margin = new Thickness(5);
myStackPanel2.Children.Add(myTextBlock2);
myBorder4.Child = myStackPanel2;

// Add the Canvas, that fills remaining space.
Border myBorder5 = new Border();
myBorder4.BorderBrush = Brushes.Black;
myBorder5.BorderThickness = new Thickness(1);
Canvas myCanvas = new Canvas();
myCanvas.ClipToBounds = true;
TextBlock myTextBlock3 = new TextBlock();
myTextBlock3.Text = "Content in the Canvas will Fill the remaining space.";
Canvas.SetTop(myTextBlock3, 50);
Canvas.SetLeft(myTextBlock3, 50);
Ellipse myEllipse = new Ellipse();
myEllipse.Height = 100;
myEllipse.Width = 125;
myEllipse.Fill = Brushes.CornflowerBlue;
myEllipse.Stroke = Brushes.Aqua;
Canvas.SetTop(myEllipse, 100);
Canvas.SetLeft(myEllipse, 150);
myCanvas.Children.Add(myTextBlock3);
myCanvas.Children.Add(myEllipse);
myBorder5.Child = myCanvas;

// Add child elements to the parent DockPanel.
myDockPanel.Children.Add(myBorder2);
myDockPanel.Children.Add(myBorder3);
myDockPanel.Children.Add(myBorder4);
myDockPanel.Children.Add(myBorder5);
<Page xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" WindowTitle="Nested Panels">
  <Border Background="AliceBlue" 
          Width="400" 
          Height="300" 
          BorderBrush="DarkSlateBlue" 
          BorderThickness="2"
          HorizontalAlignment="Left" 
          VerticalAlignment="Top">
    <DockPanel>
      <Border BorderBrush="Black" BorderThickness="1" DockPanel.Dock="Left">
        <StackPanel>
          <Button Margin="5">Left Docked</Button>
          <Button Margin="5">StackPanel</Button>
        </StackPanel>
      </Border>
      <Border BorderBrush="Black" BorderThickness="1" DockPanel.Dock="Top">
        <Grid ShowGridLines="True">
          <Grid.RowDefinitions>
            <RowDefinition/>
            <RowDefinition/>
          </Grid.RowDefinitions>
          <Grid.ColumnDefinitions>
            <ColumnDefinition />
            <ColumnDefinition />
            <ColumnDefinition />
          </Grid.ColumnDefinitions>
          <TextBlock FontSize="20" Margin="10" Grid.ColumnSpan="3" Grid.Row="0">Grid Element Docked to the Top.</TextBlock>
          <Button Grid.Row="1" Grid.Column="0" Margin="5">A Row</Button>
          <Button Grid.Row="1" Grid.Column="1" Margin="5">of Button</Button>
          <Button Grid.Row="1" Grid.Column="2" Margin="5">Elements</Button>
        </Grid>
      </Border>
      <Border BorderBrush="Black" BorderThickness="1" DockPanel.Dock="Bottom">
        <StackPanel Orientation="Horizontal">
          <TextBlock Margin="5">This StackPanel is Docked to the Bottom.</TextBlock>
        </StackPanel>
      </Border>
      <Border BorderBrush="Black" BorderThickness="1">
        <Canvas ClipToBounds="True">
          <TextBlock Canvas.Top="50" Canvas.Left="50">
            Content in the Canvas will Fill the remaining Space.
          </TextBlock>
          <Ellipse Height="100" Width="125" Fill="CornflowerBlue" Stroke="Aqua" Canvas.Top="100" Canvas.Left="150"/>
        </Canvas>
      </Border>
    </DockPanel>
  </Border>
</Page>

La aplicación compilada produce una nueva interfaz de usuario con esta apariencia.

IU que aprovecha los paneles anidados.

Elementos Panel personalizados

Aunque WPF proporciona una serie de controles de diseño flexibles, también se pueden lograr comportamientos de diseño personalizados invalidando los métodos ArrangeOverride y MeasureOverride. Es posible ajustar de forma personalizada el tamaño y la posición definiendo nuevos comportamientos de posición mediante estos métodos de invalidación.

De igual forma, los comportamientos de diseño personalizados basados en clases derivadas (como Canvas o Grid) se pueden definir invalidando sus métodos ArrangeOverride y MeasureOverride.

En el marcado siguiente se muestra cómo crear un elemento Panel personalizado mediante C#. Este nuevo elemento Panel, definido como PlotPanel, admite la posición de elementos secundarios a través del uso de coordenadas x e y incluidas en el código. En este ejemplo, se coloca un elemento Rectangle (no se muestra) en las coordenadas 50 (x) y 50 (y).

Public Class PlotPanel
    Inherits Panel
    'Override the default Measure method of Panel.

    Protected Overrides Function MeasureOverride(ByVal availableSize As System.Windows.Size) As System.Windows.Size
        Dim childSize As Size = CType(availableSize, Size)
        For Each child As UIElement In InternalChildren
            child.Measure(childSize)
        Next
        Return MyBase.MeasureOverride(availableSize)
    End Function
    Protected Overrides Function ArrangeOverride(ByVal finalSize As System.Windows.Size) As System.Windows.Size
        For Each child As UIElement In InternalChildren
            Dim x As Double = 50
            Dim y As Double = 50
            child.Arrange(New Rect(New System.Windows.Point(x, y), child.DesiredSize))
        Next
        Return MyBase.ArrangeOverride(finalSize)
    End Function
End Class
public class PlotPanel : Panel
{
    // Default public constructor
    public PlotPanel()
        : base()
    {
    }

    // Override the default Measure method of Panel
    protected override Size MeasureOverride(Size availableSize)
    {
        Size panelDesiredSize = new Size();

        // In our example, we just have one child. 
        // Report that our panel requires just the size of its only child.
        foreach (UIElement child in InternalChildren)
        {
            child.Measure(availableSize);
            panelDesiredSize = child.DesiredSize;
        }

        return panelDesiredSize ;
    }
    protected override Size ArrangeOverride(Size finalSize)
    {
        foreach (UIElement child in InternalChildren)
        {
            double x = 50;
            double y = 50;

            child.Arrange(new Rect(new Point(x, y), child.DesiredSize));
        }
        return finalSize; // Returns the final Arranged size
    }
}

El ejemplo completo está disponible en Ejemplo Create a Simple Custom Panel.

Para ver una implementación de un panel personalizado más complejo, consulte Ejemplo Create a Custom Content-Wrapping Panel.

Compatibilidad para la localización y globalización

WPF admite diversas características que facilitan la creación de una interfaz de usuario localizable.

Todos los elementos Panel admiten de forma nativa la propiedad FlowDirection, que se puede utilizar para alterar dinámicamente el flujo del contenido basándose en la configuración regional o de idioma del usuario. Para obtener más información, vea FlowDirection.

La propiedad SizeToContent proporciona un mecanismo que permite a los programadores de aplicaciones anticiparse a las necesidades de localización de la interfaz de usuario. Con el valor WidthAndHeight de esta propiedad, un elemento Window primario siempre ajusta su tamaño dinámicamente de acuerdo con el contenido y no está sujeto a restricciones artificiales en cuanto al alto o al ancho.

DockPanel, Grid y StackPanel son buenas opciones para crear una interfaz de usuario localizable. Sin embargo, Canvas no es adecuada, porque sitúa el contenido de forma absoluta, lo que hace que sea difícil su localización.

Para obtener información adicional sobre cómo crear aplicaciones de WPF con interfaces de usuario (UIs) localizables, vea Información general sobre el uso del diseño automático.

Vea también

Tareas

Ejemplo WPF Layout Gallery

Ejemplo WPF Controls Gallery

Ejemplo Create a Custom Content-Wrapping Panel

Conceptos

Información general sobre Windows Presentation Foundation

Sistema de diseño

Información general sobre alineación, márgenes y relleno

Información general sobre propiedades asociadas

Información general sobre el uso del diseño automático

Optimizar el rendimiento: Presentación y diseño