Windows Phone–Tutorial XII–ProgressBar y MVVM Light

En esta ocasión voy ha hablar sobre como implementar una barra de progreso con el framework MVVM light. Es importante notificar al usuario que la aplicación esta esperando a que un servicio responda o que le informemos del porcentaje de bajada/subida de un fichero, siempre debemos de preocuparnos de darle feedback sin ese feedback puede que el usuario sienta que la aplicación se ha colgado y se salga de ella cuando lo único que esta haciendo es esperar la respuesta de un servicio.

Windows Phone nos provee de un control ProgressBar (igual que el de silverlight) que podemos utilizar en nuestros desarrollos, pero a mi me gusta mucho mas (es el que utilizo) el creado por Alex Yakhnin (podéis descargarlo de http://blogs.msdn.com/b/priozersk/archive/2010/09/20/creating-progress-dialog-for-wp7.aspx) me ofrece muchas mas opciones que el estándar. El objetivo del articulo no es hablar del control ProgressBar sino como desde nuestra viewmodel somos capaces de acceder a elementos de la vista.

Imaginaros que en nuestra aplicación a la hora de autenticarse utilizamos un servicio WCF que se nos proporciona con el método Authenticate, como supongo que sabréis, la llamada a servicios se hacen siempre de forma asíncrona al igual que Silverlight. Si ese servicio de autentificación tarda unos segundos debemos de notificar al usuario que la aplicación esta haciendo algo y que no esta colgada

image

La problemática  es que al servicio se llama desde la viewmodel y en ese momento debemos mostrar el control progressbar y cuando nos llega el evento Completed del servicio debemos de esconder el control. Para resolver este problema MVVM Light nos ofrece la clase estática DispatcherHelper junto con el método CheckBeginInvokeOnUI que nos permite acceder a la vista desde la viewmodel, como siempre mejor con un ejemplo

En la viewmodel definiremos una propiedad que es el control de la barra de progreso

1 private ProgressIndicator _progress; 2 public ProgressIndicator Progress 3 { 4 get 5 { 6 return _progress; 7 } 8 set 9 { 10 _progress = value; 11 RaisePropertyChanged("Progress"); 12 } 13 } 14

En el método invocado por el RelayCommand SaveCommand tendremos el siguiente código

1 public MainViewModel() 2 { 3 if (IsInDesignMode) 4 { 5 // Code runs in Blend --> create design time data. 6 } 7 else 8 { 9 _setingsUser = new Settings(); 10 SaveCommand = new RelayCommand(Save); 11 } 12 } 13 14 private void Save() 15 { 16 if (SettingsUser != null) 17 { 18 DispatcherHelper.CheckBeginInvokeOnUI(() => 19 { 20 if (Progress == null) 21 { 22 Progress = new ProgressIndicator(); 23 Progress.ProgressType = ProgressTypes.WaitCursor; 24 Progress.Text = "Loading data..."; 25 } 26 Progress.Show(); 27 }); 28 MyNikePlusProxy.MyNikePlusServiceClient proxy = new MyNikePlusProxy.MyNikePlusServiceClient(); 29 proxy.AuthenticateCompleted += new System.EventHandler<MyNikePlusProxy.AuthenticateCompletedEventArgs>(proxy_AuthenticateCompleted); 30 proxy.AuthenticateAsync(SettingsUser.IdUser, SettingsUser.Password); 31 32 } 33 34 } 35

 

Como podéis observar es muy fácil de utilizar y  no necesita ninguna explicación, solo nos quedaría el completed

 

1 void proxy_AuthenticateCompleted(object sender, MyNikePlusProxy.AuthenticateCompletedEventArgs e) 2 { 3 if (e.Error == null && e.Result != string.Empty) 4 { 5 SettingsUser.AuthenticatedCookie = e.Result; 6 IsolatedStorageHelper ish = new IsolatedStorageHelper(); 7 ish.SaveEntity<Settings>("settings.dat", SettingsUser); 8 DispatcherHelper.CheckBeginInvokeOnUI(() => 9 { 10 Progress.Hide(); 11 }); 12 13 } 14 } 15

No necesita mas explicación, a probarlo!!!

Quitar Converter de WPF con BindingBase.StringFormat

La propiedad BindingBase.StringFormat es nueva en WPF 4.0 y nos va ayudar a reducir los converters que hemos tenido que hacer en las versiones anteriores, en la MSDN vienen unos cuantos ejemplos los podéis descargar y echar un vistazo, yo como siempre os lo voy a enseñar a través de un ejemplo.

Si creamos la típica clase Persona con las siguientes propiedades

1 public enum Sexo 2 { 3 hombre, 4 mujer 5 } 6 7 public class Person : INotifyPropertyChanged 8 { 9 #region Properties 10 11 public string Nombre 12 { 13 get { return _nombre; } 14 set { _nombre = value; OnPropertyChanged("Nombre"); } 15 } 16 17 private string _nombre; 18 19 public string Apellido 20 { 21 get { return _apellido; } 22 set { _apellido = value; OnPropertyChanged("Apellido"); } 23 } 24 private string _apellido; 25 26 public int Edad 27 { 28 get { return _edad; } 29 set { _edad = value; OnPropertyChanged("Edad"); } 30 } 31 private int _edad; 32 33 public DateTime FechaNacimiento 34 { 35 get { return _fechaNacimiento; } 36 set { _fechaNacimiento = value; OnPropertyChanged("FechaNacimiento"); } 37 } 38 private DateTime _fechaNacimiento; 39 40 public Sexo Sex 41 { 42 get { return _sex; } 43 set { _sex = value; OnPropertyChanged("Sex"); } 44 } 45 private Sexo _sex; 46 47 #endregion 48 49 #region Constructor(s) 50 51 public Person() { } 52 53 public Person(string firstName, string lastName, int age, DateTime birthDate, Sexo sex) 54 { 55 this.Nombre = firstName; 56 this.Apellido = lastName; 57 this.Edad = age; 58 this.FechaNacimiento = birthDate; 59 this.Sex = sex; 60 } 61 62 #endregion 63 64 #region INotifyPropertyChanged Members 65 66 public event PropertyChangedEventHandler PropertyChanged; 67 68 void OnPropertyChanged(string propName) 69 { 70 if (this.PropertyChanged != null) 71 this.PropertyChanged(this, new PropertyChangedEventArgs(propName)); 72 } 73 74 #endregion 75 }

 

Típica clase que al hacer el binding deberíamos realizar diferentes converters para el Sexo, la Edad y el Nombre, con BindingBase.StringFormat vamos a ver como no harían falta estos converters

En el code-behind de la ventana crearíamos la instancia de persona y asignaríamos el DataContext

1 public partial class MainWindow : Window 2 { 3 public Person Person 4 { 5 get 6 { 7 if (_person == null) 8 _person = new Person("Oscar", "Alvarez", 29, new DateTime(1978, 3, 9), Sexo.hombre); 9 10 return _person; 11 } 12 set 13 { 14 _person = value; 15 } 16 }private Person _person; 17 18 public MainWindow() 19 { 20 InitializeComponent(); 21 this.DataContext = Person; 22 } 23 24 }

Ahora el Xaml para ver como utilizaríamos la propiedad

1 <Window x:Class="WPFStringFormat.MainWindow" 2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 4 Title="MainWindow" Height="350" Width="525"> 5 <Grid> 6 <StackPanel> 7 <TextBlock Margin="5"> 8 <TextBlock.Text> 9 <MultiBinding StringFormat="Hola {0} {1}!"> 10 <Binding Path="Nombre"/> 11 <Binding Path="Apellido"/> 12 </MultiBinding> 13 </TextBlock.Text> 14 </TextBlock> 15 <TextBlock Text="{Binding Path=Edad, StringFormat='Tienes{0} años.'}" Margin="5"/> 16 <TextBlock Text="{Binding Path=Sex, StringFormat='Eres un {0}.'}" Margin="5"/> 17 <TextBlock Text="{Binding Path=FechaNacimiento, StringFormat='naciste en {0:dd/MM/yyyy}'}" Margin="5"/> 18 </StackPanel> 19 </Grid> 20 </Window> 21

Con esta nueva característica ahorraremos bastantes converters

Lo que viene el próximo Año para Windows Phone

Según el blog de Mary Jo Foley (lo cual es una buena fuente) el próximo año nos viene un aluvión de novedades en el sistema operativo de Windows Phone. Parece ser que Microsoft tiene programada tres actualizaciones para el próximo año.

La primera actualización aparecerá en enero en el CES de las Vegas y añadirá el tan pedido copiar y pegar (Ver video) junto a otras novedades menores, como integración con SkyDrive, o navegación turn-by-turn en Bing Maps.

La segunda vendrá en el Mobile World Conference de Barcelona. En esta se incluirá soporte para multitasking (lo que cambiara nuestra forma de programar las aplicaciones) mediante una nueva API, mayores posibilidades de personalización, ya sea mediante temas, ringtones, y también permitirá a los desarrolladores ofrecer descargas dentro de las mismas aplicaciones.

Por ultimo a mediados del 2011 viene la mas importante y para mi un salto de calidad importante ya que el navegador de Windows Phone 7 gozará de soporte para HTML5 y Silverlight al desplegar sitios web lo cual será una gran diferenciación con sus competidores.

Así que el próximo año nos tendremos que seguir poniendo las pilas

 

Explora tus opciones

Windows Phone –Tutorial XI Tombstoning

Tombstone para Windows Phone no es el deporte de saltar al mar desde un acantilado

En Windows Phone hablamos de el en Windows Phone 7 – Tutorial III–Modelo de Ejecución. En Windows Phone solo se permite una aplicación corriendo a la vez, todavía no hay la multitarea aunque la prometen en la siguiente versión a principios del año que viene, por ahora nos conformaremos con que nuestras aplicaciones no tiene multitarea.

Si abrimos el fichero app.xaml.cs vemos que tenemos 4  métodos vacíos pero que van a ser importantes para entender el concepto de Tombstoning, estos métodos son:

  • Application_Launching
  • Application_Activated
  • Application_Deactivated
  • Application_Closing

Utilizaremos la aplicación que hemos hecho en el articulo sobre Launchers, vamos a introducir en estos métodos una traza para ver cuando se lanzan, para esta traza utilizaremos la clase Debug

Debug.WriteLine("Debug Application_Launching:" + DateTime.Now.ToLongTimeString());

  • Si ejecutamos la aplicación y pulsamos al botón la flecha de retroceso del teléfono, las trazas que se generan son

                Debug Application_Launching:7:22:38 PM
                Debug Application_Closing:7:22:45 PM

Se ha ejecutado el evento de Close al pulsar la flecha de retroceso desde la primera pantalla de la aplicación.

  • Ahora vamos a pulsar la tecla de Windows del teléfono, las trazas generadas son:

               Debug Application_Launching:7:25:25 PM
               Debug Application_Deactivated:7:25:40 PM

Se ha ejecutado en este caso el evento Deactivate en vez de Close

  • Si ahora lanzamos un Launcher de la aplicación

              Debug Application_Launching:7:30:36 PM
              Debug Application_Deactivated:7:30:47 PM

Se han ejecutado los mismo eventos que pulsando la tecla de Windows

Es decir nuestra aplicación es cerrada cuando el usuario desde la primera pagina de la aplicación pulsa la flecha de retroceso, en ese momento se cierra la aplicación, en los demás casos la aplicación se desactiva pudiéndose reactivar, este ultimo procesos de desactivación/reactivación  es el llamado Tomnstoning, que podemos ver en la figura de abajo.

tombstoninglifecycle

Justo antes de desactivar la aplicación podremos salvar el estado de la misma para que cuando se vuelva a activar mostremos al usuario la aplicación tal y como la había dejado. La idea es ayudar a la aplicación a dar la sensación de que ha continuado ejecutando como si nada a pesar de que fue, en efecto, eliminada temporalmente.

Para guardar el estado a la hora de desactivarse no utilizaremos el IsolatedStorage que vimos en el anterior post. Windows Phone nos proporciona la clase PhoneApplicationService.que tiene la propiedad Current y a su vez la propiedad State  que es un diccionario al estilo de IsolatedStorageSettings donde guardaremos pares de clave/valor en el evento Deactivate para luego en el Activate recuperar los valores.

El código seria muy sencillo

1 private void Application_Activated(object sender, ActivatedEventArgs e) 2 { 3 Debug.WriteLine("Debug Application_Activated:" + DateTime.Now.ToLongTimeString()); 4 IDictionary<string, object> state = PhoneApplicationService.Current.State; 5 if (state.ContainsKey("DateDeactivate")) 6 { 7 Debug.WriteLine("El valor de DateDeactivate:" + state["DateDeactivate"]); 8 } 9 } 10 11 // Code to execute when the application is deactivated (sent to background) 12 // This code will not execute when the application is closing 13 private void Application_Deactivated(object sender, DeactivatedEventArgs e) 14 { 15 string dateDeactivate = DateTime.Now.ToLongTimeString(); 16 Debug.WriteLine("Debug Application_Deactivated:" + dateDeactivate); 17 IDictionary<string, object> state = PhoneApplicationService.Current.State; 18 state["DateDeactivate"] = dateDeactivate; 19 20 } 21 22

Si ejecutamos la aplicación y para debugear pulsamos un botón de un Launcher como el de SearchTask y damos al botón de retroceso para que vuelva a la aplicación y se produzca el efecto Tombstoning

imageimage .

Los mensajes que se muestran de la traza del código vemos que es

Debug Application_Activated:10:05:53 PM

El valor de DateDeactivate:10:05:47 PM

Vemos que a la hora de activar la aplicación recuperamos el valor que hemos guardado al desactivar la aplicación.

Guardaremos en PhoneApplicationService aquellos datos que no estén asociados a una pagina, en cambio si queremos guardar datos asociados a una pagina como la posición de un scroll, los contenidos de un TextBox… Debemos de utilizar los eventos OnNavigatedTo y OnNavigatedFrom.

Una concepto importante que tenemos que tener en cuenta es que en PhoneApplicationService son datos transitorios para el efecto de Tombstoning si queremos datos permanentes para cuando se vuelva a encender la aplicación en el caso de que el usuario la cierre (evento Close) utilizaremos lo visto en el anterior post IsolatedStorage.

Si queréis debugear el efecto Tombstoning sin lanzar ningun Launcher o Chooser debéis de pulsar el botón de windows en el emulador y rápidamente el de retroceso, en este caso se lanzaran los eventos Deactivate y Activated

Espero que os haya ayudado a entender este concepto, importante para dar un experiencia correcta al usuario y para que os aprueben vuestra aplicación en el marketplace

Dos nuevos recursos para nuestros desarrollos en Windows Phone

Microsoft ha dispuesto de dos recursos nuevos para guiarnos en el desarrollo de Windows Phone.

El primero son presentaciones y documentos junto con laboratorios

1 Platform Overview

2 Introduction to Silverlight

3 Introduction to Visual Studio

4 User Interface Design with Silverlight

5 Consuming Data Services

6 XNA Overview

7 Creating Windows Phone Applications

8 Using the Marketplace to sell solutions

Podéis encontrarlo en esta dirección: https://www.facultyresourcecenter.com/curriculum/pfv.aspx?ID=8729

 

El segundo recurso es un white paper sobre como desarrollar aplicaciones web para windows phone

 

image

Podéis descargarlo en : http://bit.ly/giUNXg

Merece la pena que les echéis un vistazo.

Windows Phone –Tutorial X IsolatedStorage

IsolatedStorage en Windows Phone es el mismo concepto que apareció en la versión 2 de Silverlight, nos va a permitir almacenar datos en el dispositivo para ser utilizados por la aplicación. Únicamente nuestra aplicación podrá utilizarlos, estos datos no pueden ser compartidos entre aplicaciones, si quieres compartir datos entre dos aplicaciones deberás utilizar la nube para ello.

En Windows Phone IsolatedStorage tiene dos modalidades:

  • Local Settings: La utilizaremos para guardar settings de la aplicación solo vamos a poder almacenar pares de clave/valor como si fuese un diccionario
  • Files & Folders: Nos permite crear carpetas y ficheros que almacenen cualquier tipo de información.

En la imagen de abajo extraída de la MSDN vemos de modo gráfico las dos posibilidades.

isolatedstorage

Para utilizar IsolatedStorage deberemos de utilizar las clases del assembly System.IO.IsolatedStorage, dentro de este assembly tenemos las clases IsolatedStorageFile y IsolatedStorageSettings para realizar los dos tipos antes mencionados.

IsolatedStorageSettings 

Permite almacenar pares clave/valor como si fuese un diccionario para poder cargarlos la siguiente vez que realicemos la carga de la aplicación. Vamos a verlo con una aplicación donde almacenaremos las settings de la aplicación.

image

El Xaml seria muy sencillo y no tendría problemas

1 x:Name="ContentGrid" 2 Grid.Row="1"> 3 <Grid.RowDefinitions> 4 <RowDefinition /> 5 <RowDefinition /> 6 <RowDefinition /> 7 <RowDefinition /> 8 <RowDefinition /> 9 <RowDefinition /> 10 <RowDefinition /> 11 </Grid.RowDefinitions> 12 <Grid.ColumnDefinitions> 13 <ColumnDefinition Width="189*" /> 14 <ColumnDefinition Width="291*" /> 15 </Grid.ColumnDefinitions> 16 <TextBlock Grid.Row="1" Height="36" HorizontalAlignment="Right" Name="textBlock1" Text="Usuario:" VerticalAlignment="Center" TextAlignment="Right" Grid.ColumnSpan="1" /> 17 <TextBox Grid.Column="1" Text="{Binding SettingsUser.UserName, Mode=TwoWay}" Grid.Row="1" HorizontalAlignment="Left" Name="txtUser" VerticalAlignment="Center" Height="77" Width="295" /> 18 <TextBlock Height="36" HorizontalAlignment="Right" Name="textBlock2" Text="Password:" TextAlignment="Right" VerticalAlignment="Center" Grid.Row="2" Grid.ColumnSpan="1" /> 19 <PasswordBox Password="{Binding SettingsUser.Password, Mode=TwoWay}" Grid.Column="1" Grid.Row="2" Height="77" HorizontalAlignment="Left" Name="txtPassword" VerticalAlignment="Top" Width="295" /> 20 <TextBlock Height="36" HorizontalAlignment="Right" Name="textBlock3" Text="Correo Electronico:" TextAlignment="Right" VerticalAlignment="Center" Grid.Row="3" Grid.ColumnSpan="1" /> 21 <TextBox Text="{Binding SettingsUser.Email, Mode=TwoWay}" Height="77" HorizontalAlignment="Left" Name="txtEmail" VerticalAlignment="Center" Width="295" Grid.Row="3" Grid.Column="1"> 22 <TextBox.InputScope> 23 <InputScope> 24 <InputScopeName NameValue="EmailNameOrAddress" /> 25 </InputScope> 26 </TextBox.InputScope> 27 </TextBox> 28 </Grid> 29 </Grid> 30 31 <phone:PhoneApplicationPage.ApplicationBar> 32 <shell:ApplicationBar IsVisible="True" IsMenuEnabled="False"> 33 <shell:ApplicationBarIconButton x:Name="appbar_btnSave" Click="appbar_btnSave_Click" IconUri="/Images/appbar.save.rest.png" Text="Siguiente"> 34 </shell:ApplicationBarIconButton> 35 </shell:ApplicationBar> 36 </phone:PhoneApplicationPage.ApplicationBar> 37

He creado un modelo con las settings del usuario que queremos guardar

1 public class Settings 2 { 3 public string UserName { get; set; } 4 public string Password { get; set; } 5 public bool RememberPassword { get; set; } 6 public string Email { get; set; } 7 }

 

En la ViewModel he creado un comando para que cuando pulse en el botón de salvar se guarden las settings del usuario.

1 ............... 2 private Settings _settingsUser = null; 3 public Settings SettingsUser 4 { 5 get 6 { 7 return _settingsUser; 8 } 9 set 10 { 11 _settingsUser = value; 12 RaisePropertyChanged("SettingsUser"); 13 } 14 } 15 public RelayCommand SaveCommand { get; private set; } 16 /// <summary> 17 /// Initializes a new instance of the MainViewModel class. 18 /// </summary> 19 public MainViewModel() 20 { 21 if (IsInDesignMode) 22 { 23 // Code runs in Blend --> create design time data. 24 } 25 else 26 { 27 _settingsUser = new Settings(); 28 IsolatedStorageHelper ish = new IsolatedStorageHelper(); 29 ish.RestoreEntity(_settingsUser); 30 SaveCommand = new RelayCommand(Save); 31 } 32 } 33 34 private void Save() 35 { 36 if (SettingsUser != null) 37 { 38 IsolatedStorageHelper ish = new IsolatedStorageHelper(); 39 ish.SaveEntity(SettingsUser); 40 } 41 42 } 43

Ahora al grano si os fijáis en el método Save he creado una nueva clase IsolatedStorageHelper  en la que he hecho un pequeño wrapping de IsolatedStorageSettings esta clase permite salvar un par clave valor o toda una entidad, es decir, se recorre las propiedades y va salvando cada una de las propiedades.

La clase IsolatedStorageSettings funciona como un diccionario, tiene un método Contains(string key) que comprueba si esta la clave ya almacenada, si no esta almacenada la añade con el método Add(strinh key, object value) y si ya esta almacenada la asigna. Como veis es muy fácil de utilizar.

A la hora de recuperar las Settings utilizo el metodo RestoreEntity que recupera toda la entidad, esta baso en el metodo Restore que recupera un valor a partir de su clave como en un diccionario.

Aquí tenéis el código de IsolatedStorageHelper 

1 public class IsolatedStorageHelper 2 { 3 4 /// <summary> 5 /// Salva una entidad en IsolatedStorageSettings 6 /// </summary> 7 /// <param name="entity"></param> 8 public void SaveEntity(Object entity) 9 { 10 var propInfo = entity.GetType().GetProperties(); 11 if (null != propInfo) 12 { 13 for (int i = 0; i < propInfo.Length; i++) 14 { 15 Backup(propInfo[i].Name, propInfo[i].GetValue(entity, null)); 16 17 } 18 } 19 } 20 21 /// <summary> 22 /// Restaura una entidad completa 23 /// </summary> 24 /// <param name="entity"></param> 25 public void RestoreEntity(Object entity) 26 { 27 var propInfo = entity.GetType().GetProperties(); 28 if (null != propInfo) 29 { 30 for (int i = 0; i < propInfo.Length; i++) 31 { 32 propInfo[i].SetValue(entity,Restore(propInfo[i].Name), null); 33 } 34 } 35 } 36 37 /// <summary> 38 /// Salva una par clave/valor en IsolatedStorageSettings 39 /// </summary> 40 /// <param name="token"></param> 41 /// <param name="value"></param> 42 /// <returns></returns> 43 public bool Backup(string token, object value) 44 { 45 if (null == value) 46 return false; 47 48 var store = IsolatedStorageSettings.ApplicationSettings; 49 if (store.Contains(token)) 50 store[token] = value; 51 else 52 store.Add(token, value); 53 54 store.Save(); 55 return true; 56 } 57 58 /// <summary> 59 /// Devuelve un valor almacendao a partir de su clave 60 /// </summary> 61 /// <param name="token"></param> 62 /// <returns></returns> 63 public object Restore(string token) 64 { 65 var store = IsolatedStorageSettings.ApplicationSettings; 66 if (!store.Contains(token)) 67 return null; 68 69 return store[token]; 70 } 71 } 72

Es importante saber que a la hora de debugear las settings estas se quedan almacenadas mientras esta el emulador levando, si lo cerráis y volvéis a ejecutar la aplicación habéis perdido todo lo grabado. Una vez que deis al botón de guardar cerrar la aplicación desde el botón Stop de Visual Studio cierra la aplicación pero mantiene el emulador levantado.

Aquí tenéis el código para descargarlo

 

IsolatedStorageSettings 

Vamos a utilizar la aplicación anterior para guardar las settings de usuario esta vez en un fichero, en vez de el diccionario IsolatedStorageSettings. Vamos a grabar la instancia se Settings en un solo archivo, el formato de grabación puede ser cualquiera un fichero txt, un xml… En mi caso voy a grabar en un fichero con la extensión que quiera pero con el objeto serializado para que ocupe lo mínimo posible.

Para ello a nuestra clase Settings (Model) la vamos a decorar con el atributo DataContract y a cada propiedad con el atributo DataMember porque vamos a utilizar DataContractSerializer para la serialización del objeto.

Para salvar nuestro objeto hemos extendido la clase IsolatedStorageHelper  con nuevos métodos para Salvar y Recuperar un objeto.

1 public void SaveEntity<T>(string name, T objectToSave) 2 { 3 using (IsolatedStorageFile storageFile = IsolatedStorageFile.GetUserStoreForApplication()) 4 using (IsolatedStorageFileStream storageFileStream = new IsolatedStorageFileStream(name, System.IO.FileMode.Create, storageFile)) 5 { 6 DataContractSerializer serializer = new DataContractSerializer(typeof(T)); 7 serializer.WriteObject(storageFileStream, objectToSave); 8 } 9 } 10 11 public T RestoreEntity<T>(string name) where T : class, new() 12 { 13 T loadedObject = null; 14 using (IsolatedStorageFile storageFile = IsolatedStorageFile.GetUserStoreForApplication()) 15 using (IsolatedStorageFileStream storageFileStream = new IsolatedStorageFileStream(name, System.IO.FileMode.OpenOrCreate, storageFile)) 16 { 17 if (storageFileStream.Length > 0) 18 { 19 DataContractSerializer serializer = new DataContractSerializer(typeof(T)); 20 loadedObject = serializer.ReadObject(storageFileStream) as T; 21 } 22 if (loadedObject == null) 23 { 24 loadedObject = new T(); 25 } 26 } 27 28 return loadedObject; 29 }

Como podéis ver en el código de arriba el manejo es igual que en Silverlight se genera un archivo y se escribe o lee sobre el, no hay mucho misterio.

 

Os dejo el código

 

Windows Phone –Tutorial IX Launcher & Chooser

Si recordamos nuestro articulo Windows Phone 7 – Tutorial III–Modelo de Ejecución teníamos la definición de un concepto importante en el ciclo de vida de nuestra aplicación y eran los Launchers y Choosers, si recordamos las definimos

Launcher & Chooser: Son todas aquellas aplicaciones que vienen con el teléfono, como llamadas, mensajes, cámara y que nuestra aplicación podrá invocar. La diferencia entre Launcher y Chooser se puede resumir en que los Launchers no devuelven ninguna información de su ejecución y en cambio los Choosers si devuelven información al termino de su ejecución, un ejemplo para verlo mas claro un Launcher es una llamada de teléfono y un Chooser es la llamada a la cámara de fotos que nos devolverá la foto que ha realizado el usuario.

Windows Phone nos proporciona una API para cada uno de estos Launchers y Choosers, de esta manera podremos utilizarnos en nuestras aplicaciones.

Los Launchers se encuentran en el namespace Microsoft.Phone.Tasks y son los siguientes

He hecho una pequeña aplicación con una botonera con todos los launchers además de un Textbox, la finalidad del textbox es que rellenéis el textbox antes de dar a algún launcher para que comprobéis que cuando pasáis a un Launcher al volver a la aplicación, lo que habéis escrito ha desaparecido, en otros posts mas adelante veremos como solucionar este problema (tombstone).

 

image

Los Choosers están en el mismo espacio de nombres y son:

Cada Chooser tiene sus propiedades a la hora de llamarlos que al igual que los Launchers se realiza con el método Show(), pero lo que debemos de saber es como recoger el resultado de la acción del usuario como por ejemplo la foto que ha sacado el usuario. Para ello todos los Choosers tienen el evento Completed que se lanza cuando el usuario ha realizado la acción. Por ejemplo para PhotoChooserTask  utilizaríamos el siguiente código

1 private void btnPhotoChooserTask_Click(object sender, RoutedEventArgs e) 2 { 3 PhotoChooserTask pct = new PhotoChooserTask(); 4 pct.ShowCamera = true; 5 pct.Completed += new EventHandler<PhotoResult>(pct_Completed); 6 pct.Show(); 7 } 8 9 void pct_Completed(object sender, PhotoResult e) 10 { 11 if (e.TaskResult == TaskResult.OK) 12 { 13 var picture = e.ChosenPhoto; 14 BitmapImage imgBitmap = new BitmapImage(); 15 imgBitmap.SetSource(picture); 16 17 //Asignamos la imagen a un objeto de la aplicación 18 imgPlaceholder.Source = imgBitmap; 19 } 20 } 21

He realizado una pequeña aplicación con los Choosers que existen para que veais su utilización

image

 

Abajo os dejo una tabla con las características de los Launchers y Choosers

 

Task

Launcher

Chooser

Devuelve Datos

Tombstoning

Suspende Aplicación

CameraCaptureTask

 

X

X

X

X

EmailAddressChooserTask

 

X

X

X

X

EmailComposeTask

X

       

MarketplaceDetailTask

X

       

MarketplaceHubTask

X

       

MarketplaceReviewTask

X

       

MarketplaceSearchTask

X

       

MediaPlayerLauncher

X

   

X

X

PhoneCallTask

X

     

X

PhoneNumberChooserTask

 

X

X

X

X

PhotoChooserTask

 

X

X

X

X

SaveEmailAddressTask

X

X

SavePhoneNumberTask

X

SearchTask

X

SmsComposeTask

X

WebBrowserTask

X

Windows Phone –Tutorial VIII Themes

Windows Phone nos proporciona dos estilos para nuestros teléfonos que afectan al background, estos son dark o light

 

imageimage

Podemos cambiar el valor en Settings (en el emulador también aparece)

imageimage

Es importante tener en cuenta nuestros diseños para que funcione en cualquiera de los dos temas, para ello lo primero que tenemos que saber es como detectar cual es el que el usuario ha elegido para adaptar nuestros estilos de la aplicación al elegido por el usuario. La detección del tema que tiene el usuario en el teléfono es muy manual tanto que nos basaremos en detectar el color de fondo que tiene la aplicación, para ello debemos utilizar la siguiente tabla

image

Para detectarlo utilizaremos la siguiente instrucción

SolidColorBrush backgroundBrush = Application.Current.Resources["PhoneBackgroundBrush"] as SolidColorBrush;

Si la variable backgroundBrush es igual a Color.FromArgb(255, 0, 0, 0) estamos con Dark Theme y si es igual a Color.FromArgb(255, 255, 255, 255) estamos en Light Theme, de esta manera tan sencilla sabemos si el usuario ha elegido un tema u otro.

Vamos a ejecutar la aplicación del articulo Windows Phone–Tutorial – Navegación con MVVM Light Toolkit eligiendo Light Theme

 

imageimage

Podemos ver que los iconos de la barra de aplicación han cambiado automáticamente de estilo sin tener que hacer nada, pero que pasa si esa imagen la metemos n un objeto Image en cualquier parte de la pantalla.

imageimage

En el tema Light la imagen sigue estando pero el usuario no la ve porque es blanca, con lo que deberiamos cambiarla dependiendo del tema. Existe un truco para no tener que dibujar dos iconos, uno para cada tema, y es envolver la imagen con un rectángulo relleno con la brocha del tema que esta en el recurso PhoneForegroundBrush  y con mascara de opacidad

 

1 <Rectangle Width="48" Height="48" Fill="{StaticResource PhoneForegroundBrush}" Margin="100,152,332,344"> 2 <Rectangle.OpacityMask> 3 <ImageBrush ImageSource="/Assets/appbar.next.rest.png"/> 4 </Rectangle.OpacityMask> 5 </Rectangle> 6

image

 

Como podéis observar ya aparece nuestra imagen, tanto en fondo negro como en blanco.

Otro concepto que tenemos que tener en cuenta en nuestros diseños es el concepto accent color, que es el color que elige el usuario para resaltar, el color elegido esta en el recurso PhoneAccentColor

La lista de colores que tiene el usuario a elegir es :

 

image

Por ejemplo podemos aplicar el estilo PhoneAccentColor al titulo de la página y al fondo de la ventana

1 <Grid x:Name="LayoutRoot" Background="Transparent"> 2 <Grid.RowDefinitions> 3 <RowDefinition Height="Auto"/> 4 <RowDefinition Height="*"/> 5 </Grid.RowDefinitions> 6 7 <Rectangle Stroke="Black" Grid.RowSpan="2"> 8 <Rectangle.Fill> 9 <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0"> 10 <GradientStop Color="{StaticResource PhoneBackgroundColor}" Offset="0"/> 11 <GradientStop Color="{StaticResource PhoneAccentColor}" Offset="1"/> 12 </LinearGradientBrush> 13 </Rectangle.Fill> 14 </Rectangle> 15 16 <!--TitlePanel contains the name of the application and page title--> 17 <StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28"> 18 <TextBlock x:Name="ApplicationTitle" Text="Test" Style="{StaticResource PhoneTextNormalStyle}"/> 19 <TextBlock x:Name="PageTitle" Text="TEMAS" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"> 20 <TextBlock.Foreground> 21 <SolidColorBrush Color="{StaticResource PhoneAccentColor}"/> 22 </TextBlock.Foreground> 23 </TextBlock> 24 </StackPanel> 25 26 <!--ContentPanel - place additional content here--> 27 <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> 28 <TextBlock Height="589" HorizontalAlignment="Left" Margin="9,12,0,0" Name="textBox1" Text="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis volutpat, ipsum vehicula lobortis placerat, felis lorem varius massa, a accumsan mi justo vel augue. Donec venenatis magna et mauris ornare convallis. Mauris ut metus purus, eu pharetra nisi. Aliquam dignissim enim id nulla consequat ut tempor tortor porttitor. Morbi ultrices leo in augue rutrum ultrices. Proin consequat, metus id blandit tincidunt, quam neque blandit nisl, non consequat eros eros a lorem. Fusce tristique ornare convallis. Quisque ut tellus vel risus imperdiet vehicula. Praesent non metus eget massa cursus euismod. Etiam tempor rutrum nunc. Aenean aliquet dapibus felis sit amet elementum." VerticalAlignment="Top" Width="441" TextWrapping="Wrap" /> 29 </Grid> 30 </Grid> 31

Nos queda la aplicación

imageimage

Windows Phone –Tutorial VII InputScope

Durante este tutorial hemos visto que cuando pulsábamos en un TextBox, automáticamente aparecía en el emulador el teclado para que el usuario introduzca el texto en el TextBox.  Pero muchas veces esos TextBox van a pedir datos al usuario como un número de teléfono o una url … Windows Phone pone a disposición de nosotros una serie de teclados especializados para cada tipo de entrada que pidamos al usuario, estos tipos de teclados los indicaremos a través de la propiedad InputScope , esta propiedad contiene un enumerado para cada necesidad ofreciéndonos numerosas posibilidades:

1. AddressCity
2. AddressCountryName
3. AddressCountryShortName
4. AddressStateOrProvince
5. AddressStreet
6. AlphanumericFullWidth
7. AlphanumericHalfWidth
8. ApplicationEnd
9. Bopomofo
10. Chat
11. CurrencyAmount
12. CurrencyAmountAndSymbol
13. CurrencyChinese
14. Date
15. DateDay
16. DateDayName
17. DateMonth
18. DateMonthName
19. DateYear
20. Default
21. Digits
22. EmailNameOrAddress
23. EmailSmtpAddress
24. EmailUserName
25. EnumString
26. FileName
27. FullFilePath
28. Hanja
29. Hiragana
30. KatakanaFullWidth
31. KatakanaHalfWidth
32. LogOnName
33. Maps
34. NameOrPhoneNumber
35. Number
36. NumberFullWidth
37. OneChar
38. Password
39. PersonalFullName
40. PersonalGivenName
41. PersonalMiddleName
42. PersonalNamePrefix
43. PersonalNameSuffix
44. PersonalSurname
45. PhraseList
46. PostalAddress
47. PostalCode
48. Private
49. RegularExpression
50. Search
51. Srgs
52. TelephoneAreaCode
53. TelephoneCountryCode
54. TelephoneLocalNumber
55. TelephoneNumber
56. Text
57. Time
58. TimeHour
59. TimeMinorSec
60. Url
61. Xml
62. Yomi

Yo os voy a mostrar solo alguno de ellos, quizás los que considero mas importantes para que veais las diferencias entre ellos, por defecto su valor es el de Text y nos muestra el siguiente teclado

Standard

TelephoneNumber

 

image

URL

image

Number

image

EmailNameOrAddress

image

Así podíamos seguir con toda la lista, pero nos vamos a parar aquí. Os voy a contar un par de trucos que hay con esta propiedad, el primero es que si lo utilizáis de esta manera el IntelliSense no va a funcionar y no va a mostrar la lista de posibles valores.

<TextBox Text="{Binding UserText, Mode=TwoWay}" InputScope="Text" Height="74" HorizontalAlignment="Left" Margin="26,41,0,0" Name="txtText"
  VerticalAlignment="Top" Width="398" />

En cambio si lo utilizáis de esta manera va a aparecer

  <TextBox Text="{Binding UserText, Mode=TwoWay}"  Height="74" HorizontalAlignment="Left" Margin="26,41,0,0" Name="txtText"  VerticalAlignment="Top" Width="398" >
                <TextBox.InputScope>
                    <InputScope>
                        <InputScopeName NameValue="Text" />
                    </InputScope>
                </TextBox.InputScope>
            </TextBox>

 

image

 

El otro truco si habéis llegado hasta aquí es bastante interesante la hora de probar nuestras aplicaciones, seguramente os habéis dado cuenta de que el teclado de vuestro ordenador no funciona en el emulador que tenéis que ir dando clicks con el ratón en cada letra y eso es un poco lento. Para hacer que funcione el teclado solo tenéis que pulsar la tecla Pause/Break de vuestro teclado y ya os funcionará el teclado de vuestro ordenador.