Windows Phone 8 de manera concisa: La interfaz de usuario
() translation by (you can also view the original English article)
¿Que es XAML?
XAML es el acrónimo de Lenguaje de Marcado de Aplicaciones Extensible. Es un lenguaje de marcado basado en XML, y su propósito y filosofía son muy similares a HMTL. Cada control que puede ser colocado en una página, ya sea un botón, un cuadro de texto, o controles personalizados, es identificado por una etiqueta específica XML. Como XML, la estructura es jerárquica, puedes colocar etiquetas dentro de otras etiquetas. Por ejemplo, ésta estructura jerárquica es como puedes definir el maquetado de una página, gracias a algunos controles que actúan como un contenedor para otros controles, como Grid
o StackPanel
.
El siguiente es un ejemplo del XAML que define una página de Windows Phone:
1 |
<phone:PhoneApplicationPage
|
2 |
x:Class="FirstApp.MainPage" |
3 |
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" |
4 |
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" |
5 |
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone" |
6 |
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone" |
7 |
xmlns:d="http://schemas.microsoft.com/expression/blend/2008" |
8 |
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" |
9 |
mc:Ignorable="d" |
10 |
FontFamily="{StaticResource PhoneFontFamilyNormal}" |
11 |
FontSize="{StaticResource PhoneFontSizeNormal}" |
12 |
Foreground="{StaticResource PhoneForegroundBrush}" |
13 |
SupportedOrientations="Portrait" Orientation="Portrait" |
14 |
shell:SystemTray.IsVisible="True"> |
15 |
|
16 |
<Grid x:Name="LayoutRoot" Background="Transparent"> |
17 |
<Grid.RowDefinitions>
|
18 |
<RowDefinition Height="Auto"/> |
19 |
<RowDefinition Height="*"/> |
20 |
</Grid.RowDefinitions>
|
21 |
|
22 |
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> |
23 |
<StackPanel>
|
24 |
<TextBlock Text="This is a page" /> |
25 |
</StackPanel>
|
26 |
</Grid>
|
27 |
</Grid>
|
28 |
</phone:PhoneApplicationPage>
|
PhoneApplicationPage
es la clase base de una página de Windows Phone. Como puedes ver, cada control es colocado dentro de ella. Ten en cuenta también que el atributo x:Class
, identifica cuál es la clase detrás del código que está conectada a ésta página. En éste ejemplo, el código que puede interactuar con la página será almacenado en una clase llamada MainPage
que es parte del spacename (espacio de nombres) FirstApp
. Podemos ver ésta clase simplemente al dar click en la flecha negra cerca del archivo XAML en el Solution Explorer (Explorador de Soluciones). Verás otro archivo con el mismo nombre del XAML más la extensilón .cs
.



