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.
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.
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