Traducción aproximada del artículo Getting started with Silverlight: Part 2 – Defining the UI layout and Navigation publicado el 6 de Octubre del 2009 en inglés por Tim Heuer.


Esta es la segunda parte de una serie introductoria sobre desarrollo con Silverlight. Las soluciones resultantes de la aplicación en este ejemplo están disponibles para descarga en C# y VisualBasic.

Un aspecto importante para tener éxito con Silverlight es entender cómo diseñar aplicaciones con XAML. A menos que ya sean maestros del CSS, éste es uno de los más grandes retos para la mayoría de los que vienen del mundo de sitios de web.


Familiarizándonos con las opciones de diseño

Silverlight provee un sistema flexible para la distribución de elementos en la interfase al usuario. Hay modelos para estilos de colocación dinámicos o absolutos. Entre los varios controles para organizar la interfase, los más comunes son:

  • Canvas
  • StackPanel
  • Grid

Examinemos cómo trabaja cada uno de estos controles viendo los resultados de poner elementos dentro de ellos. Unos cuantos simples botones son suficientes para este propósito. Para hacerlo más simple, vamos a usar el mismo proyecto con el que empezamos en la parte anterior y demostrar los controles en la página Home.xaml (el código para esta demostración será luego eliminado, así que se puede usar cualquier lugar para experimentar por ahora).


Canvas

El lienzo (Canvas) es el contenedor más básico y se usa para colocar elementos en posiciones absolutas mediante coordenadas. Para posicionar los elementos se usan propiedades adjuntas(Attached Properties). Propiedades adjuntas son aquellas que pertenecen al contenedor (en este caso Canvas) pero que son extendidas o heredadas a los elementos contenidos (los botones, en este ejemplo). Así es como podemos posicionar varios botones en un Canvas:


<Canvas>
    <Button Canvas.Top="50"
            Canvas.Left="50"
            Content="Botón 1"
            FontSize="18"
            Width="150"
            Height="45" />

    <Button Canvas.Top="150"
            Canvas.Left="20"
            Content="Botón 2"
            FontSize="18"
            Width="150"
            Height="45" />

    <Button Canvas.Top="70"
            Canvas.Left="80"
            Canvas.ZIndex="99"
            Content="Botón 3"
            FontSize="18"
            Width="150"
            Height="45" />
</Canvas>


Al ejecutar se verá de esta manera:


Diseño con el elemento Canvas.


Como se puede ver, este tipo de arreglo visual es absoluto. El código muestra que también tenemos disponible una propiedad adjunta llamada ZIndex que es la responsable por el traslape entre dos botones. La propiedad puede ser útil en juegos o en situaciones que simulan efectos físicos requiriendo posicionamiento muy específico. El lienzo es útil para cuando las cosas no se mueven mucho o cuando se tiene buen control del tamaño de la aplicación. Para los demás casos, es difícil justificar su uso en vez de un StackPanel o Grid.


StackPanel

La traducción literal de StackPanel, panel de apilamiento, describe exactamente su comportamiento, pues apila los elementos en dirección vertical (predeterminada) u horizontal. Usando el ejemplo anterior:


<StackPanel>
    <Button Margin="10"
            Content="Botón 1"
            FontSize="18"
            Width="150"
            Height="45" />

    <Button Margin="10"
            Content="Botón 2"
            FontSize="18"
            Width="150"
            Height="45" />

    <Button Margin="10"
            Content="Botón 3"
            FontSize="18"
            Width="150"
            Height="45" />
</StackPanel>


creando el siguiente arreglo:


Diseño con el elemento StackPanel.


si cambiamos la dirección de apilamiento con el atributo Orientation (la única diferencia con el caso anterior):


<StackPanel Orientation="Horizontal">
    <Button Margin="10"
            Content="Botón 1"
            FontSize="18"
            Width="150"
            Height="45" />

    <Button Margin="10"
            Content="Botón 2"
            FontSize="18"
            Width="150"
            Height="45" />

    <Button Margin="10"
            Content="Botón 3"
            FontSize="18"
            Width="150"
            Height="45" />
