DevelopersDotNet.com

Contenido original para la comunidad de desarrolladores Microsoft

WPF – ¿StackPanel, Canvas, DockPanel, Grid? ¡¡¡Auxilio…!!!

 

Aventuras en WPF

Hola a todos, pues yo aquí entrándole a escribirles del mundo de WPF, del cual, mi amigo Mike ya es un experto y como verán en sus blogs, ya les platica de WPF, de WF y seguramente ya estará planeando la entrega de algo de WCF. Yo también les platicaré de WPF, pero desde otro punto de vista (el mío jajaja) y espero les agrade a todos ustedes. He decidido platicarles de WPF, ya que WPF y XAML en sí, son un nuevo y muy vasto universo de cosas de las que creo que hay mucha tela de donde cortar, así que, con la venia de ustedes y del gran Mike, empecemos.

Lo primero que me encontré desde hace algún tiempo al empezar a ver algunos demos en MSDN, fue que existe una nueva serie de etiquetas XAML por medio de las cuales podemos organizar y crear una aplicación Windows, sin embargo, como que la diferencia entre estas etiquetas no quedaba verdaderamente clara a medida que utilizaba y veía más demos, algunas de ellas, inclusive, daban el mismo efecto sin importar si las agrego o las cambio en una aplicación Windows de WPF. Entre estas nuevas etiquetas tenemos a las siguientes:

·         Grid

·         StackPanel

·         DockPanel

·         Canvas

En mucha de la documentación de WPF y demos, se utilizan estos controles, pero en realidad no encontré muchos lugares en donde se explique fácilmente ¿qué es cada uno de ellos? ¿Cuándo utilizarlos? Así que como ya se imaginarán, este artículo trata exactamente de eso, de explicar a estos nuevos amigos de XAML.

El Grid

El primer elemento del cual les platicaré es el GRID, este es un control que define su contenido mediante columnas y renglones, es como si estuviéramos “dibujando” una hoja de Excel y metiendo contenido por medio de coordenadas dentro de cada una de las “celdas” que se forman, los contenidos los puedo insertar en cada celda por medio de coordenadas, esto quiere decir, entre otras cosas, que si meto dos elementos en la misma coordenada, puedo hacer que esos elementos se “encimen” para alcanzar efectos de “layering”.

Cada una de las celdas por default, se ajustan al contenido, así también, cada columna y renglón tienen propiedades Width y Height por medio de las cuales puedo especificar un alto y ancho en específico, o puedo utilizar el valor “*” para determinar que una celda o columna ocupe el espacio “restante” dentro de su contenedor.

Como ya todos sabemos, ahora con XAML, puedo crear la parte visual de mis aplicaciones por medio de etiquetas, les quiero mostrar lo que tendría que hacer si quisieran crear un GRID con código (Cuidado, la idea es ya NO hacerlo de esta manera) y posteriormente les muestro la comparación de la creación de ese mismo GRID con XAML.

Grid con código:

Para crear un GRID, como lo hice en este ejemplo, basta con que copien este código al constructor de su ventana:

        Dim myGrid As New Grid()

        myGrid.Height = 80

        myGrid.Width = 250

        myGrid.ShowGridLines = True

 

        myGrid.HorizontalAlignment = Windows.HorizontalAlignment.Left

        myGrid.VerticalAlignment = Windows.VerticalAlignment.Top

 

        Dim colDef1, colDef2 As New ColumnDefinition

        myGrid.ColumnDefinitions.Add(colDef1)

        myGrid.ColumnDefinitions.Add(colDef2)

 

        Dim rowDef1, rowDef2, rowDef3 As New RowDefinition

        myGrid.RowDefinitions.Add(rowDef1)

        myGrid.RowDefinitions.Add(rowDef2)

        myGrid.RowDefinitions.Add(rowDef3)

 

        Dim txt1 As New TextBlock

        txt1.Text = "Artículos en DevelopersDotNet"

        txt1.FontSize = 14

        txt1.FontWeight = FontWeights.Bold

        Grid.SetColumnSpan(txt1, 3)

        Grid.SetRow(txt1, 0)

        myGrid.Children.Add(txt1)

 

        Dim txt2 As New TextBlock

        txt2.Text = "Enero 07"

        txt2.FontSize = 12

        txt2.FontWeight = FontWeights.Bold

        Grid.SetRow(txt2, 1)

        Grid.SetColumn(txt2, 0)

        myGrid.Children.Add(txt2)

 

        Dim txt3 As New TextBlock

        txt3.Text = "Febrero 07"

        txt3.FontSize = 12

        txt3.FontWeight = FontWeights.Bold

        Grid.SetRow(txt3, 1)

        Grid.SetColumn(txt3, 1)

        myGrid.Children.Add(txt3)

 

        Dim txt4 As New TextBlock

        txt4.Text = "7"

        Grid.SetRow(txt4, 2)

        Grid.SetColumn(txt4, 0)

        myGrid.Children.Add(txt4)

 

        Dim txt5 As New Controls.TextBlock

        txt5.Text = "15"

        Grid.SetRow(txt5, 2)

        Grid.SetColumn(txt5, 1)

        myGrid.Children.Add(txt5)

 

        Me.Content = myGrid

 