Comencemos analizando éste simple XAML para introducir algunos conceptos clave, como espacio de nombres y recursos.
Namespaces (Espacio de nombres)
Ya deberías estar familiarizado con espacio de nombres; son una forma de estructurar tu código al asignar una ruta lógica a tu clase.
Por defecto, Visual Studio asigna espacio de nombres usando la misma estructura de directorios del proyecto. Ésto significa que si, por ejemplo, tienes una clase llamada MyClass
almacenada dentro de un archivo en el directorio Classes
, el namespace completo por defecto de tu clase será Classes.MyClass
.
Namespaces en XAML trabajan exactamente de la misma manera. Los controles XAML son, al final, clases que son parte de tu proyecto, así que tienes que indicar a la página donde puede encontrarlas. En la página estándar puedes ver muchos ejemplos de declaraciones de namespace:
1 |
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone" |
Cada namespace comienza con el prefijo xmlns
, que es el namespace XML estándar, seguido por un prefijo personalizado (en éste ejemplo es phone
). Éste prefijo es muy importante, porque es el que vamos a usar en el resto de la página para añadir los controles. Luego, definimos el namespace completo que contiene los controles. Si la clase es parte de nuestro proyecto, es suficiente especificar solo el namespace; de otra forma, también necesitamos definir que ensamblado (el nombre del DLL) contiene la clase.
En el ejemplo anterior, queremos incluir controles y recursos en nuestra página que son definidos dentro del namespace Microsoft.Phone.Controls
, que es incluido en la librería Microsoft.Phone.dll
.
La clase PhoneApplicationPage
te da un ejemplo de como usar un namespace. Ya que la clase PhoneApplicationPage
es parte del namespace Microsoft.Phone.Controls
, tenemos que agregar el prefijo phone
a la etiqueta para usarla:
1 |
<phone:PhoneApplicationPage /> |
Es muy importante entender como funcionan los namespaces en XML, porque necesitaremos declararlos cada vez que usemos controles de terceros (que creamos nosotros mismos o son parte de una librería externa) o recursos, como convertidores.
Propiedades y Eventos
Cada control puede ser personalizado de dos formas: al establecer propiedades y acciones. Ambos son identificados por atributos de la etiqueta XAML, pero tienen dos propósitos diferentes.
Properties (Propiedades) son usadas para cambiar el aspecto o el comportamiento del control. Generalmente, una propiedad es simplemente establecida al asignar un valor al atributo específico. Por ejemplo, si queremos asignar un valor a la propiedad Text
de un control TextBlock
, podemos hacerlo de la siguiente forma:
1 |
<TextBlock Text="This is a text block" /> |
Hay también una sintaxis extendida que puede ser usada en el caso de una propiedad compleja que no pueda ser definida con un string (cadena de caracteres) normal. Por ejemplo, si necesitamos establecer una imagen como un fondo de un control, necesitamos usar el siguiente código:
1 |
<Grid>
|
2 |
<Grid.Background>
|
3 |
<ImageBrush ImageSource="/Assets/Background.png" /> |
4 |
</Grid.Background>
|
5 |
</Grid>
|
Propiedades complejas son establecidas al usar una etiqueta anidada con el nombre del control más el nombre de la propiedad, separado por un punto (para establecer la propiedad Background
del control Grid
, usamos la sintaxis Grid.Background
).
Una propiedad importante que es compartida por cada control es x:Name
, que es un string que unívocamente identifica el control en la página. No puedes tener dos controles con el mismo nombre en una página. Establecer ésta propiedad es muy importante si necesitas interactuar con el control en el code behind (código asociado a la página)-podrás referirte a ella al usar su nombre.
Events (Eventos) son una forma de manejar interacciones del usuario con el control. Uno de los más utilizados es Tap
, que es desencadenado cuando los usuarios presionan el control.
1 |
<Button Tap="OnButtonClicked" /> |
Cuando definimos una acción, Visual Studio automáticamente te pedirá crear un event handler (manejador de eventos), que es el método (declarado en el code-behind) que se ejecuta cuando el evento es desencadenado.
1 |
private void OnButtonClicked(object sender, GestureEventArgs e) |
2 |
{
|
3 |
MessageBox.Show("Hello world"); |
4 |
}
|
En el ejemplo previo, mostramos el clásico mensaje "Hello world" a los usuarios cuando es presionado el botón.
Resources (Recursos)
Como en el HTML, podemos definir los estilos CSS que pueden ser reutilizados en diferentes partes del sitio web o la página. XAML ha introducido el concepto de resources (recursos) que pueden ser aplicados a diferentes controles en una aplicación.
Básicamente cada control XAML soporta la etiqueta Resources
: gracias a la estructura jerárquica, cada control anidado podrá usarlo. En el mundo real, hay dos lugares comunes para definir un recurso: en la página y en el nivel de aplicación.
Page resources (recursos de página) son definidos en una página y están disponibles para todos los controles que son parte de la página. Son colocados en una propiedad específica llamada Resources
de la clase PhoneApplicationPage
.
1 |
<phone:PhoneApplicationPage.Resources>
|
2 |
<!-- you can place resources here -->
|
3 |
</phone:PhoneApplicationPage.Resources>
|
Application resources, (recursos de aplicación), en cambio, están globalmente disponibles y pueden ser usados dentro de cualquier página de la aplicación. Son definidos en el archivo App.xaml
, y la plantilla estándar ya incluye la definición necesaria.
1 |
<Application.Resources>
|
2 |
<!-- here you can place global resources -->
|
3 |
</Application.Resources>
|
Cada recurso es unívocamente identificado por un nombre que es asignado usando la propiedad x:Key
. Para aplicar un resource a un control, necesitamos introducir el concepto de markup extensions (extensiones de marcado). Éstas son extensiones especiales que nos permiten aplicar diferentes comportamientos que de otra forma necesitarían algo de código para funcionar adecuadamente. Hay muchas extensiones de marcado en el mundo XAML, y el necesario para aplicar un recurso es llamado StaticResource
. Aquí un ejemplo de como usarlo:
1 |
<TextBlock Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}" /> |
En éste ejemplo, el resource es aplicado a la propiedad Style
al incluir la palabra reservada StaticResource
dentro de llaves, seguido por el nombre del resource que es el valor de la propiedad x:Key
.
Resources pueden también ser definidos en un archivo externo llamado ResourceDictionary
si quieres organizar mejor tu proyecto. Para hacer ésto, da click derecho en tu proyecto en Visual Studio, da click en Add > New Item, y elige XML file. Dále al archivo un nombre que finalice con una extensión .xaml e incluye la siguiente definición:
1 |
<ResourceDictionary
|
2 |
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" |
3 |
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> |
4 |
|
5 |
<!-- put resources here -->
|
6 |
|
7 |
</ResourceDictionary>
|
Ahora puedes agregarlo a tu proyecto al declararlo en el App.xaml
:
1 |
<Application.Resources>
|
2 |
<ResourceDictionary>
|
3 |
<ResourceDictionary.MergedDictionaries>
|
4 |
<ResourceDictionary Source="Assets/Resources/Styles.xaml" /> |
5 |
</ResourceDictionary.MergedDictionaries>
|
6 |
</ResourceDictionary>
|
7 |
</Application.Resources>
|
Nota la propiedad MergedDictionaries
: todos los archivos de resources (recursos) externos deben ser declarados aquí. De ésta forma, serán automáticamente fusionados y cada resource declarado en cada archivo externo estará disponible para cada página, como si fueron declarados en línea.
Veamos ahora, en detalle, cuáles son las clases más importantes de resources disponibles.
Estilos
Los estilos XAML funcionan de la misma manera que los estilos CSS: puedes definir los valores de diferentes propiedades juntos en un sólo estilo que puede ser aplicado a múltiples controles para que todos utilicen el mismo layout. Así es como se ve una definición de estilo:
1 |
<Style x:Key="CustomText" TargetType="TextBlock"> |
2 |
<Setter Property="FontSize" Value="24" /> |
3 |
<Setter Property="FontWeight" Value="Bold" /> |
4 |
</Style>
|
Un estilo es definido por una etiqueta Style
, que tiene dos atributos importantes: x:Key
, el nombre del estilo, y TargetType
, el tipo de controles que será adecuado para éste estilo.
Dentro de la etiqueta Style
puedes colocar cuantas etiquetas Setter
como quieras. Cada uno identifica una propiedad del control que quieres cambiar. Cada etiqueta Setter
necesita dos atributos: Property
es la propiedad del control que quieres cambiar, y Value
es el valor que quieres asignar a la propiedad.
El estilo definido en el ejemplo anterior puede aplicarse a cualquier control TextBlock
. Su propósito es cambiar el tamaño de la fuente a 24 y aplicar un estilo negrita al texto.
Hay también tipos especiales de estilos llamados implicit styles. Son definidos de la misma forma que los estilos en el ejemplo anterior, excepto que carecen del atributo x:Key
. En éste caso, el estilo es automáticamente aplicado a cada control que cumple con el valor de la propiedad TargetType
, de acuerdo al alcance donde ha sido definido el estilo. Si el estilo es definido como un recurso de página, será aplicado solo a los controles de la página; si el estilo se define como un recurso de aplicación, será aplicado a cada control en la aplicación.
Data Templates (Plantillas de Datos)
Plantillas de Datos son un tipo especial de recurso que puede ser aplicado a un control para definir su apariencia. Plantillas de datos con frecuencia se utilizan con controles que pueden mostrar colecciones de elementos, como ListBox
o LongListSelector
.
El siguiente ejemplo muestra una plantilla de datos:
1 |
<DataTemplate x:Key="PeopleTemplate"> |
2 |
<StackPanel>
|
3 |
<TextBlock Text="Name" /> |
4 |
<TextBlock Text="{Binding Path=Name}" /> |
5 |
<TextBlock Text="Surname" /> |
6 |
<TextBlock Text="{Binding Path=Surname}" /> |
7 |
</StackPanel>
|
8 |
</DataTemplate>
|
Una plantilla de datos simplemente contiene el XAML que será usado para renderizar el elemento específico. Si, por ejemplo, aplicamos ésta plantilla de datos para la propiedad ItemTemplate
de un control ListBox
, el resultado será que el definido XAML será repetido para cada elemento en la colección (por el momento, solo ignora la extensión de marcado Binding
; la abordaremos más adelante cuando hablemos sobre data binding o enlazado de datos).
Como otro recurso, las plantillas de datos pueden ser asignadas a una propiedad usando la extensión de marcado StaticResurce
.
1 |
<ListBox ItemTemplate="{StaticResource PeopleTemplate}" /> |
Animaciones
XAML es un lenguaje poderoso porque nos permite hacer más que solo crear el layout de las aplicaciones. Una de las características más interesantes es la de animación, que puede ser creada usando el control Storyboard
.
El control Storyboard
puede ser usado para definir diferentes tipos de animaciones:
-
DoubleAnimation
, puede ser usada para cambiar el valor numérico de una propiedad (por ejemplo,Width
oFontSize
). -
ColorAnimation
, que puede ser usado para interactuar con propiedades que definen un color (como dentro de unSolidColorBrush
). -
PointAnimation
, que puede ser aplicado a propiedades que definen una coordenada.
El siguiente código de ejemplo define una animación:
1 |
<Storyboard x:Name="Animation"> |
2 |
<DoubleAnimation Storyboard.TargetName="RectangleElement" |
3 |
Storyboard.TargetProperty="Width" |
4 |
From="200" |
5 |
To="400" |
6 |
Duration="00:00:04" /> |
7 |
</Storyboard>
|
Las primeras dos propiedades son heredadas del control Storyboard
: Storyboard.TargetName
es usado para establecer el nombre del control que vamos a animar, mientras Storyboard.TargetProperty
es la propiedad cuyo valor vamos a cambiar durante la animación.
Posteriormente, definimos el comportamiento de la animación: el valor inicial (la propiedad From
), el valor final (la propiedad To
) y la duración (la propiedad Duration
). El comportamiento definido en el ejemplo previo anima un control Rectangle
al incrementar su anchura de 200 a 400 en cuatro segundos.
Podemos también controlar la animación más profundamente al usar la variante UsingKeyFrames
disponible para cada tipo de animación:
1 |
<Storyboard x:Name="Animation"> |
2 |
<DoubleAnimationUsingKeyFrames Storyboard.TargetName="RectangleElement" |
3 |
Storyboard.TargetProperty="Width"> |
4 |
<LinearDoubleKeyFrame KeyTime="00:00:00" Value="200" /> |
5 |
<LinearDoubleKeyFrame KeyTime="00:00:02" Value="250" /> |
6 |
<LinearDoubleKeyFrame KeyTime="00:00:04" Value="500" /> |
7 |
</DoubleAnimationUsingKeyFrames>
|
8 |
</Storyboard>
|
De ésta manera puedes controlar el timing (ritmo o función de tiempo) de la animación. En el ejemplo anterior, el tipo de animación es el mismo (es DoubleAnimation
), pero podemos establecer, para un tiempo específico, que es el valor a aplicar usando la etiqueta LinearDoubleKeyFrame
.
En el ejemplo anterior, la Width
(Anchura) del control Rectangle
, es establecida en 200 al inicio. Luego, después de dos segundos, se incrementa a 250 y después de cuatro segundos, es establecida en 500.
Definiendo la Velocidad de las Animaciones
Otra forma de crear animaciones es utilizar fórmulas matemáticas que pueden aplicar un comportamiento realista a un objeto, como elasticidad, o aceleración y desaceleración. Pudieras lograr el mismo resultado al usar keyframes, pero requeriría mucho trabajo. Por ésta razón, el framework de animación ofrece una serie de funciones predefinidas para marcar la velocidad y que pueden aplicarse fácilmente a una animación.
Para añadir una función de defnir la velocidad, solo necesitas establecer la propiedad EasingFunction
de una animación, como se muestra en el siguiente ejemplo:
1 |
<Storyboard x:Name="EasingAnimation"> |
2 |
<PointAnimation From="0,0" To="0, 200" Duration="00:00:3" |
3 |
Storyboard.TargetName="Circle" |
4 |
Storyboard.TargetProperty="Center"> |
5 |
<PointAnimation.EasingFunction>
|
6 |
<BounceEase Bounces="2" EasingMode="EaseOut" /> |
7 |
</PointAnimation.EasingFunction>
|
8 |
</PointAnimation>
|
9 |
</Storyboard>
|
Después de que has definido una animación regular (en el ejemplo, es una PointAnimation
que mueve un objeto Ellipse
de las coordenadas (0,0) a (0,200), puedes establecer la propiedad EasingFunction
con una de las funciones disponibles para definir la velocidad. Éste ejemplo muestra como usar la función BounceEase
, que puede utilizarse para aplicar un efecto elástico al objeto (el número de estiramientos ejecutados se especifica con la propiedad Bounces
).
Otras funciones easing disponibles son:
-
BackEase
, que retrasa el movimiento de la animación ligeramente antes de que inicie. -
CircleEase
, que aplica una función circular a la animación aceleración. -
ElasticEase
, que crea una animación que asemeja a un muelle oscilante.
La documentación oficial MSDN muestra una lista completa de las funciones de definición de velocidad disponibles.
Como Controlar Animaciones
Las animaciones son tratadas como recursos. Pueden ser definidas como recursos locales, recursos de página, o recursos de aplicación. A diferencia de recursos tradicionales, los controles Storyboard
son identificados por la propiedad x:Name
, como un control regular.
El siguiente ejemplo muestra una animación que es establecida como un recurso de página:
1 |
<phone:PhoneApplicationPage.Resources>
|
2 |
<Storyboard x:Name="Animation"> |
3 |
<DoubleAnimation Storyboard.TargetName="RectangleElement" |
4 |
Storyboard.TargetProperty="Width" |
5 |
From="200" |
6 |
To="400" |
7 |
Duration="00:00:04" /> |
8 |
</Storyboard>
|
9 |
</phone:PhoneApplicationPage.Resources>
|
Gracias al identificador único, podrás controlar la animación en el code behind. Cada objeto Storyboard
ofrece muchos métodos para controlarlo, como Begin()
, Stop()
, o Resume()
. En el siguiente código puedes ver los manejadores de eventos asignaos a dos botones que son usados para iniciar y detener la animación.
1 |
private void OnStartClicked(object sender, GestureEventArgs e) |
2 |
{
|
3 |
Animation.Begin(); |
4 |
}
|
5 |
|
6 |
private void OnStopClicked(object sender, GestureEventArgs e) |
7 |
{
|
8 |
Animation.Stop(); |
9 |
}
|
Data Binding (Enlazado de Datos)
Data binding es una de las características más poderosas proporcionaas por XAML. Con data binding, podrás crear un canal de comunicación entre el elemento UI y varias fuentes de datos, que puede ser otro control o una propiedad en una de tus clases. Más aún, data binding está muy conectado al sistema de notificación XAML (que detallaremos más adelante) así que cada vez que cambias algo en tu objeto, el control que lo despliega será automáticamente actualizado para reflejar los cambios y mostrar el nuevo valor.
Cuando creas un canal de comunicacion que usa data binding, defines una source
(fuente) (que define los datos a mostrar) y un targe
t (que se encarga de desplegar los valores). Por defecto, el canal de enlazado es establecido en modo OneWay
. Ésto significa que cuando la source
cambia, el target
es actualizado para mostrar el nuevo valor, pero no viceversa. Si necesitamos crear un canal de comunicación de dos vias (por ejemplo, debido a que el target es un control TextBox
y necesitamos interceptar un nuevo valor insertado por el usuario), podemos establecer la propiedad Mode
del enlazado a TwoWay
.
1 |
<TextBox Text="{Binding Path=Name, Mode=TwoWay}" /> |
Casi todo control en XAML puede participar en data binding. La mayoría de las propiedades disponibles para un control, de hecho, son dependency properties
. Mas allá de ofrecer capacidades básicas de leer y escribir, éstas son propiedades especiales que soportan notificaciones, asi que pueden notificar al otro lado del canal que algo ha cambiado.
El ejemplo siguiente muestra como el data binding puede usarse para crear un canal entre dos controles XAML.
1 |
<StackPanel>
|
2 |
<Slider x:Name="Volume" /> |
3 |
<TextBlock x:Name="SliderValue" Text="{Binding ElementName=Volume, Path=Value}" /> |
4 |
</StackPanel>
|
Lo primero que tienes que notar es que para aplicar el enlazado, necesitamos usar otra markup extension (extensión de marcado), llamada Binding
. Con ésta expresión, conectamos la propiedad Text
de un control TextBlock
(el target) a la propiedad Value
de un control Slider
llamdo Volume
(la fuente).
Ya que Text
y Value
son propiedades dependientes, cada vez que mueves el slider, el valor seleccionado, será mostrado automáticamente en la pantalla en el control TextBlock
.
Data Binding con Objetos
Una de las características más poderosas de data binding es la capacidad para conectar controles con objetos que son parte de nuestro código. Sin embargo, primero, necesitamos introducir el concepto DataContext
. DataContext
es una propiedad que está disponible para casi todo control y puede usarse para definir su contexto de enlazado, que también es heredado automáticamente por cada control anidado. Cuando defines un objeto como DataContext
, el control y todos sus hijos tendrán acceso a todas sus propiedades.
Veamos un ejemplo que nos ayudará a entender mejor como funciona. Digamos que tienes una clase que representa a una persona:
1 |
public class Person |
2 |
{
|
3 |
public string Name { get; set; } |
4 |
public string Surname { get; set; } |
5 |
}
|
Nuestra meta es mostrar información sobre una persona usando ésta clase. Aquí se muestra como podemos hacerlo usando data binding. Primero, veamos el code behind:
1 |
public MainPage() |
2 |
{
|
3 |
InitializeComponent(); |
4 |
Person person = new Person(); |
5 |
person.Name = "Matteo"; |
6 |
person.Surname = "Pagani"; |
7 |
Author.DataContext = person; |
8 |
}
|
Cuando se inicializa la página, creamos un nuevo objeto Person
y establecemos un valor para las propiedades Name
y Surname
. Luego, establecemos éste objeto nuevo como DataContext
del control Author
. Veamos en la página XAML el control Author
y como las propiedades Name
y Surname
son mostradas:
1 |
<StackPanel x:Name="Author"> |
2 |
<TextBlock Text="Name" /> |
3 |
<TextBlock Text="{Binding Path=Name}" /> |
4 |
<TextBlock Text="Surname" /> |
5 |
<TextBlock Text="{Binding Path=Surname}" /> |
6 |
</StackPanel>
|
Author
es el nombre asignado a un control StackPanel
, que es el contenedor que hemos colocado dentro de controles diferentes TextBlock
. En el ejemplo anterior podemos ver nuevamente en acción la extensión de marcado Binding
, ésta vez con un atributo diferente: Path
. Estamos usándolo para indicar al XAML que propiedad mostrar del actual DataContext
. Ya que el DataContext
es heredado del control StackPanel
, cada TextBlock
tiene acceso a las propiedades del objeto Person
que hemos creado en el code behind. Nota que el atributo Path
es opcional. Los dos siguientes enunciados son exactamente los mismos:
1 |
<TextBlock Text="{Binding Path=Name}" /> |
2 |
<TextBlock Text="{Binding Name}" /> |
La Interfaz INotifyPropertyChanged
El código previo tiene un defecto. Todo funciona bien, pero si cambias el valor de una de las propiedades Name
o Surname
durante la ejecución, la interfaz de usuario no se actualizará para mostrar el nuevo valor. La razon es que Name
y Surname
son propiedades simples, así que no pueden notificar a la interfaz de usuario que algo ha cambiao, a diferencia de la propiedades de dependencia. Para éste escenario, el framework XAML ha introducido la interfaz INotifyPropertyChanged
que puede ser implementada por objetos que necesitan satisfacer éste requerimiento de notificación. Aquí está como la clase Person
puede ser cambiada para implementar ésta interfaz:
1 |
public class Person: INotifyPropertyChanged |
2 |
{
|
3 |
private string _name; |
4 |
private string _surname; |
5 |
|
6 |
public string Name |
7 |
{
|
8 |
get { return _name; } |
9 |
set
|
10 |
{
|
11 |
_name = value; |
12 |
OnPropertyChanged(); |
13 |
}
|
14 |
}
|
15 |
|
16 |
|
17 |
public string Surname |
18 |
{
|
19 |
get { return _surname; } |
20 |
set
|
21 |
{
|
22 |
_surname = value; |
23 |
OnPropertyChanged(); |
24 |
}
|
25 |
}
|
26 |
|
27 |
public event PropertyChangedEventHandler PropertyChanged; |
28 |
|
29 |
protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) |
30 |
{
|
31 |
PropertyChangedEventHandler handler = PropertyChanged; |
32 |
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); |
33 |
}
|
34 |
}
|
Ésta clase ahora implementa la interfaz INotifyPropertyChanged
, permitiéndonos soportar un event handler (llamado PropertyChangedEventHandler
) que es desencadenado cada vez que cambia el valor de una propiedad. La clase también implementa un método llamado OnPropertyChanged()
que actua como un contenedor del event handler y necesita ser invocado cuando cambia una propiedad.
También necesitamos un cambio en nuestras propiedades. Cada vez que el conjunto de la propiedad es llamado (significando que un nuevo valor ha sido asignado) damos paso al método OnPropertyChanged()
. El resultado será que cada control ligado con la propiedad será notificado del cambio y actualizará su estatus visual consecuentemente.
Data Binding y Collections (Colecciones)
Data binding es especialmente útil cuando tienes que tratar con colecciones de objetos como arreglos o listas (básicamente, cada colección de framework genera que implemente la interfaz IEnumerable
. Casi cada control que soporta colecciones, que hereda de la clase ItemsControl
(como ListBox
o LongListSelector
), tiene una propiedad llamada ItemsSource
, que puede ser directamente asignada a una lista.
Puedes controlar como será renderizado cada objeto de la colección al usar la propiedad ItemTemplate
. Como hemos visto cuando hablamos sobre plantillas de datos, ésta propiedad nos permite establecer que usará XAML para mostrar el objeto.
Ahora que hemos hablado sobre data binding, hay otra pieza importante para añadir. En el código de ejemplo que usamos para mostrar las plantillas de datos, incluimos algunas expresiones de enlazado para mostrar el nombre y apellido de una persona.
1 |
<DataTemplate x:Key="PeopleTemplate"> |
2 |
<StackPanel>
|
3 |
<TextBlock Text="Name" /> |
4 |
<TextBlock Text="{Binding Path=Name}" /> |
5 |
<TextBlock Text="Surname" /> |
6 |
<TextBlock Text="{Binding Path=Surname}" /> |
7 |
</StackPanel>
|
8 |
</DataTemplate>
|
Cuando estableces una colección como ItemSource
, cada objeto que es parte de ella se convierte en el DataContext
de ItemTemplate
. Si, por ejemplo, la propiedad ItemsSource
de un ListBox
es conectada a una colección cuyo tipo es List<Person>
, los controles incluídos en ItemTemplate
podrán acceder a todas las propiedades de la clase Person
.
Éste es el verdadero significado del código de ejemplo previo: para cada objeto Person
que es parte de la colección, vamos a mostrar los valores de las propiedades Name
y Surname
.
Otra importante pieza del rompecabezas cuando tratas con colecciones es la clase ObservableCollection<T>
. Actúa como una colección regular, así que fácilmente puedes añadir, remover y mover objetos. Bajo la máscara, implementa la interfaz INotifyPropertyChanged
así que cada vez que la colección sea cambiada, la UI recibe una notificación. De ésta manera, cada vez que manipulamos la colección (por ejemplo, añadimos un nuevo elemento), el control que está conectado a ella, automáticamente se actualizará para reflejar los cambios.
Converters (Convertidores)
Los convertidores juegan un papel importante en data binding. A veces, de hecho, necesitas modificar los datos fuente antes de que sean enviados al target. Un ejemplo común es cuando tienes que tratar con propiedades DateTime
. La clase DateTime
contiene una representación completa de una fecha, incluyendo horas, minutos, segundos, y milisegundos. La mayor parte del tiempo, sin embargo, no necesitas mostrar toda la representación-con frecuencia sólo la fecha es suficiente.
Aquí es donde los convertidores son útiles. Puedes cambiar los datos (o, como se muestra en el siguiente ejemplo, aplicar diferente formato) antes de enviarlos al control que va a mostrarlos usando data binding.
Para crear un convertidor, necesitas añadir una nueva clase a tu proyecto (da click derecho en Visual Studio, elige Add > Class), y tiene que heredar de la interfaz IValueConverter
. El siguiente es un ejemplo de convertidor:
1 |
public class DateTimeConverter : IValueConverter |
2 |
{
|
3 |
public object Convert(object value, Type targetType, object parameter, CultureInfo culture) |
4 |
{
|
5 |
if (value != null) |
6 |
{
|
7 |
DateTime date = (DateTime)value; |
8 |
return date.ToShortDateString(); |
9 |
}
|
10 |
return string.Empty; |
11 |
}
|
12 |
|
13 |
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) |
14 |
{
|
15 |
if (value != null) |
16 |
{
|
17 |
DateTime date = DateTime.Parse(value.ToString()); |
18 |
return date; |
19 |
}
|
20 |
return DateTime.Now; |
21 |
}
|
22 |
}
|
Cuando soportas la interfaz IValueConverter
, estás obligado a implementar dos métodos:
-
Convert()
es el método invocado cucando los datos de la fuente se envían al target. -
ConvertBack()
hace lo contrario-es invocado cuando datos del target son enviados de regreso a la fuente.
La mayor parte del tiempo basta con implementar el método Convert()
que es soportado por cada enlazado. El método ConvertBack()
, en cambio, es soportado sólo cuando tienes el enlazado TwoWay
(de dos vías).
Ambos métodos reciben información importante como parámetros de entrada:
- El valor regresado de la fuente binding (que es el que necesitas manipular).
- La propiedad a la que ha sido aplicada el enlazado.
- Un parámetro opcional que puede ser establecido en XAML usando la propiedad
ConverterParameter
. Éste parámetro puede ser usado para aplicar un comportamiento diferente en el convertidor lógico. - La cultura actual.
El código de muestra previo muestra el ejemplo DateTime
mencionado antes. En el método Convert()
obtenemos el valor original y, después de que lo hemos convertido en un objeto DateTime
, regresamos un string con el formato corto.
En el método ConvertBack()
, obtenemos el string regresado del control y lo convertimos en un objeto DateTime
antes de enviarlo de vuelta al código.
Los convertidores son tratados como recursos-necesitas declararlos e incluirlos en tu expresión de enlazado usando la palabra reservada StaticResource
.
1 |
<phone:PhoneApplicationPage.Resources>
|
2 |
<converters:DateTimeConverter x:Key="DateConverter" /> |
3 |
</phone:PhoneApplicationPage.Resources>
|
4 |
<TextBlock Text="{Binding Path=BirthDate, Converter={StaticResource DateConverter}}" /> |
Es importante resaltar que los convertidores pueden tener un impacto negativo en rendimiento si los usas mucho, ya que la operación de enlazao necesita ser aplicada nuevamente cada vez que los datos cambian. En éste caso, es mejor encontrar una forma para modificar directamente los datos fuente para añadir una nueva propiedad en tu clase con el valor modificado.
Controles
El Windows Phone 8 SDK incluye muchos controles integrados que pueden ser usados para definir la interfaz de usuario de la aplicación. Hay tantos controles que es casi imposible analizar todos en ésta serie, asi que solo nos enfocaremos en los más importantes.
Controles de Layout
Algunos controles simplemente actúan como contenedores para otros controles y definen el layout de la página. Discutamos los más importantes.
StackPanel
El control StackPanel
puede usarse simplemente para alinear los controles anidados uno debajo de otro.. Puede adaptarse automáticamente al tamaño de los controles hijos.
1 |
<StackPanel>
|
2 |
<TextBlock Text="First text" /> |
3 |
<TextBlock Text="Second text" /> |
4 |
</StackPanel>
|
Puedes también usar el control StackPanel
para alinear los controles horizontalmente, uno al lado de otro, al establecer la propiedad Orientation
en Horizontal
.
1 |
<StackPanel Orientation="Horizontal"> |
2 |
<TextBlock Text="First text" /> |
3 |
<TextBlock Text="Second text" /> |
4 |
</StackPanel>
|
Grid (Grilla)
El control Grid
puede ser usado para crear layouts de tabla, que cubrirán todo el tamaño del contenedor padre. Soporta filas y columnas en las que puedes colocar los diferentes controles. El siguiente código de ejemplo demuestra su uso:
1 |
<Grid>
|
2 |
<Grid.RowDefinitions>
|
3 |
<RowDefinition Height="50" /> |
4 |
<RowDefinition MaxHeight="100" /> |
5 |
</Grid.RowDefinitions>
|
6 |
<Grid.ColumnDefinitions>
|
7 |
<ColumnDefinition Width="200" /> |
8 |
<ColumnDefinition Width="*" /> |
9 |
</Grid.ColumnDefinitions>
|
10 |
<TextBlock Text="1° row - 2° column" Grid.Row="0" Grid.Column="1" /> |
11 |
</Grid>
|
Defines el layout del grid al usar las propiedades RowDefinitions
y ColumnDefinitions
. Puedes añadir una etiqueta RowDefinition
para cada fila que tendrá la tabla, mientras la etiqueta ColumnDefinition
puede ser usada para establecer el número de columnas. Para cada fila y columna puedes establecer la anchura y altura, o simplemente puedes omitirlas para que automáticamente se adapten a los controles anidados.
Para definir una posición del control dentro de la grilla, vamos a usar dos propiedades adjuntadas, que son propiedades dependencias que son heredadas del control Grid
y pueden usarse con cada control. Con la propiedad Grid.Row
, podemos establecer el número de la fila, y con Grid.Column
, podemos establecer el número de la columna.
El código de ejemplo previo es usado para desplegar un TextBlock
en la celda que es colocada en la primera fila, segunda columna de la grilla, como puedes ver en la siguiente gráfica:



ScrollViewer
El control ScrollViewer
es un contenedor, pero no define un layout. Si quieres arreglar controles anidados, aún tienes que usar otro contenedor como un StackPanel
o un Grid
. El propósito de éste control es crear un layout que es más grande que el tamaño de la pantalla. De ésta forma, los usuarios podrán desplazarse con el scroll para ver el resto de la interfaz de usuario.
Éste control es útil, por ejemplo, cuando tienes que mostrar texto que no cabe en la página.
1 |
<ScrollViewer>
|
2 |
<StackPanel>
|
3 |
<TextBlock TextWrapping="Wrap" Text="This can be long text" /> |
4 |
</StackPanel>
|
5 |
</ScrollViewer>
|
Border
El propósito del control Border
es mostrar un borde. Éste es útil porque puede contener un control hijo que será incluido en el borde.
El siguiente ejemplo muestra como encerrar una imagen dentro de un borde rojo.
1 |
<Border BorderThickness="5" BorderBrush="Red"> |
2 |
<Image Source="/Assets/windows-phone-8-logo.png"/> |
3 |
</Border>
|



Hay algunas propiedades clave del control Border
. El primero es BorderThickness
, que especifica el grosor del borde. Puedes especificar un solo valor, como lo hicimos en el ejemplo previo. En éste caso, el mismo grosor es aplicado a cada lado. También puedes especificar múltiples valores para dar a cada borde un tamaño diferente, como en el siguiente ejemplo:
1 |
<Border BorderThickness="5, 10, 15, 20" BorderBrush="Red"> |
2 |
<Image Source="/Assets/windows-phone-8-logo.png"/> |
3 |
</Border>
|