</StackPanel>


obtenemos:


Diseño con el elemento StackPanel en dirección horizontal.


El StackPanel provee una manera sencilla de organizar elementos uno sobre, o a la par, del otro sin tener que complicarse mucho sobre su posicionamiento en el contenedor.


Grid

En la mayoría de los casos la cuadrícula (Grid) es el arreglo más versátil (la mayoría, pero no siempre). Tal y como el nombre lo dice, Grid está organizado en filas y columnas. Sin embargo, no es lo mismo que el elemento <table> con contenido dentro etiquetas <tr> y <td> al que están acostumbrados los que han trabajado en diseño de Web. En el Grid uno define primero la estructura de la cuadrícula y luego se usan propiedades adjuntas para indicarle al contenido dónde debe ubicarse.

Examinemos el código siguiente (nótese que he hecho visibles las líneas de la cuadrícula para resaltar la estructura, pero no es lo común):


<Grid ShowGridLines="True">
    <Grid.RowDefinitions>
        <RowDefinition Height="65" />
        <RowDefinition Height="65" />
        <RowDefinition Height="65" />
    </Grid.RowDefinitions>

    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="175" />
        <ColumnDefinition Width="175" />
        <ColumnDefinition Width="175" />
    </Grid.ColumnDefinitions>

    <Button Grid.Column="0"
            Grid.Row="0"
            Margin="10"
            Content="Botón 1"
            FontSize="18"
            Width="150"
            Height="45" />

    <Button Grid.Column="2"
            Grid.Row="0"
            Margin="10"
            Content="Botón 2"
            FontSize="18"
            Width="150"
            Height="45" />

    <Button Grid.Column="1"
            Grid.Row="2"
            Margin="10"
            Content="Botón 3"
            FontSize="18"
            Width="150"
            Height="45" />
</Grid>


Hemos definido un Grid con tres filas y tres columnas asignando alturas y anchuras específicas. Noten también que hemos puesto los elementos Button en lugares específicos de la cuadrícula usando las propiedades adjuntas. El arreglo resultante se ve así:


Diseño con el elemento Grid.


Las propiedades adjuntas Grid.Column y Grid.Row le dicen al elemento dónde debe ubicarse en el contenedor. Es en esta labor de organización visual donde Expression Blend es sumamente útil. Obsérvese como en Blend se pueden usar guías para especificar visualmente la estructura de filas y columnas y luego generar automáticamente el código XAML:


Blend ofrece guías visuales para configurar el elemento Grid.


Las flechas señalan las guías de la cuadrícula y al ícono indicador que la dimensiones son fijas (el candado). De hecho, la plantilla predeterminada que escogimos usó todos estos tipos de controles de arreglo visual para crear el armazón básico de nuestra aplicación.


Creando nuestra aplicación para Twitter

Podemos al fin empezar a construir nuestro programa. Este es el boceto general de lo que queremos crear:


Boceto de la aplicación a crear.


Noten cómo hemos asignado un espacio para que el usuario teclee los términos para la búsqueda, con los resultados deseados en una especie de lista. También tendremos un área de navegación con enlaces a otras vistas como por ejemplo un historial de términos buscados y tal vez algunas estadísticas.

Afortunadamente, la plantilla que usamos para generar una aplicación de navegación ya ha hecho por nosotros mucho del trabajo pesado en el diseño. Voy a hacer algunos cambios en MainPage.xaml. En la línea 22, remuevo el logo de la aplicación y luego cambio ApplicationNameTextBlock a “Monitor de Búsquedas en Twitter”.


Cambios en MainPage.xaml.


Dentro de poco volveremos a la navegación, pero primero vamos a crear las vistas. Para añadir una nueva página llamada Busqueda.xaml, hacemos clic derecho en en la carpeta Views del proyecto en Visual Studio y seleccionamos Página de Silverlight en el cuadro Agregar Nuevo Elemento:


Creando la nueva vista: Busqueda.xaml.


El resultado debe ser una nueva página en blanco con un control Grid para el arreglo visual. Aquí es donde crearemos la página de búsqueda que vimos antes. El encabezado es suplido por el elemento Frame que aloja la navegación en MainPage.xaml.