Si ustedes copian y pegan este código en el constructor de su ventana, obtendrán el siguiente resultado:

Figura1

 

 

 

 

 

 

 

 

 

¡Qué horror!, Como nos podemos dar cuenta, el código de creación del GRID no es complicado, pero si es talachudo.

Grid con XAML:

La noticia es que con XAML, podemos obtener el mismo resultado con muchas menos líneas de código. En vez de construir el GRID de manera dinámica con código VB .Net, lo haremos con XAML mediante este código:

  <Grid VerticalAlignment="Top" HorizontalAlignment="Left" ShowGridLines="True" Width="250" Height="80">

    <Grid.ColumnDefinitions>

      <ColumnDefinition />

      <ColumnDefinition />

    </Grid.ColumnDefinitions>

    <Grid.RowDefinitions>

      <RowDefinition />

      <RowDefinition />

      <RowDefinition />

    </Grid.RowDefinitions>

 

    <TextBlock FontSize="14" FontWeight="Bold" Grid.ColumnSpan="3" Grid.Row="0">Artículos en DevelopersDotNet</TextBlock>

    <TextBlock FontSize="12" FontWeight="Bold" Grid.Row="1" Grid.Column="0">Enero 07</TextBlock>

    <TextBlock FontSize="12" FontWeight="Bold" Grid.Row="1" Grid.Column="1">Febrero 07</TextBlock>

    <TextBlock Grid.Row="2" Grid.Column="0">7</TextBlock>

    <TextBlock Grid.Row="2" Grid.Column="1">15</TextBlock>

  </Grid>

¿No les medio recuerda a algo llamado HTML?, jajaja. Como nos podemos dar cuenta, es mucho más sencillo utilizar WPF para crear la parte de presentación de nuestras aplicaciones. ¡Y ES EL MISMO RESULTADO!, Así también, podemos ver como el GRID, es un elemento que sirve para poder posicionar contenido mediante celdas y columnas escrito en XAML de una manera muy intuitiva.

El StackPanel

Otro de los elementos que nos permiten insertar contenido mediante XAML en nuestras aplicaciones WPF, es el StackPanel, en este caso, podemos pensar en el StackPanel como un control, por medio del cual, vamos a meter contenido pero que se va a “apilar” poco a poco dentro del mismo StackPanel, por eso el nombre de “stack” (pila o apilar). El StackPanel permite apilar el contenido de manera Vertical u Horizontal, y el contenido no hay mucho para donde hacerlo, o sea, que como vamos definiendo cada uno de los elementos del contenido, se van apilando dentro del StackPanel, a diferencia del Grid, no es necesario indicar posición por columna o renglón, ya que aquí, todo como se define, entra al StackPanel en órden.

El StackPanel define a una serie de elementos hijos en una SOLA línea que puede ser alineada vertical u horizontalmente.

Si copias el siguiente ejemplo de XAML dentro de tu ventana, verás la forma de funcionar del StackPanel:

  <StackPanel Name="SP1">

    <Border Background="Red" BorderBrush="Black" BorderThickness="1">

      <TextBlock Foreground="Black" FontSize="16">Hola</TextBlock>

    </Border>

      <Image Source="C:\Users\guillermom\Pictures\title.gif" Height="100" Width="100" />

    <Border Background="Orange" BorderBrush="Black" BorderThickness="1">

      <TextBlock Foreground="Black" FontSize="12">Developers</TextBlock>

    </Border>

    <Button Click="CambiarOrientacion" Width="80" Name="B1">Dame Click</Button>

  </StackPanel>