La segunda propiedad importante es BorderBrush
, que es usada para establecer el pincel que es aplicado al border. Puede usar cualquiera de los pinceles disponibles XAML. Por defecto, acepta un SolidColorBrush
, así que simplemente puedes especificar el color que quieres aplicar.
Otra propiedad útil es Padding
, que puede usarse para especificar la distancia entre el borde y el control hijo, como se muestra en el siguiente ejemplo:
1 |
<Border BorderThickness="5" BorderBrush="Red" Padding="10"> |
2 |
<Image Source="/Assets/windows-phone-8-logo.png"/> |
3 |
</Border>
|



Output Controls
El propósito de éstos controles es mostrar algo a los usuarios, como texto, una imagen, etc.
TextBlock
TextBlock
es uno de los principales controles XAML y es usado para mostrar texto en la pantalla. Su propiedad más importante es Text
, que, por supuesto, contiene el texto a desplegar. Tienes muchas propiedades de donde elegir para modificar la apariencia del texto, como FontSize
, FontWeight
, y FontStyle
, y puedes ajustar automáticamente el texto en líneas múltiples en caso que el texto sea demasiado largo al establecer la propiedad TextWrapping
en true
.
1 |
<TextBlock Text="This is long and bold text" TextWrapping="Wrap" FontWeight="Bold" /> |
También puedes aplicar diferente formato al texto sin usar múltiples controles TextBlock
al usar la etiqueta Run
, que puede ser usada para dividir el texto como se muestra en el siguiente ejemplo:
1 |
<TextBlock>
|
2 |
<Run Text="Standard text" /> |
3 |
<LineBreak /> |
4 |
<Run Text="Bold test" FontWeight="Bold" /> |
5 |
</TextBlock>
|
RichTextBlock
El control RichTextBlock
es similar a TextBlock
, pero ofrece más control sobre los estilos de formato que pueden ser aplicados al texto. Como el ofrecido por HTML, puedes definir párrafos, aplican diferentes estilos de texto, y más.
1 |
<RichTextBox>
|
2 |
<Paragraph>
|
3 |
<Bold>This is a paragraph in bold</Bold> |
4 |
</Paragraph>
|
5 |
<Paragraph>
|
6 |
<Italic>This is a paragraph in italics</Italic> |
7 |
<LineBreak /> |
8 |
</Paragraph>
|
9 |
</RichTextBox>
|
Image
El control Image
puede usarse para mostrar imágenes. Puedes establecer la propiedad Source
con una ruta remota (la URL de una imagen publicada en internet) o una ruta local (un archivo que es parte de tu proyecto en Visual Studio. No puedes asignar una ruta que se refiere a una imagen almacenada en el almacenamiento local de la aplicación. Veremos después en ésta serie como manejar ésta limitación.
1 |
<Image Source="http://www.syncfusion.com/Content/en-US/Home/Images/syncfusion-logo.png" /> |
Puedes controlar también como será adaptada la imagen para llenar el tamaño del control al usar la propiedad Stretch
, que puede tener los siguientes valores:
-
Uniform
: el valor por defecto. La imagen es redimensionada para encajar en el contenedor manteniendo la relación de aspecto para que la imagen no se distorsione. Si la relación de aspecto del contenedor es diferente de la de la imagen, la imagen se verá más pequeña que el espacio disponible. -
Fill
: la imagen es redimensionada para que quepa en el contenedor, ignorando la relación de aspecto. Llenará todo el espacio disponible, pero si el tamaño del control no tiene la relación de aspecto que la imagen, se verá distorsionada. -
UniformToFill
es una mezcla de los valores previos. Si la imagen tiene diferente relación de aspecto que el contenedor, la imagen es cortada para que pueda mantener la correcta relación de aspecto y, al mismo tiempo, llene todo el espacio disponible. -
None
: La imagen es mostrada en su tamaño original.
Input Controls
Éstos controles son usados para obtener elementos de entrada de los usuarios.
TextBox
TextBox
es otro control básico XAML, y es simplemente una forma de obtener texto de los usuarios. El texto ingresado será almacenado en la propiedad Text
del control. Cuando usuarios presionan un control TextBox
, el teclado virtual es abierto automáticamente. Como desarrollador, puedes controlar el tipo de teclado que es mostrado de acuerdo a los tipos de datos que estás obteniendo.
Por ejemplo, puedes mostrar un teclado numérico si los usuarios solo necesitan ingresar un número, o puedes usar un teclado de correo electrónico (que proporciona fácil acceso a símbolos como @ si estás recabando una dirección de correo electrónico.
Puedes controlar el tipo de teclado con la propiedad InputScope
. La lista de valores soportados es muy larga y puede encontrarse en la documentación MSDN. Alguno de los más usados son:
-
Text
para entrada de texto genérico con soporte de diccionario. -
Number
para entrada de número genérico. -
TelephoneNumber
para entrada de número telefónico específico (es el mismo teclado que se muestra cuando escribes un número en la aplicación nativa Phone. -
EmailNameOrAddress
que añade acceso rápido a símbolos como @. -
Url
que agrega acceso rápido a dominios comunes como .com o .it (dependiendo del lenguaje del teclado). -
Search
que proporciona sugerencias automáticas.
1 |
<TextBox InputScope="TelephoneNumber" /> |
Consejo: Si el control TextBox es usado para recabar texto genérico, siempre recuerda establecer la propiedad InputScope en Text. De ésta forma, los usuarios obtienen soporte de las herramientas autocompletado y autocorrección.



PasswordBox
PasswordBox
trabaja exactamente como el control TextBox
, excepto que los caracteres insertados son convertidos automáticamente en puntos para que la gente que se encuentra cerca del usuario no pueda leer el texto. Como el nombre del control lo implica, es usado típicamente para recabar contraseñas.
Theme Resources (Recursos de Tema)
Una de las metas del desarrollador debe ser mantener la interfaz de usuario de su aplicación tan consistente como sea posible con las pautas proporcionadas por el sistema operativo. Para ayudar a alcanzar ésta meta, el SDK ofrece muchos recursos instantáneos que pueden aplicarse a controles para obtener la misma apariencia y sentirlos como aplicaciones nativas. Éstos estilos son típicamente usados con controles como TextBox
, TextBlock
, o RadioButton
, y proporcionan un conjunto estándar de características visuales (como tamaña de fuente, opacidad, color, etc.) que son consistentes con las demás aplicaciones.
Otra buena razón para usar los recursos de tema es que toman en cuenta el tema establecido por los usuarios. Por ejemplo, puedes usar el estilo PhoneAccentBrush
si quieres dar a un control el mismo color que el color de énfasis del teléfono.
Los muchos recursos del tema son divididos en diferentes categorías como recursos de pinceles, recursos de color, nombres y estilos de fuente, y recursos de texto. Puedes encontrar una lista completa de estilos disponibles en la documentación MSDN. Son simplemente aplicados usando la propiedad Style
ofrecida por cada control, como se muestra en el siguiente ejemplo:
1 |
<TextBlock Text="App name" Style="{StaticResource PhoneTextNormalStyle}" /> |
Interactuando con Usuarios
En ésta categoría, podemos recabar todos los controles que pueden usar para interactuar con usuarios, como Button
, Checkbox
, o RadioButton
.
El texto a desplegar es establecido con la propiedad Content
, como el el siguiente ejemplo:
1 |
<Button Content="Tap me" Tap="OnClickMeClicked" /> |
La propiedad Content
también puede ser compleja para que puedas añadir otros controles XAML. En el siguiente ejemplo podemos ver como insertar una imagen dentro de un botón.
1 |
<Button Tap="OnClickMeClicked"> |
2 |
<Button.Content>
|
3 |
<StackPanel>
|
4 |
<Image Source="/Assets/logo.png" Height="200" /> |
5 |
</StackPanel>
|
6 |
</Button.Content>
|
7 |
</Button>
|
Éstos controles ofrecen muchas formas de interactuar con los usuarios. Los eventos más comunes son Click
, Tap
, y DoubleTap
.
Nota: Click y Tap son el mismo evento-se desencadenan cuando los usuarios presionan el control. Tap ha sido introucido en Windows Phone 7.5 para ser más consistente con la interfaz táctil, pero para evitar romper antiguas aplicaciones, el evento Click aún es soportado.
Los controles Windows Phone Signature
La mayoría de los controles que hemos visto hasta ahora son parte del framework y están disponibles en cada tecnología basada en XML, como Silverlight, WPF, y aplicaciones Windows Store.
Sin embargo, hay controles que están disponibles únicamente en la plataforma Windows Phone, ya que son específicas para la experiencia móvil. Veámoslas.
Panorama
El control Panorama
con frecuencia es usado en aplicaciones Windows Phone ya que generalmente es tratado como un punto de inicio. El control obtiene su nombre porque una imagen sobredimensionada es utilizada como fondo de la página. Usuarios pueden desplazarse a la izquierda o a la derecha para ver las otras páginas disponibles. Ya que la imagen es más grande que el tamaño de la página, el teléfono aplica un efecto parallax que es visualmente placentero para los usuarios.
La otra característica principal del control Panorama
, es que los usuarios pueden obtener una muestra de la siguiente página. La página actual no ocupa todo el espacio disponible porque una insinuación de la próxima página es mostrada en el extremo derecho.
Un control Panorama
es típicamente usado para proporcionar una vista general del contenido que está disponible para la aplicación. Es un punto de inicio, no un contenedor de datos. Por ejemplo, no es apropiado usar una página panorama para mostrar todas las noticias publicadas en un blog. Es mejor, en cambio, mostrar sólamente las noticias más recientes y proporcionar un botón para redireccionar a los usuarios a otra página, donde podrán verlas todas.
Desde un punto de vista del desarrollador, un control Panorma
se compone de diferentes páginas: cada una es un control PanoramaItem
que contiene el layout de la página.
El Panorama
pueden tener título genérico, como el título de la aplicación (que es asignado a la propiedad Title
), mientras cada página puede tener su propio título específico (que es asignado a la propiedad Header
).
1 |
<phone:Panorama Title="Panorama"> |
2 |
<phone:PanoramaItem Header="First page"> |
3 |
<StackPanel>
|
4 |
<TextBlock Text="Page 1" /> |
5 |
</StackPanel>
|
6 |
</phone:PanoramaItem>
|
7 |
<phone:PanoramaItem Header="Second page"> |
8 |
<StackPanel>
|
9 |
<TextBlock Text="Page 2" /> |
10 |
</StackPanel>
|
11 |
</phone:PanoramaItem>
|
12 |
</phone:Panorama>
|
Nota: el control Panorama (como el control Pivote) no está disponible por defecto en una página. Tendrás que declarar el siguiente namespace:
1 |
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone" |
Pivot
El control Pivot
, desde una perspectiva técnica y de interacción del usuario, funciona de una manera similar al control Panorma
-usuarios pueden desplazarse por la pantalla a la izquierda o derecha para ver las otras páginas. La diferencia, desde el punto de vista de los usuarios, es que la vista cabrá en el tamaño de la pantalla. Los usuarios pueden ver que página es la siguiente porque su encabezado es mostrado junto al encabezado de la página actual en gris.



Sin embargo, el control Pivot
es usado para fines no compartidos por el control Panorama
.
- Para mostrar una clase de información aplicada a diferentes contextos. La gráfica previa es un buen ejemplo. La información de cada página es la misma (el pronostico del tiempo), pero referida a diferentes contextos (las ciudades).
- Para mostrar diferentes clases de información que se refieren al mismo contexto. Una página de detalles de contacto en el People Hub en Windows Phone es un buen ejemplo de ésto-tienes mucha información (los detalles del contacto, actualizaciones de redes sociales, conversaciones, etc.), pero todo pertenece al mismo contexto (el contacto).
Como se anticipó previamente, el XAML para el control Pivot
funciona como el XAML para el control Panorama
. El control principal es llamado Pivot
, mientras los controles anidados que representan las paginas son llamados PivotItem
.
1 |
<phone:Pivot Title="Pivot"> |
2 |
<phone:PivotItem Header="First page"> |
3 |
<StackPanel>
|
4 |
<TextBlock Text="Page 1" /> |
5 |
</StackPanel>
|
6 |
</phone:PivotItem>
|
7 |
<phone:PivotItem Header="Second page"> |
8 |
<StackPanel>
|
9 |
<TextBlock Text="Page 2"/> |
10 |
</StackPanel>
|
11 |
</phone:PivotItem>
|
12 |
</phone:Pivot>
|
La ApplicationBar (Barra de Aplicación)
La ApplicationBar
es un control colocado en la parte inferior de la página y es usado como un acceso rápido para las funciones que están conectadas a la vista actual.
Puedes añadir dos tipos de elementos a una ApplicationBar (barra de aplicaciones):
- Íconos son siempre mostrados (a menos que la
ApplicationBar
esté minimizada), y hasta cuatro pueden mostrarse a la vez. - Menu items son simplemente elementos de texto que son mostrados sólo cuando la barra de aplicaciones es abierta. No hay límite para el número de elementos que pueden ser incluidos.



La ApplicationBar
no se comporta como otros controles XAML. No es parte de la página-de hecho, es declarada afuera de la Grid
principal, la llamada LayoutRoot
-y no hereda de la clase FrameworkElement
, como cualquier otro control. La mayor desvantaja de ésta es que el control no soporta enlazado; tendrás que depender de librerías de terceros, como la implementación disponible en el Cimbalino Toolkit for Windows Phone.
Aqui está un ejemplo de Application Bar
:
1 |
<phone:PhoneApplicationPage.ApplicationBar>
|
2 |
<shell:ApplicationBar IsVisible="True" IsMenuEnabled="True"> |
3 |
<shell:ApplicationBarIconButton IconUri="/Assets/Add.png" Text="Add" Click="ApplicationBarIconButton_Click" /> |
4 |
<shell:ApplicationBar.MenuItems>
|
5 |
<shell:ApplicationBarMenuItem Text="update" Click="ApplicationBarMenuItem_Click"/> |
6 |
</shell:ApplicationBar.MenuItems>
|
7 |
</shell:ApplicationBar>
|
8 |
</phone:PhoneApplicationPage.ApplicationBar>
|
ApplicationBar
es una propiedad de la clase PhoneApplicationPage
, que contiene la definición ApplicationBar
. No es parte de los namespaces XAML estándar, pero es parte del siguiente namespace, que ya debería ser declarado en cada página estándar Windows Phone:
1 |
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone" |
Botones con íconos son declarados directamente dentro de la etiqueta ApplicationBar
. La clase base es ApplicationBarIconButton
, y las propiedades más importantes son Text
(la descripción del ícono) y IconUri
(la ruta de la imagen usada como el ícono), mientras Click
es el event handler que es invocado cuando un usuario presiona el botón.
Los elementos de menú, en cambio están agrupados dentro de una propiedad del control ApplicationBar
llamada MenuItems
. Puedes añadir tantos controles ApplicationBarMenuItem
como quieras. Se comportan como los botones con íconos excepto que, ya que son solo texto, no incluye a propiedad IconUri
.
Otra importante limitación del control ApplicationBar
es que no pueden asignar la propiedad x:Name
a un control ApplicationBarIconButton
o ApplicationBarMenuItem
. Ésto significa que si necesitamos cambiar el valor de una propiedad en el código, no podemos simplemente usar la notación ControlName.PropertyName
.
La solución es usar el código subyacente para accesar directamente a las colecciones que contienen los botones y los elementos del menú que están disponibles por el objeto ApplicationBar
. Son llamados Buttons
y MenuItems
, como puedes ver en el siguiente código de ejemplo:
1 |
ApplicationBarIconButton iconButton = this.ApplicationBar.Buttons[0] as ApplicationBarIconButton; |
2 |
iconButton.Text = "New text"; |
3 |
ApplicationBarMenuItem menuItem = this.ApplicationBar.MenuItems[0] as ApplicationBarMenuItem; |
4 |
menuItem.Text = "New text"; |
El propósito de éste ejemplo es accesar al primer botón con ícono y al primer elemento del menú dentro del ApplicationBar,
y para cambiar el valor de la propiedad Text
.
Al final, hay otras dos formas de personalizar el ApplicationBar
. Lo primero es minimizarlo. De ésta forma, sólo tres puntos al margen derecho serán mostrados; los íconos no serán visibles. Para lograr ésto, tienes que establecer la propiedad Mode
a Minimized.
La otra forma es cambiar la opacidad. Puedes establecer la propiedad Opacity
con un valor entre 0 (transparente) y 1 (opaco). La mayor diferencia es que cuando ApplicationBar
es translúcido, el contenido de la página va debajo de la barra y ocupa todo el tamaño de la pantalla, cuando la barra es opaca, el contenido no llenará todo el espacio disponible en la pantalla ya que la parte inferior de la página será reservada para la ApplicationBar
.
Mostrando Colecciones de Datos con el LongListSelector
Uno de los requerimientos más comunes en una aplicación es mostrar una colección de elementos que pueden ser tomados de un servicio remoto o una base de datos local. El Windows Phone SDK ha incluido, desde el inicio, algunos controles para éste propósito, como ItemsControl
y ListBox
. En Windows Phone 8, Microsoft ha introducido un nuevo y más poderoso control, que previamente estaba disponible como parte del Windows Phone Toolkit (encontrarás más detalles sobre este paquete de herramientas más adelante en éste artículo). Éste control es llamado LongListSelector
y tiene muchas ventajas sobre otros controles similares.
- mejor desempeño
- soporte de virtualización para evitar cargar todos los datos al mismo tiempo, en lugar e cargar datos sólo cuando es necesario.
- soporte de grupo para convertir la lista en una
jump list
(lista de elementos recientes),para que los datos sean agrupados en categorías y los usuarios puedan fácilmente saltar desde uno a otro (por ejemplo, en el People Hub (Hub de contactos), los contactos son agrupados por la primera letra).
Creando una Lista común
El control LongListSelector
puede usarse como un regular ListBox
para mostrar una lista común y corriente de elementos, sin agruparlos. En éste caso, solo necesitas establecer la propiedad IsGroupingEnable
a false
. Excepto por ésta modificación, podrás usar un LongListSelector
como un ItemsControl
estándar. Definirás la propiedad ItemTemplate
con un DataTemplate
para definir el layout del elemento, y asignarás la colección que quieras mostrar a la propiedad ItemsSource
.
1 |
<phone:LongListSelector x:Name="List" |
2 |
IsGroupingEnabled="False" |
3 |
ItemTemplate="{StaticResource PeopleItemTemplate}" /> |
Creando una List Grouped (Lista Agrupada) por letra.
Crear una lista agrupada por la primera letra de los elementos es un poco más complicado ya que tenemos que cambiar nuestra fuente de datos. Ya no será una colección de datos común y corriente. Además, si queremos mantener consistente la experiencia del usuario con otras aplicaciones, necesitaremos crear una jump list (lista de elementos recientes) con todas las letras del alfabeto. Los grupos que no tienen miembros se deshabilitarán para que los usuarios no puedan presionarlos.
Para lograr éste resultado, Microsoft ofrece una clase llamada AlphaKeyGroup<T>
, que representa una letra del alfabeto y todos los elementos que comienzan con él. Sin embargo, ésta clase no es parte del Windows Phone SDK, y debería ser agregada manualmente a tu proyecto al dar click derecho en tu proyecto en el Explorador de Soluciones en Visual Studio, y eligiendo Add new class. El siguiente código de ejemplo es la implementación completa.
1 |
public class AlphaKeyGroup<T> : List<T> |
2 |
{
|
3 |
/// <summary>
|
4 |
/// The delegate that is used to get the key information.
|
5 |
/// </summary>
|
6 |
/// <param name="item">An object of type T.</param>
|
7 |
/// <returns>The key value to use for this object.</returns>
|
8 |
public delegate string GetKeyDelegate(T item); |
9 |
|
10 |
/// <summary>
|
11 |
/// The key of this group.
|
12 |
/// </summary>
|
13 |
public string Key { get; private set; } |
14 |
|
15 |
/// <summary>
|
16 |
/// Public constructor.
|
17 |
/// </summary>
|
18 |
/// <param name="key">The key for this group.</param>
|
19 |
public AlphaKeyGroup(string key) |
20 |
{
|
21 |
Key = key; |
22 |
}
|
23 |
|
24 |
/// <summary>
|
25 |
/// Create a list of AlphaGroup<T> with keys set by a SortedLocaleGrouping.
|
26 |
/// </summary>
|
27 |
/// <param name="slg">The </param>
|
28 |
/// <returns>The items source for a LongListSelector.</returns>
|
29 |
private static List<AlphaKeyGroup<T>> CreateGroups(SortedLocaleGrouping slg) |
30 |
{
|
31 |
List<AlphaKeyGroup<T>> list = new List<AlphaKeyGroup<T>>(); |
32 |
|
33 |
foreach (string key in slg.GroupDisplayNames) |
34 |
{
|
35 |
list.Add(new AlphaKeyGroup<T>(key)); |
36 |
}
|
37 |
|
38 |
return list; |
39 |
}
|
40 |
|
41 |
/// <summary>
|
42 |
/// Create a list of AlphaGroup<T> with keys set by a SortedLocaleGrouping.
|
43 |
/// </summary>
|
44 |
/// <param name="items">The items to place in the groups.</param>
|
45 |
/// <param name="ci">The CultureInfo to group and sort by.</param>
|
46 |
/// <param name="getKey">A delegate to get the key from an item.</param>
|
47 |
/// <param name="sort">Will sort the data if true.</param>
|
48 |
/// <returns>An items source for a LongListSelector.</returns>
|
49 |
public static List<AlphaKeyGroup<T>> CreateGroups(IEnumerable<T> items, CultureInfo ci, GetKeyDelegate getKey, bool sort) |
50 |
{
|
51 |
SortedLocaleGrouping slg = new SortedLocaleGrouping(ci); |
52 |
List<AlphaKeyGroup<T>> list = CreateGroups(slg); |
53 |
|
54 |
foreach (T item in items) |
55 |
{
|
56 |
int index = 0; |
57 |
if (slg.SupportsPhonetics) |
58 |
{
|
59 |
//Checks whether your database has the string yomi as an item.
|
60 |
//If it does not, then generate Yomi or ask users for this item.
|
61 |
//index = slg.GetGroupIndex(getKey(Yomiof(item)));
|
62 |
}
|
63 |
else
|
64 |
{
|
65 |
index = slg.GetGroupIndex(getKey(item)); |
66 |
}
|
67 |
if (index >= 0 && index < list.Count) |
68 |
{
|
69 |
list[index].Add(item); |
70 |
}
|
71 |
}
|
72 |
|
73 |
if (sort) |
74 |
{
|
75 |
foreach (AlphaKeyGroup<T> group in list) |
76 |
{
|
77 |
group.Sort((c0, c1) => { return ci.CompareInfo.Compare(getKey(c0), getKey(c1)); }); |
78 |
}
|
79 |
}
|
80 |
|
81 |
return list; |
82 |
}
|
83 |
|
84 |
}
|
Las principales características de ésta clase son:
- hereda de
List<T>
, asi que representa una lista de elementos. - Tiene una propiedad llamada
Key
, que es la clave que identifica el grupo (la letra del alfabeto). - Usa un tipo de colección especial llamado
SortedLocaleGroup
, que puede manejar las diferencias culturales entre un idioma y otro. - Ofrece un método llamado
CreateGroups()
, que es el que vamos a usar para agrupar nuestros datos.
Para explicar mejor como usar la clase AlphaKeyGroup<T>
, usaremos un ejemplo real. Definamos una colección de personas que queremos agrupar por la primera letra de sus nombres, en una forma similar a la del Hub de Contactos. El primer paso es crear una clase que represente una sola persona:
1 |
public class Person |
2 |
{
|
3 |
public string Name { get; set; } |
4 |
public string Surname { get; set; } |
5 |
public string City { get; set; } |
6 |
}
|
Luego, cuando inicia la aplicación, llenamos la lista con un conjunto de datos falsos, como se muestra en el siguiente ejemplo:
1 |
void LongListSelectorAlphabetic_Loaded(object sender, RoutedEventArgs e) |
2 |
{
|
3 |
List<Person> people = new List<Person> |
4 |
{
|
5 |
new Person |
6 |
{
|
7 |
Name = "John", |
8 |
Surname = "Doe", |
9 |
City = "Como" |
10 |
},
|
11 |
new Person |
12 |
{
|
13 |
Name = "Mark", |
14 |
Surname = "Whales", |
15 |
City = "Milan" |
16 |
},
|
17 |
new Person |
18 |
{
|
19 |
Name = "Ricky", |
20 |
Surname = "Pierce", |
21 |
City = "New York" |
22 |
}
|
23 |
};
|
24 |
}
|
Ahora es momento de usar la clase AlphaGroupKey<T>
para convertir ésta lista simple en una lista agrupada al llamar al método CreateGroups()
.
1 |
List<AlphaKeyGroup<Person>> list = AlphaKeyGroup<Person>.CreateGroups(people, |
2 |
Thread.CurrentThread.CurrentUICulture, |
3 |
p => p.Name, true); |
El método requiere cuatro parámetros:
- La colección que queremos agrupar: en el ejemplo, es la colección de objetos
Person
que creamos. - La cultura a usar para generar las letras del alfabeto: la práctica estándar es usar el valor de la propiedad
Thread.CurrentThread.CurentUICulture
, que es el lenguaje primario establecido por el usuario para el teléfono. - La propiedad del objeto que será usado para agrupar: Ésta es especificada usando una expresión lambda. En el ejemplo, la lista será agrupada por la primera letra del nombre.
- El último parámetro, de tipo
Boolean
, es usado para determinar si aplicar un orden: si se establece atrue
, la colección será ordenada alfabéticamente.
Lo que obtenemos a cambio es una colección de objetos AlphakeyGroup<T>
, uno para cada letra del alfabeto. Ésta es la colección que necesitamos asignar a la propiedad ItemsSource
de LongListSelectorControl
.
1 |
List<AlphaKeyGroup<Person>> list = AlphaKeyGroup<Person>.CreateGroups(people, |
2 |
Thread.CurrentThread.CurrentUICulture, |
3 |
p => p.Name, true); |
4 |
|
5 |
People.ItemsSource = list; |
Sin embargo, éste código no es suficiente-también necesitamos proporcionar plantillas adicionales en XAML que son usadas para definir el layout de jump list.
La primera propiedad para definir es llamada GroupHeaderTemplate
, y representa el encabezado que es mostrado en la lista antes de cada grupo. En el caso de una lista alfabética, es la misma letra. El siguiente código XAML muestra una plantilla de ejemplo, que recrea el mismo aspecto y sentido de aplicaciones nativas.
1 |
<DataTemplate x:Key="PeopleGroupHeaderTemplate"> |
2 |
<Border Background="Transparent" Padding="5"> |
3 |
<Border Background="{StaticResource PhoneAccentBrush}" BorderBrush="{StaticResource PhoneAccentBrush}" BorderThickness="2" Width="62" |
4 |
Height="62" Margin="0,0,18,0" HorizontalAlignment="Left"> |
5 |
<TextBlock Text="{Binding Key}" Foreground="{StaticResource PhoneForegroundBrush}" FontSize="48" Padding="6" |
6 |
FontFamily="{StaticResource PhoneFontFamilySemiLight}" HorizontalAlignment="Left" VerticalAlignment="Center"/> |
7 |
</Border>
|
8 |
</Border>
|
9 |
</DataTemplate>
|
Con éste layout, la letra es colocada dentro de un cuadrado con un color de fondo igual al color de énfasis del teléfono. Toma nota de dos cosas importantes:
- Algunas propiedades del control
Border
usan el recursoPhoneAccentBrush
. Es uno de los recursos de tema previamente descritos, que identifica el color de énfasis del teléfono. - La propiedad
Text
del controlTextBlock
está ligada a la propiedadKey
de la claseAlphaGroupKey<T>
. De ésta forma, podemos mostrar la letra del grupo adentro del cuadrado.



La segunda propiedad para definir es llamada JumpListStyle
y, a diferencia de la propiedad anterior, no es una plantilla sino un estilo. Su propósito es definir la apariencia y sentido de la jump list, que es la vista mostrada cuando los usuarios presionan una letra. Muestra todas las letras del alfabeto para que los usuarios puedan presionar una de ellas y rápidamente saltar a ese grupo.
Aquí está una simple definición que, de nuevo, recrea la apariencia y sentido de aplicaciones nativas, todas las letras del alfabeto son mostradas lado a lado en múltiples filas.
1 |
<phone:JumpListItemBackgroundConverter x:Key="BackgroundConverter"/> |
2 |
<phone:JumpListItemForegroundConverter x:Key="ForegroundConverter"/> |
3 |
<Style x:Key="PeopleJumpListStyle" TargetType="phone:LongListSelector"> |
4 |
<Setter Property="GridCellSize" Value="113,113"/> |
5 |
<Setter Property="LayoutMode" Value="Grid" /> |
6 |
<Setter Property="ItemTemplate"> |
7 |
<Setter.Value>
|
8 |
<DataTemplate>
|
9 |
<Border Background="{Binding Converter={StaticResource BackgroundConverter}}" Width="113" Height="113" Margin="6" > |
10 |
<TextBlock Text="{Binding Key}" FontFamily="{StaticResource PhoneFontFamilySemiBold}" FontSize="48" Padding="6" |
11 |
Foreground="{Binding Converter={StaticResource ForegroundConverter}}" VerticalAlignment="Center"/> |
12 |
</Border>
|
13 |
</DataTemplate>
|
14 |
</Setter.Value>
|
15 |
</Setter>
|
16 |
</Style>
|
Éste estilo usa dos convertidores que son parte del Windows Phone Toolkit llamado JumpListItemBackgroundConverter
y JumpListItemForegroundConverter
. Como puedes ver en el ItemTemplate
que es aplicado al control, éstos convertidores son usados para definir el color del texto y el color de fondo del Border
. Su objetivo es manejar grupos vacíos. Si la colección contiene uno o más elementos, será desplegada en blanco con el color de énfasis del teléfono como fondo. Si en cambio, la letra es asociada a un grupo sin elementos, tanto el texto como el fondo serán gris. De ésta forma, los usuarios inmediatamente ven que el grupo está deshabilitado y presionándolo no causará ningún efecto.



Al final, no olvides como para cualquier otro control basado en la clase ItemsControl
necesitarás establecer la propiedad ItemTemplate
, con un DataTemplate
para definir como se observará un solo elemento. El siguiente ejemplo muestra una implementación básica, donde el nombre y el apellido son mostrados uno debajo del otro.
1 |
<DataTemplate x:Key="PeopleItemTemplate"> |
2 |
<StackPanel>
|
3 |
<TextBlock Text="{Binding Name}" /> |
4 |
<TextBlock Text="{Binding Surname}" /> |
5 |
</StackPanel>
|
6 |
</DataTemplate>
|
Una vez que has reunido todos los estilos y las plantillas requeridas, puedes aplicarlos al control LongListSelector
, como se muestra en el siguiente ejemplo:
1 |
<phone:LongListSelector
|
2 |
x:Name="People" |
3 |
GroupHeaderTemplate="{StaticResource PeopleGroupHeaderTemplate}" |
4 |
ItemTemplate="{StaticResource PeopleItemTemplate}" |
5 |
JumpListStyle="{StaticResource PeopleJumpListStyle}" |
6 |
IsGroupingEnabled="True" |
7 |
HideEmptyGroups="True" /> |
Nota que la propiedad HideEmptyGroups
, que puede ser usada para ocultar todos los grupos en la lista que no contienen ningún elemento.
Creando una Lista Agrupada por Categoría
En algunos casos, podríamos necesitar agrupar elementos por un campo personalizado en lugar de la primera letra. Observemos otro ejemplo. Queremos agrupar objetos Person
por el campo City
; necesitamos entonces, una clase para remplazar el AlphaKeyGroup<T>
, ya que no es adecuado para nuestro escenario.
Introduzcamos la clase Group<T>
, que es mucho más simple:
1 |
public class Group<T> : List<T> |
2 |
{
|
3 |
public Group(string name, IEnumerable<T> items) |
4 |
: base(items) |
5 |
{
|
6 |
this.Key = name; |
7 |
}
|
8 |
|
9 |
public string Key |
10 |
{
|
11 |
get; |
12 |
set; |
13 |
}
|
14 |
}
|
Bajo la máscara, ésta clase se comporta como AlphaKeyGroup<T>
. Hereda de List<T>
, así que representa una colección de elementos, y define un grupo que es identificado por una clave (que será el nombre de la categoría).
1 |
private List<Group<T>> GetItemGroups<T>(IEnumerable<T> itemList, Func<T, string> getKeyFunc) |
2 |
{
|
3 |
IEnumerable<Group<T>> groupList = from item in itemList |
4 |
group item by getKeyFunc(item) |
5 |
into g |
6 |
orderby g.Key |
7 |
select new Group<T>(g.Key, g); |
8 |
|
9 |
return groupList.ToList(); |
10 |
}
|
El método previo toma como parámetros de entrada:
- La colección para agrupar.
- una función (expresada con una expresión lambda) para establecer la propiedad del objeto queremos usar para agrupar
El siguiente ejemplo es el código que podemos usar para agrupar la colección de objetos Person
por la propiedad City
:
1 |
List<Group<Person>> groups = GetItemGroups(people, x => x.City); |
2 |
PeopleByCity.ItemsSource = groups; |
El resultado retornado por el método GetItemGroups()
puede ser directamente asignado a la propiedad ItemsSource
del control LongListSelector
.
Como lo hicimos en el escenario de agrupar alfabéticamente, todavía necesitamos definir el layout de los encabezados del grupo y el jump list. Podemos reusar los recursos que hemos definido previamente, pero, si queremos lograr un mejor resultado, podemos adaptarlos para que el rectángulo de fondo llene el tamaño del nombre de la categoría, como se muestra en el siguiente código de ejemplo:
1 |
<phone:JumpListItemBackgroundConverter x:Key="BackgroundConverter"/> |
2 |
<phone:JumpListItemForegroundConverter x:Key="ForegroundConverter"/> |
3 |
|
4 |
<DataTemplate x:Key="PeopleCityGroupHeaderTemplate"> |
5 |
<Border Background="Transparent" Padding="5"> |
6 |
<Border Background="{StaticResource PhoneAccentBrush}" BorderBrush="{StaticResource PhoneAccentBrush}" BorderThickness="2" |
7 |
Height="62" Margin="0,0,18,0" HorizontalAlignment="Left"> |
8 |
<TextBlock Text="{Binding Key}" Foreground="{StaticResource PhoneForegroundBrush}" FontSize="48" Padding="6" |
9 |
FontFamily="{StaticResource PhoneFontFamilySemiLight}" HorizontalAlignment="Left" VerticalAlignment="Center"/> |
10 |
</Border>
|
11 |
</Border>
|
12 |
</DataTemplate>
|
13 |
|
14 |
<Style x:Key="PeopleCityJumpListStyle" TargetType="phone:LongListSelector"> |
15 |
<Setter Property="GridCellSize" Value="113,113"/> |
16 |
<Setter Property="LayoutMode" Value="List" /> |
17 |
<Setter Property="ItemTemplate"> |
18 |
<Setter.Value>
|
19 |
<DataTemplate>
|
20 |
<Border Background="{Binding Converter={StaticResource BackgroundConverter}}" Height="113" Margin="6" > |
21 |
<TextBlock Text="{Binding Key}" FontFamily="{StaticResource PhoneFontFamilySemiBold}" FontSize="48" Padding="6" |
22 |
Foreground="{Binding Converter={StaticResource ForegroundConverter}}" VerticalAlignment="Center"/> |
23 |
</Border>
|
24 |
</DataTemplate>
|
25 |
</Setter.Value>
|
26 |
</Setter>
|
27 |
</Style>
|
La siguiente gráfica muestra el resultado del código de ejemplo.



Recuerda ensamblar el control LongListSelector
al añadir todos los estilos y plantillas que acabas de crear, como se muestra en el siguiente ejemplo:
1 |
<phone:LongListSelector
|
2 |
x:Name="PeopleByCity" |
3 |
ItemTemplate="{StaticResource PeopleItemTemplate}" |
4 |
GroupHeaderTemplate="{StaticResource PeopleCityGroupHeaderTemplate}" |
5 |
JumpListStyle="{StaticResource PeopleCityJumpListStyle}" |
6 |
IsGroupingEnabled="True" /> |
Interactuando con la Lista
Los dos conceptos clave para tener en cuenta cuando interactúas con un control LongListSelector
(o cualquier otro control que hereda de la clase ItemsControl
son:
- el evento
SelectionChanged
, que es desencadenado cada vez que el usuario presiona uno de los elementos de la lista - la propiedad
SelectedItem
, que guarda los elementos que han sido seleccionados por el usuario
Con la combinación de éstos dos elementos, podrás detectar cuando y que elemento ha sido seleccionado por el usuario, y responder adecuadamente. Por ejemplo, puedes redireccionar al usuario a una página donde puede ver los detalles del elemento seleccionado.
1 |
<phone:LongListSelector
|
2 |
x:Name="People" |
3 |
GroupHeaderTemplate="{StaticResource PeopleGroupHeaderTemplate}" |
4 |
ItemTemplate="{StaticResource PeopleItemTemplate}" |
5 |
SelectionChanged="LongListSelectorAlphabetic_Loaded" /> |
El ejemplo previo muestra un control LongListSelector
que ha sido suscrito al evento SelectionChanged
. El siguiente ejemplo, en cambio, muestra el código del event handler (manejador de evento):
1 |
private void People_OnSelectionChanged(object sender, SelectionChangedEventArgs e) |
2 |
{
|
3 |
Person selectedPerson = People.SelectedItem as Person; |
4 |
string uri = string.Format("/DetailPage.xaml?Id={0}", selectedPerson.Id); |
5 |
NavigationService.Navigate(new Uri(uri, UriKind.Relative)); |
6 |
}
|
El ejemplo anterior muestra un control LongListSelector
que ha sido suscrito al evento SelectionChanged
. El siguiente ejemplo, en cambio, muestra el código del event handler:
1 |
private void People_OnSelectionChanged(object sender, SelectionChangedEventArgs e) |
2 |
{
|
3 |
Person selectedPerson = People.SelectedItem as Person; |
4 |
string uri = string.Format("/DetailPage.xaml?Id={0}", selectedPerson.Id); |
5 |
NavigationService.Navigate(new Uri(uri, UriKind.Relative)); |
6 |
}
|
Gracias a la propiedad SelectedItem
, tomamos el objeto seleccionado Person
y redireccionamos al usuario a otra página llamada DetailPage.xaml
para mostrar los detalles de la persona seleccionada. Cubriremos la navegación en profundidad en el próximo artículo.
El Windows Phone Toolkit
En éste artículo hemos visto solo los controles básicos, pero notarás que faltan muchos controles en el SDK que son utilizados en otras aplicaciones. La mayoría de ellos están disponibles en una librería llamada Windows Phone Toolkit, que está disponible en CodePlex y NuGet. Es mantenida directamente por Microsoft y es una forma de mantener un proceso de desarrollo separado y más rápido que el necesario para lanzar una nueva versión del SDK.
Aquí está una breve lista de los controles disponibles más importantes:
-
ToggleSwitch
: Especialmente útil para configurar páginas, ya que es un cambio que puede ser usado para definir un valor booleano (on/off). -
ContextMenu
: Un menú que puede ser mostrado cuando usuarios mantienen presionado un elemento. -
DatePicker
yTimePicker
: Usados para seleccionar una fecha o la hora respectivamente. -
WrapPanel
: Un contenedor especial que puede alinear controles anidados uno junto a otro y automáticamente pasar a otra línea si no queda espacio. -
AutoCompleteBox
: UnTextBox
especial que puede dar sugerencias al usuario basadas en el texto que el usuario está escribiendo. -
ListPicker
: Usado para mostrar una lista de elementos. Es útil especialmente cuando le pides a usuarios escoger entre diferentes valores. -
ExpanderView
: Usado para crear elementos que pueden ser expandidos usando una estructura de árbol para mostrar otros elementos. Un buen ejemplo de éste control es la aplicación Mail; es usada para mostrar conversaciones. -
MultiSelectList
: Similar a unListBox
, pero automáticamente coloca casillas de verificación junto a cada elemento, permitiendo a los usuarios seleccionar elementos múltiples. -
PhoneTextBox
: Un controlTextBox
especial con muchas características integradas, como soporte para íconos de acción, marcadores de posición, un contador de caracteres, etc. -
HubTile
: Puede ser usado para crear la experiencia de la pantalla Inicio ofrecida por Live Tiles dentro de una aplicación. -
CustomMessageBox
: UnMessageBox
especial que ofrece muchas más opciones que el estándar, como personalización de botones, soporte de plantilla personalizada, etc. -
Rating
: Da a los usuarios la posibilidad de clasificar algo dentro de una aplicación. La experiencia de usuario es similar a la ofrecida por la Store (tienda), donde los usuarios pueden votar por una aplicación. -
SpeechTextBox
: OtroTextBox
especial que soporta reconocimiento de voz para que los usuarios puedan dictar texto en lugar de escribirlo.
El Windows Phone Toolkit también incluye un remplazo del marco Windows Phone (la clase que maneja las vistas y la navegación) con soporte integrado para animaciones para que puedes fácilmente añadir efectos de transición cuando los usuarios se mueven de una página de la aplicación a otra. Adentrémonos más en éste tópico.
Transiciones de Página
En éste artículo, hemos aprendido como animar objetos que son colocados en una página. Con frecuencia, una de las formas más fáciles de mejorar el aspecto y sensación de nuestra aplicación es añadir una animación durante la transición de una página a otra. El Windows Phone Toolkit juega un importante papel en lograr el resultado ya que la el cuadro de la aplicación estándar proporcionado por el SDK, que maneja todas las páginas de nuestra aplicación, no soporta transiciones. En cambio, el toolkit ofrece una clase frame específica, llamado TransitionFrame
, que puede usarse para remplazar a la clase frame original, que es llamada PhoneApplicationFrame
.
El primer paso es remplazar el frame original. Puedes hacer ésto en el archivo App.xaml.cs
, que contiene una región oculta titulada Phone application intialization. Si lo expandes, encontrarás un método llamado InitializePhoneApplication()
, que entre otras cosas, inicializa el frame de la aplicación con el siguiente código:
1 |
RootFrame = new PhoneApplicationFrame(); |
Remplazando el frame original es fácil. Una vez que has instalado el Windows Phone Toolkit, puedes cambiar la inicialización del objeto RootFrame
al usar la clase TransitionFrame
, que es parte del namespace Microsoft.Phne.Controls
, como se muestra en el siguiente ejemplo:
1 |
RootFrame = new TransitionFrame(); |
Ahora estás listo para establecer que animaciones quieres usar en tus páginas de acuerdo al tipo de navegación. Comencemos por observar el siguiente código de ejemplo, que debe ser añadido en cada página que quieras animar con una transición. El código tiene que ser colocado bajo el nodo de la principal PhoneApplicationPage
antes de que sea definido el layout de la página:
1 |
<toolkit:TransitionService.NavigationInTransition>
|
2 |
<toolkit:NavigationInTransition>
|
3 |
<toolkit:NavigationInTransition.Backward>
|
4 |
<toolkit:TurnstileTransition Mode="BackwardIn"/> |
5 |
</toolkit:NavigationInTransition.Backward>
|
6 |
<toolkit:NavigationInTransition.Forward>
|
7 |
<toolkit:TurnstileTransition Mode="ForwardIn"/> |
8 |
</toolkit:NavigationInTransition.Forward>
|
9 |
</toolkit:NavigationInTransition>
|
10 |
</toolkit:TransitionService.NavigationInTransition>
|
11 |
|
12 |
<toolkit:TransitionService.NavigationOutTransition>
|
13 |
<toolkit:NavigationOutTransition>
|
14 |
<toolkit:NavigationOutTransition.Backward>
|
15 |
<toolkit:TurnstileTransition Mode="BackwardOut"/> |
16 |
</toolkit:NavigationOutTransition.Backward>
|
17 |
<toolkit:NavigationOutTransition.Forward>
|
18 |
<toolkit:TurnstileTransition Mode="ForwardOut"/> |
19 |
</toolkit:NavigationOutTransition.Forward>
|
20 |
</toolkit:NavigationOutTransition>
|
21 |
</toolkit:TransitionService.NavigationOutTransition>
|
Transiciones son añadidas usando TransitionService
, que es parte del Windows Phone Toolkit (asegúrate que el namespace xmlns:toolkit="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone.Controls.Toolkit"
sea añadido a tu página.
Soporta dos tipos de animaciones, especificadas usando la propiedad NavigationInTransition
:
- animaciones
In
, que son aplicadas cuando usuarios se mueven a la página actual - animaciones
Out
, que son aplicadas cuando usuarios se van de la página actual.
Para cada tipo de transición, también tienes la oportunidad de especificar dos condiciones adicionales:
- La propiedad
Backward
es usada para especificar la transición a usar cuando usuarios llegan a la página después de presionar el botón Back. - La propiedad
Forward
es usada para especificar la transición a usar cuando usuarios llegan a la página a través del flujo regular de navegación.
Finalmente, puedes especificar que transición quieres usar para cada escenario.} El toolkit ofrece una lista de animaciones predefinidas, como RotateTransition
para aplicar un efecto de rotación, TurnstileTransition
para simular hojear un libro, o SlideTransition
para aplicar un efecto de deslizamiento. Cada transición ofrece una propiedad Mode
, que puede ser usada para personalizar el efecto que es aplicado. El ejemplo previo muestra un efecto TurnstileTransition
aplicado durante cada navegación, con un efecto diferente de acuerdo al tipo de navegación (hacia atrás o hacia adelante).
Personalizar la Turnstile Transition
El framework de animación puede ser usado no sólo para aplicar una transición a una página entera, sino también a cada control de la página. Éste escenario es soportado por el control TurnstileFeatherTransition
, que aplica un efecto turnstile (voltear página) a los controles de la página. Puedes decidir que orden será usada para animar e ingresar las controles en la página con la propiedad FeatheringIndex
.
El primer paso es añadir un TransitionService
a tu página y definir un conjunto de animaciones TurnstileFeatherTransition
, como en el siguiente ejemplo:
1 |
<toolkit:TransitionService.NavigationInTransition>
|
2 |
<toolkit:NavigationInTransition>
|
3 |
<toolkit:NavigationInTransition.Backward>
|
4 |
<toolkit:TurnstileFeatherTransition Mode="BackwardIn"/> |
5 |
</toolkit:NavigationInTransition.Backward>
|
6 |
<toolkit:NavigationInTransition.Forward>
|
7 |
<toolkit:TurnstileFeatherTransition Mode="ForwardIn"/> |
8 |
</toolkit:NavigationInTransition.Forward>
|
9 |
</toolkit:NavigationInTransition>
|
10 |
</toolkit:TransitionService.NavigationInTransition>
|
11 |
<toolkit:TransitionService.NavigationOutTransition>
|
12 |
<toolkit:NavigationOutTransition>
|
13 |
<toolkit:NavigationOutTransition.Backward>
|
14 |
<toolkit:TurnstileFeatherTransition Mode="BackwardOut"/> |
15 |
</toolkit:NavigationOutTransition.Backward>
|
16 |
<toolkit:NavigationOutTransition.Forward>
|
17 |
<toolkit:TurnstileFeatherTransition Mode="ForwardOut"/> |
18 |
</toolkit:NavigationOutTransition.Forward>
|
19 |
</toolkit:NavigationOutTransition>
|
20 |
</toolkit:TransitionService.NavigationOutTransition>
|
Entonces, puedes aplicar la propiedad TurnstileFeatherTransition.FeatheringIndex
a cualquier control en la página y especificar el orden en el que aparecerán, comenzando con 0 para definir el primer control que ingresará en la página.
1 |
<StackPanel>
|
2 |
<TextBlock Text="First Element" |
3 |
toolkit:TurnstileFeatherEffect.FeatheringIndex="0"/> |
4 |
<TextBlock Text="Second Element" |
5 |
toolkit:TurnstileFeatherEffect.FeatheringIndex="1"/> |
6 |
<TextBlock Text="Third Element" |
7 |
toolkit:TurnstileFeatherEffect.FeatheringIndex="2"/> |
8 |
</StackPanel>
|
En el ejemplo previo, los tres controles TextBlock
aparecerán en la página comenzando con el primero (cuyo FeatheringIndex
es igual a 0) y finalizando con el último (cuyo FeatheringIndex
es igual a 2).
Conclusión
Ha sido un viaje largo hasta ahora y apenas hemos tenido contacto con la superficie; cubriendo todas las características del XAML requeriría un libro entero. En éste artículo, hemos considerado algunos conceptos clave usados en el desarrollo de Windows Phone.
- Presentamos los conceptos básico de XAML como propiedades, eventos, namespaces, y recursos.
- Aprendimos como funciona el enlazado de datos. Es una de las características mas poderosas de XAML y es muy importante entenderla para ser productivo.
- Hemos visto algunos de los controles básicos que son incluídos en el SDK y como podemos usarlos para definir la interfaz de usuario de nuestra aplicación.
- Discutimos algunos controles que son específicos a la experiencia Windows Phone, como los controles
Panorama
,Pivot
, yApplicationBar
.
Éste tutorial, representa un capítulo de Windows Phone 8 de Manera Concisa, un libro electrónico gratuito del equipo de Syncfusion.
¡Sé el primero en conocer las nuevas traducciones–sigue @tutsplus_es en Twitter!