Infraestructura de Navegación de Silverlight

Desviémonos un momento para considerar la infraestructura de navegación (navigation framework) de Silverlight. Si recuerdan, usamos la plantilla de aplicación de navegación al generar la solución. Esta plantilla nos dio MainPage.xaml y las vistas Home y About. La infraestructura de navegación está compuesta de tres partes fundamentales: UriMapper, Frame y Page.


UriMapper

La manera en que prefiero ver el UriMapper es como una especie de generador de rutas. Aunque no es requerido, pienso que es útil para disimular y simplificar la trayectoria al destino de la navegación. El URI para Home.xaml es literalmente “/Views/Home.xaml”, pero en vez de ponerla al descubierto, se puede convertir a algo más sencillo y legible como “/Home”. Así evitamos dar detalles sobre la configuración técnica que inclusive luego podría cambiar a otra ruta diferente. Encontraremos a UriMapper como un elemento de Frame en MainPage.xaml:


<navigation:Frame x:Name="ContentFrame"
                  Style="{StaticResource ContentFrameStyle}"
                  Source="/Home"
                  Navigated="ContentFrame_Navigated"
                  NavigationFailed="ContentFrame_NavigationFailed">

    <navigation:Frame.UriMapper>

        <uriMapper:UriMapper>
            <uriMapper:UriMapping Uri=""
                                  MappedUri="/Views/Home.xaml" />

            <uriMapper:UriMapping Uri="/{pageName}"
                                  MappedUri="/Views/{pageName}.xaml" />
        </uriMapper:UriMapper>

    </navigation:Frame.UriMapper>

</navigation:Frame>


En este caso el UriMapper es declarado en XAML, pero también puede existir en forma de recurso y ser añadido al elemento Frame de esta manera (suponiendo que el nombre del recurso es UriMapperRoutes):


<navigation:Frame x:Name="ContentFrame"
                  Style="{StaticResource ContentFrameStyle}"
                  Source="/Home"
                  Navigated="ContentFrame_Navigated"
                  NavigationFailed="ContentFrame_NavigationFailed"
                  UriMapper="{StaticResource UriMapperRoutes}" />


Por el momento seguiremos con la versión generada por la plantilla.


Frame

Los que son desarrolladores de ASP.NET pueden comparar el elemento Frame al control ContenPlaceHolder. El Frame se define como el área que puede ser navegada. Uno especifica una vista predeterminada pero luego la navegación afectará el contenido del área como veremos más adelante. Examinando el código anterior, podemos ver cómo la vista predeterminada para nuestra aplicación es la ruta “/Home” asignada al atributo Source.


Page

El último componente fundamental de la navegación es el elemento Page que acabamos de crear en el último paso. Éste es básicamente el área de contenido desplegado en el Frame. Page es parecido al UserControl usado normalmente (por ejemplo MainPage es un UserControl), pero es especial en que puede interactuar con la navegación. En nuestra aplicación vamos a considerar los elementos Page como vistas.

Se puede aprender más sobre navegación en este vídeo con una explicación detallada:

En realidad, la navegación es fácil de entender una vez que se experimenta con ella un poco. También es muy potente. Éste es el mecanismo que nos permite crear enlaces directos en aplicaciones Silverlight.


Creando la interfase al usuario para nuestra vista de búsqueda

Continuemos ahora con la interfase gráfica para la página Busqueda.xaml que acabamos de crear. Para ahora puede que algunos se pregunten qué son todos esos elementos {StaticResource XXXXXXXX} en el XAML. Los explicaremos al llegar a la sección 5 sobre estilización y creación de plantillas. Por el momento vamos a ignorarlos.

Revisando nuestro boceto, podemos ver que se necesita un área para la entrada de texto, un botón y una cuadrícula para enseñar los resultados. Empecemos por acomodar esos controles en la página Busqueda.xaml usando Blend. Se puede hacer desde Visual Studio por medio de un clic derecho y luego seleccionando la opción Abrir en Expression Blend:


Con un clic derecho en un archivo Xaml se puede escoger editarlo en Blend.


Blend y Visual Studio comparten la misma estructura para proyectos, por lo que se pueden abrir archivos en ambos simultáneamente permitiendo editar el XAML de manera visual antes de comenzar a escribir código.

En Blend vamos a configurar el Grid con dos filas, una para el campo de búsqueda y el botón, y la segunda para desplegar los resultados. Arrastramos un StackPanel a la fila superior y, luego de configurarlo para que sea horizontal, le ponemos un TextBox y un Button.

Lo siguiente que vamos a hacer es agregar un DataGrid para enseñar los datos. DataGrid no es un control interno; más bien es parte del kit de desarrollo por lo que hay que añadir una referencia. Aunque esto se puede hacer de varias formas, en este caso Blend lo hará automáticamente por nosotros. En la barra de herramientas hacemos clic en el signo ‘>>’ luego buscamos el DataGrid:


SelectingDataGrid


Una vez visible, arrastramos el control a la segunda fila. Esta acción automáticamente agrega la referencia a System.Windows.Controls.Data.dll. También introduce una declaración en XAML:


<navigation:Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                 mc:Ignorable="d"
                 xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
                 xmlns:data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"
                 x:Class="MonitorTwitter.Views.Busqueda"
                 d:DesignWidth="640"
                 d:DesignHeight="480"
                 Title="Busqueda Page">

    <Grid x:Name="LayoutRoot">
        <Grid.RowDefinitions>
            <RowDefinition Height="32" />
            <RowDefinition />
        </Grid.RowDefinitions>

        <StackPanel Orientation="Horizontal">
            <TextBox x:Name="Termino"
                     TextWrapping="Wrap"
                     Margin="0,0,20,0"
                     Width="350"
                     Text="Termino de busqueda"
                     FontSize="18.667" />
            <Button x:Name="Buscar"
                    Height="28"
                    HorizontalAlignment="Left"
                    Margin="0,0,20,0"
                    Width="100"
                    RenderTransformOrigin="0.5,0.536"
                    Content="BUSCAR" />
        </StackPanel>

        <data:DataGrid x:Name="Resultados"
                       Grid.Row="1"
                       Margin="0,10,0,0" />

    </Grid>
</navigation:Page>


Nótese la línea 8 donde se añade xmlns:data con un nuevo espacio de nombres para controles adicionales a los internos. Más abajo se usa este ámbito en la declaración data:DataGrid de la línea 21. Si se han seguido todos los pasos su XAML debe ser muy similar al de arriba. El arreglo visual se ve así:


Arreglo visual de la página de búsqueda.


Tal vez notaron en el XAML que ya asigné nombres al TextBox (Termino), al Button (Buscar) y al DataGrid (Resultados). Esto nos ayudará luego cuando tengamos que referirnos a los componentes en el código.

Para poder agregar la referencia al control DataGrid, Blend tuvo que alterar el proyecto. Por eso, al volver a Visual Studio se nos pregunta si queremos recargarlo, a lo que debemos contestar afirmativamente. Esto demuestra lo bien integradas que están ambas herramientas en el manejo de los archivos de proyecto. Vamos ahora a empezar a codificar en Visual Studio.


Cambiando UriMapper para que al inicio apunte hacia Busqueda.xaml

Ya que tenemos la página de búsqueda lista (que es prácticamente la página principal de la aplicación), hagamos algunos cambios a la infraestructura de navegación. En el elemento Frame de MainPage.xaml, vamos a cambiar la ruta inicial de “/Home.xaml” a “/Busqueda.xaml” junto con otros ajustes relacionados. El elemento Frame debe verse así después de los cambios:


<navigation:Frame x:Name="ContentFrame"
                  Style="{StaticResource ContentFrameStyle}"
                  Source="/Busqueda"
                  Navigated="ContentFrame_Navigated"
                  NavigationFailed="ContentFrame_NavigationFailed">
    <navigation:Frame.UriMapper>
        <uriMapper:UriMapper>
            <uriMapper:UriMapping Uri=""
                                  MappedUri="/Views/Busqueda.xaml" />
            <uriMapper:UriMapping Uri="/{pageName}"
                                  MappedUri="/Views/{pageName}.xaml" />
        </uriMapper:UriMapper>
    </navigation:Frame.UriMapper>