En este caso, la ventana producida con este XAML es:

Figura2

Como nos podemos dar cuenta en el XAML, el StackPanel está formado, en este ejemplo, por una serie de elementos: 2 etiquetas Border que definen a los cuadros donde están contenidos los textos “Hola”, la etiqueta Image de DevelopersDotNet (cuya imagen la pueden sacar de este mismo sitio) y “Developers”, además de un inofensivo botón que dice “Dame Click” que por el momento no hace nada. J

Esta es la idea del StackPanel, es un elemento que a medida que se van definiendo elementos hijos, automáticamente los va apilando uno tras otro en una secuencia que puede ser vertical u horizontal.

Vamos a ver el efecto precisamente de la propiedad Orientation de nuestro StackPanel cambiándola, para eso nos sirve nuestro botón. Como nos podemos dar cuenta, en la definición del Botón, tenemos un atributo llamado Click, en el cual defino que llame a un método de nombre: “CambiarOrientacion”

Vámonos a la sección de código de nuestra ventana y agreguemos el siguiente código:

Private Sub CambiarOrientacion(ByVal sender As Object, ByVal e As System.Windows.RoutedEventArgs)

        If SP1.Orientation = Orientation.Horizontal Then

            SP1.Orientation = Orientation.Vertical

        Else

            SP1.Orientation = Orientation.Horizontal

        End If

    End Sub

Como podemos observar en el código, lo que estamos haciendo es cambiar la propiedad orientación del SP1 (StackPanel) que estamos utilizando, al momento de echar a andar la aplicación y darle click al botón, veremos que la ventana cambia a esta forma:

Figura3 

Y viceversa…!!!! Si dan click al botón nuevamente, regresa a la forma anterior.

Maravilloso… Como se dan cuenta, automáticamente el StackPanel organiza ahora de izquierda a derecha cada uno de los elementos que están definidos como sus elementos hijos.

El DockPanel

El DockPanel, como los dos controles de los que les he platicado, sirve para organizar el contenido dentro de una ventana WPF, este control en particular, tiene la característica de que el contenido se organiza por medio de elementos hijos, los cuales se “pegan” (dock) a los bordes de la ventana. Basta con indicar el borde de la ventana al cual se “pegarán” cada uno de los contenidos y listo, tenemos la distribución de nuestra forma WPF lista. Es algo así como crear FRAMES de HTML.

Así como el GRID, les muestro el código necesario para crear un DockPanel y la diferencia de hacerlo con XAML que es mucho más sencillo:

DockPanel con Código (Otra vez ¡qué horror!):

        Dim myDockPanel As New DockPanel()

        myDockPanel.LastChildFill = True

 

        Dim myBorder1 As New Border()

 

        myBorder1.Background = Brushes.Silver

        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 = "Encabezado"

        myBorder1.Child = myTextBlock1

 

        Dim myBorder2 As New Border()

        myBorder2.Background = Brushes.Black

        myBorder2.BorderBrush = Brushes.Black

        myBorder2.BorderThickness = New Thickness(1)

        DockPanel.SetDock(myBorder2, Dock.Bottom)

        Dim myTextBlock2 As New TextBlock()

        myTextBlock2.Foreground = Brushes.White

        myTextBlock2.Text = "Pié de Página"

        myBorder2.Child = myTextBlock2

 

        Dim myBorder3 As New Border()

        myBorder3.Background = Brushes.Red

        myBorder3.BorderBrush = Brushes.Black

        myBorder3.BorderThickness = New Thickness(1)

        DockPanel.SetDock(myBorder3, Dock.Left)

        Dim myTextBlock3 As New TextBlock()

        myTextBlock3.Foreground = Brushes.Black

        myTextBlock3.Text = "Menú"

        myBorder3.Child = myTextBlock3

 

        Dim myBorder4 As New Border()

        myBorder4.Background = Brushes.White

        myBorder4.BorderBrush = Brushes.Black

        myBorder4.BorderThickness = New Thickness(1)

        DockPanel.SetDock(myBorder4, Dock.Left)

        Dim mImagen As New Image

        mImagen.Width = 75

        mImagen.Height = 75

        Dim bi As New BitmapImage

        bi.BeginInit()

        bi.UriSource = New Uri("C:\Users\guillermom\Pictures\GoW.bmp")

        bi.EndInit()

        mImagen.Source = bi

        myBorder4.Child = mImagen

 

        Dim myBorder5 As New Border()

        myBorder5.Background = Brushes.LightBlue

        myBorder5.BorderBrush = Brushes.Black

        myBorder5.BorderThickness = New Thickness(1)

        Dim myTextBlock5 As New TextBlock()

        myTextBlock5.Foreground = Brushes.Black

        myTextBlock5.Text = "Contenido que se ajusta al espacio sobrante"

        myBorder5.Child = myTextBlock5

 

        myDockPanel.Children.Add(myBorder1)

        myDockPanel.Children.Add(myBorder2)

        myDockPanel.Children.Add(myBorder3)

        myDockPanel.Children.Add(myBorder4)

        myDockPanel.Children.Add(myBorder5)

        Me.Content = myDockPanel

 