</navigation:Frame>


Como ya no vamos a usar Home.xaml podemos borrar el archivo. A continuación añadimos una nueva vista llamada Historial.xaml e introducimos un enlace a esta página en MainPage.xaml.


<Border x:Name="LinksBorder"
        Style="{StaticResource LinksBorderStyle}">
    <StackPanel x:Name="LinksStackPanel"
                Style="{StaticResource LinksStackPanelStyle}">

        <HyperlinkButton x:Name="Link1"
                         Style="{StaticResource LinkStyle}"
                         NavigateUri="/Busqueda"
                         TargetName="ContentFrame"
                         Content="página principal"/>

        <Rectangle x:Name="Divider1"
                   Style="{StaticResource DividerStyle}" />

        <HyperlinkButton x:Name="Link2"
                         Style="{StaticResource LinkStyle}"
                         NavigateUri="/Historial"
                         TargetName="ContentFrame"
                         Content="historial" />

        <Rectangle x:Name="Divider2"
                   Style="{StaticResource DividerStyle}"/>

        <HyperlinkButton x:Name="Link3"
                         Style="{StaticResource LinkStyle}"
                         NavigateUri="/About"
                         TargetName="ContentFrame"
                         Content="acerca de"/>

    </StackPanel>
</Border>


Al ejecutar la aplicación debería verse así:


Apariencia final de la aplicación.


Con el diseño básico de la interfase al usuario ya listo, empezaremos a añadir datos en la tercera parte.

Etiquetas asignadas:
 

16 Respuestas a “Introducción a Silverlight – Parte 2: Diseño de la interfase al usuario y navegación”

  1. IAMJAVI.com dice:

    Muy buena introduccion, llevo cerca de un año desarrollando en Silverligh y puedo decirte que no estoy muy de acuerdo respecto al tipo de contenedores mas utilizados, GRID es un contenedor que consume muchisimos recursos, y realmente con un poco de pericia Stackpanel sirve para todo :)

    Saludos, y sigue asi.

    • David Mora dice:

      Javi,

      Gracias por tu aporte. Es siempre interesante conocer la experiencia de otros desarrolladores. Imagino que tal como el <div> se puede usar en sustitución del <table> en HTML, el StackPanel es un hábil sustituto del Grid en muchos casos.

  2. Excelente aporte, David. Si bien trabajo desde hace varios con .net, hace muy poco que comencé a probar SilverLight y, la verdad sea dicha, al principio cuesta bastante agarrarle la mano.

    Este tutorial me voló varias dudas de un saque.

    Gracias.

  3. David Mora dice:

    Antonio,

    Gracias por el comentario. Silverlight es fantástico pero, como dices certeramente, algunos aspectos requieren más esfuerzo para aprenderlos por ser poco conocidos.

    Si sientes que hay algún tema específico que podría tener mejor cobertura, asegúrate de plantearlo en el área de comentarios, o en la caja de sugerencias a la derecha de la página.

    Por el momento, me alegra que el artículo de Tim te haya sido útil.

  4. Brian Koo dice:

    Súper didáctico, muchas gracias por el aporte David. Había dejado de trabajar en .NET hace como 2 años y retome la experiencia utilizando Silverlight, me estaba costando un poco hasta que ubiqué este tutorial. Nuevamente muchas gracias, espero que a medida mejore mi expertiz pueda aportar a la comunidad.

    Saludos,

    • David Mora dice:

      Brian,

      ¡Qué bueno que te ayudó a ubicarte un poco de nuevo en el mundo de .NET! Gracias por el comentario. No obstante, la mayoría del crédito debe ir a Tim Heuer quien escribió el texto original. Si lo deseas, te puedes sentir libre de dejar un comentario en su blog (sin importar si es en español).

      Que te vaya bien en tus aventuras con Silverlight.

  5. Juan Carlos dice:

    Hola David, de nuevo yo.
    Tengo una duda, mas bien un error y no entiendo a que se debe.
    Cuando dices…

    “Blend y Visual Studio comparten la misma estructura para proyectos, por lo que se pueden abrir archivos en ambos simultáneamente permitiendo editar el XAML de manera visual antes de comenzar a escribir código.”

    pues eso… intento abrir desde Visual Studio 2010 el formulario Busqueda.xaml y en Blend 3 recibo el siguiente error:

    - El formato de la solución “MonitorTwitterVB.sln” no está admitido y pueda que no se abra correctamente.
    ¿Desea abrir la solución de todos modos?

    Si le pongo que Si, me sigue dando otros errores y termina con que no se puede abrir la aplicación.

    ¿Cual podría ser el problema?

    Desde ya muchas gracias.
    Saludos.

    • David Mora dice:

      Juan Carlos,

      La raíz del problema es la combinación específica de Visual Studio 2010 y Blend 3. Tal vez el artículo no es lo suficientemente específico. El asunto es que Blend 3 sólo puede abrir soluciones y proyectos en el formato de Visual Studio 2008. Si quieres editar una solución de Visual Studio 2010 tienes que usar Blend 4.

      Por el momento todavía puedes descargar la versión gratuita de prueba de Blend 4. Al parecer la versión final sale en un poco más de una semana. Si tienes una licencia de Blend 3 entonces puedes obtener la nueva versión gratis también.

      Saludos.

      • Juan Carlos dice:

        Efectivamente David, ese era el problema.
        Y con respecto al artículo, pues dejame decirte que el mismo es excelente. El poco claro soy yo. Te felicito y seguí adelante.

        Mil gracias de nuevo.

        Saludos.

  6. [...] pueden añadir una similar usando xmlns en Busqueda.xaml tal y como hicimos con DataGrid en la parte 2. Luego insertamos el control como contenedor base y ponemos el Grid adentro. El archivo [...]

  7. Diego PasMan dice:

    Hola… hay una tercera parte de este Introductorio???.. cual es el link?… Gracias…

  8. Oscar Zarate dice:

    Hola David,

    Muchas gracias por el aporte/traduccion.

    Me parece que hay un “typo” en donde dice “Las propiedades adjuntas Grid.Button y Grid.Row le dicen al elemento”
    Te refieres a “Las propiedades adjuntas Grid.COLUMN y Grid.Row le dicen al elemento”?

    SaludOZ,

  9. Baquiax dice:

    Mira yo, tambien estoy empezando con Silverlight, y este tutorial me aclaro muchas cosas, y a la vez me genero mas dudas.
    Pero ahi voy, espero que puedas publicar algo sobre el, Patron de Diseño Observer en Silverlight, y como lo debo implementar.
    Gracias.

  10. Anthony dice:

    Hola amigo, te cuento que recien estoy llevando o usando Silverlight, y bueno, con mi profesor, estamos usando bases de datos.

    Pero a mi no me sale el complemento ADO.NET ENTITY DATA MODEL.

    Necesito saber como o que me falta para tenerlo.

    Gracias

Responder



Licencia de uso

El contenido de las traducciones está sujeto a los términos de protección de derechos de uso de los autores originales quienes han autorizado su publicación en este blog. Asegúrese de entender los terminos de la licencia de cada autor antes de usar tal contenido.

Mis propios artículos son publicados bajo los términos de la Licencia Reconocimiento-Compartir bajo la misma licencia 3.0 Estados Unidos de Creative Commons:

Creative Commons License
Blog de Maromas Digitales by Maromas Digitales, LLC is licensed under a Creative Commons Reconocimiento-Compartir bajo la misma licencia 3.0 Estados Unidos License.

License

The contents of all translated articles is subject to the copyright and licensing terms of the original authors and has been published here with their express permission. Verify the original author's licensing terms before using the contents of these articles.

My own articles are licensed under the Creative Commons Attribution-Share Alike 3.0 United States License:

Creative Commons License
Blog de Maromas Digitales by Maromas Digitales, LLC is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.