TOODO esto para generar la siguiente ventana WPF:

Figura4 

No se asusten mis estimados, el logo es del juego Gears of War de XBOX 360, mi preferido al momento (mi gamertag es morgu ormex por si quieren entrarle a las guerras en línea algún día)

DockPanel con XAML (ahhh, mucho mejor J)

Mejor usemos XAML…!!!. Para producir exactamente la misma interfase gráfica de nuestra ventana WPF, necesitamos:

<DockPanel LastChildFill="True">

    <Border Background="Silver" BorderBrush="Black" BorderThickness="1" DockPanel.Dock="Top">

      <TextBlock Foreground="Black">Encabezado</TextBlock>

    </Border>

    <Border Background="Black" BorderBrush="Black" BorderThickness="1" DockPanel.Dock="Bottom">

      <TextBlock Foreground="White">Pié de Página</TextBlock>

    </Border>

    <Border Background="Red" BorderBrush="Black" BorderThickness="1" DockPanel.Dock="Left">

      <TextBlock Foreground="Black">Menú</TextBlock>

    </Border>

    <Border Background="White" BorderBrush="Black" BorderThickness="1" DockPanel.Dock="Left">

      <Image Source="C:\Users\guillermom\Pictures\GoW.bmp" Width="75" Height="75"/>

    </Border>

    <Border BorderBrush="Black" BorderThickness="1" Background="LightBlue">

      <TextBlock Foreground="Black">Contenido que se ajusta al espacio sobrante</TextBlock>

    </Border>

  </DockPanel>

Y YA..!!! (En serio, pruébenlo). Vean como aquí a diferencia del Grid que es por celdas y coordenadas, y del StackPanel, cuya distribución es automática y en una sola línea vertical y horizontal, el DockPanel nos deja acomodar varios elementos hijos de la manera en que nosotros vayamos necesitando, no es necesario ponerlos en orden como en el StackPanel, si no, sólo especificar la propiedad: DockPanel.Dock (cuyos valores son: LEFT, TOP, RIGHT y BOTTOM) de cada uno de dichos elementos, en este caso, agregamos varios elementos Border y un Image para conseguir el mismo resultado que con código, pero con menos líneas. También, cabe resaltar que la propiedad LastChildFill del DockPanel puesta en “true”, hace que automáticamente el último elemento agregado, en este caso, el último Border al cual no le puse el DockPanel.Dock se ajuste al tamaño que “sobra” llenando el espacio cobrante de la ventana.

El Canvas

El último de los controles de los que voy a platicar en este artículo es el Canvas, este amigo tiene la característica de que podemos posicionar cualquier elemento dentro de él por medio de las clásicas propiedades Top y Left de cada elemento y que en este caso, estas propiedades se denominan Canvas.Left y Canvas.Top. Así pues, un ejemplo sencillo del uso del Canvas es el siguiente:

    <Canvas>

      <Canvas.Background>

            <LinearGradientBrush StartPoint="0,0" EndPoint="1,1">

              <GradientStop Color="#CCCCFF" Offset="0" />

              <GradientStop Color="AliceBlue" Offset="1" />

            </LinearGradientBrush>

      </Canvas.Background>

      <TextBlock FontSize="12" Canvas.Top="100" Canvas.Left="10">Posiciones Absolutas</TextBlock>

      <TextBlock FontSize="16" Canvas.Top="180" Canvas.Left="100">Pintando en el Canvas</TextBlock