MULTIMEDIA

Persisting Data on the Client with Silverlight 4

9/14/2010 11:50:21 AM
1. Problem

You need to persist data on the end user's machine.

2. Solution

Use isolated storage to store data on the client.

3. How It Works

In some situations, you may want to store data to the client's computer, such as user-specific settings or application state information. However, it is not possible to use the regular file system of the operating system from a web browser application, because native file system operations require full trust, but Web-based applications run in a partial-trust isolated sandbox.

NOTE

Elevated OOB applications have much more access to the file system.

Isolated storage provides a safe client-side storage area for partial-trust applications to persist information on a per-user basis. In Silverlight, all I/O operations are restricted to the isolated storage. Silverlight 4 includes the ability to run Silverlight applications OOB when online or offline. Offline Silverlight applications can still access the same isolated storage area as they can when running in the browser, so users have seamless access to their data.

Besides storing settings, isolated storage can be used to improve user experience as well as reduce bandwidth by storing partially filled-out forms, so that the form data can be reloaded when the user returns, even if, for example, the user stored the data using Internet Explorer but accesses the application later using Firefox.

The System.IO.IsolatedStorage namespace contains types for creating and using a virtual file system. Table 1 lists the classes available in this namespace.

Table 1. Classes Related to IsolatedStorage
ClassDescription
IsolatedStorageExceptionException that is thrown when an isolated storage operation fails
IsolatedStorageFileRepresents an isolated storage area containing files and directories
IsolatedStorageFileStreamRepresents a file within isolated storage
IsolatedStorageSettingsProvides a Dictionary object that stores key-value pairs within isolated storage

Isolated storage is not unlimited. Administrators can set user quota restrictions that limit the amount of data that can be stored in isolated storage, so it is not suited for large amounts of data. The default size of isolated storage for in-browser Silverlight applications is 1MB. The default size of isolated storage for out-of-browser Silverlight applications at install time (called detach) is 25MB.

NOTE

Isolated storage is not encrypted, though developers can encrypt and decrypt files stored in local storage if desired. Developers can also sign and validate signatures using the SHA1 hash function.

The quota can be increased further by the user through the UI thread, usually as a result of a UI event handler. Otherwise, the quota cannot be increased on a background thread or without user action. Isolated storage remains intact even if the browser cache is cleared, but isolated storage can be manually deleted by the user or application by using the File I/O classes.

Applications can request more space by invoking the IsolatedStorageFile.IncreaseQuotaTo method in response to a user-initiated event, such as a mouse click or key press, as noted previously.

To work with isolated storage, first obtain an isolated store for the application using the IsolatedStorageFile.GetUserStoreForApplication method. This returns an IsolatedStorageFile object, which you can use to create directories using the CreateDirectory method and files using the CreateFile method. The CreateFile method returns an IsolatedStorageFileStream object. The IsolatedStorageFileStream class inherits from FileStream, so you can use the class with StreamReader and StreamWriter objects.

Another option is to use the IsolatedStorageSettings class, which is a Dictionary object that can be used to quickly store key/value pairs in isolated storage.

4. The Code

To test isolated storage, your sample does two things. It allows a user to store and update a setting using the IsolatedStorageSettings class and to save and reload form state between browser sessions. Figure 1 shows the UI with a mock form.

Figure 1. Recipe 3's test application UI

The Silverlight application shown in Figure 1 has a TextBox on the left with "Hi There Book Reader!" as a value. Any value entered in this TextBox is stored in the IsolatedStorageSettings dictionary object, which is a convenient place to store name/value pair settings or data. The UserControl.Loaded event handler pulls this setting out of the collection, and the ButtonUpdateSetting event handler stores the setting when the Button titled Update Setting is clicked.

The Save Form Data button and the Load Form Data button both work with the sample form fields located in the rounded green/silver area on the right side of the application. The Save Form Data button concatenates the text from the form data into a string, with each value separated by the pipe (|) symbol. The Load Form Data button reads in the data as a String and calls String.Split to separate the fields into an array of string values.

The SaveFormData_Click event stores the form data into isolated storage. In general, any data that is persisted into IsolatedStorage is persisted between browser sessions. The ReadFormData_Click event retrieves the data from the file created in isolated storage. Listings 1 and 2 show the code for the Silverlight application's MainPage class.

Listing 1. Recipe 3's MainPage.xaml File
<UserControl x:Class="Ch02_ProgrammingModel.Recipe2_3.MainPage"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
d:DesignHeight="300" d:DesignWidth="400">
<Grid x:Name="LayoutRoot" Background="#FFFFFFFF">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.06*"/>
<ColumnDefinition Width="0.455*"/>
<ColumnDefinition Width="0.485*"/>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="0.08*"/>
<RowDefinition Height="0.217*"/>
<RowDefinition Height="0.61*"/>
<RowDefinition Height="0.093*"/>
</Grid.RowDefinitions>
<Button HorizontalAlignment="Stretch" Margin="8 " VerticalAlignment="Stretch"
Grid.Column="1" Grid.Row="1" Content="Save Form Data"
Click="SaveFormData_Click"/>
<StackPanel HorizontalAlignment="Stretch" Margin="8,8,10,8" Grid.Column="1"
Grid.Row="2">
<TextBlock Height="Auto" Width="Auto" Text="Enter Setting Value"
TextWrapping="Wrap" Margin="4,4,4,4"/>
<TextBox Height="126" Width="Auto" Text="" TextWrapping="Wrap"
Margin="4,4,4,4" x:Name="settingTextData"/>
</StackPanel>
<Button HorizontalAlignment="Stretch" Margin="8" VerticalAlignment="Stretch"
Grid.Column="2" Grid.Row="1" Content="Load Form Data"
Click="ReadFormData_Click"/>
<Button HorizontalAlignment="Stretch" Margin="4,4,14,4"
VerticalAlignment="Stretch"
Grid.Column="1" Grid.Row="3" Content="Update Setting"
Click="ButtonUpdateSetting"/>

<Border Grid.Column="2" Grid.Row="2" Grid.RowSpan="2"
CornerRadius="10,10,10,10">
<Border.Background>
<LinearGradientBrush EndPoint="0.560000002384186,0.00300000002607703"
StartPoint="0.439999997615814,0.996999979019165">
<GradientStop Color="#FF586C57"/>
<GradientStop Color="#FFA3BDA3" Offset="0.536"/>
<GradientStop Color="#FF586C57" Offset="0.968999981880188"/>
</LinearGradientBrush>
</Border.Background>
<StackPanel Margin="4,4,4,4" x:Name="FormData">
<TextBlock Height="Auto" Width="Auto" Text="First Name:"
TextWrapping="Wrap" Margin="2,2,2,0"/>
<TextBox Height="Auto" Width="Auto" Text="" TextWrapping="Wrap" x:
Name="Field1" Margin="2,0,2,4"/>
<TextBlock Height="Auto" Width="Auto" Text="Last Name:"
TextWrapping="Wrap" Margin="2,4,2,0"/>
<TextBox Height="Auto" x:Name="Field2" Width="Auto" Text=""
TextWrapping="Wrap" Margin="2,0,2,4"/>
<TextBlock Height="Auto" Width="Auto" Text="Company:"
TextWrapping="Wrap" Margin="2,4,2,0"/>
<TextBox Height="Auto" x:Name="Field3" Width="Auto" Text=""
TextWrapping="Wrap" Margin="2,0,2,2"/>
<TextBlock Height="22.537" Width="182" Text="Title:"
TextWrapping="Wrap" Margin="2,4,2,0"/>
<TextBox Height="20.772" x:Name="Field4" Width="182" Text=""
TextWrapping="Wrap" Margin="2,0,2,2"/>
</StackPanel>
</Border>
</Grid>
</UserControl>
Listing 2 has the codebehind page for MainPage.xaml where the events are located. The code declares several class level variables such as settings that are used by the event handlers to load and save setting values to IsolatedStorage.
Listing 2. Recipe 3's MainPage.xaml.cs Class File
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.IO;
using System.IO.IsolatedStorage;
using System.Text;

namespace Ch02_ProgrammingModel.Recipe2_3
{
  public partial class MainPage : UserControl
{
private IsolatedStorageSettings settings =
IsolatedStorageSettings.ApplicationSettings;
private string setting = "MySettings";
private string FormDataFileName = "FormFields.data";
private string FormDataDirectory = "FormData";
public MainPage()
{
InitializeComponent();
}

private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
try
{
if (settings.Keys.Count != 0)
{
settingTextData.Text = settings[setting].ToString();
}
}
catch (IsolatedStorageException ex)
{
settingTextData.Text = "Error saving setting: " + ex.Message;
}
}

private void SaveFormData_Click(object sender, RoutedEventArgs e)
{
try
{
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
{
//Use to control loop for finding correct number of textboxes
int TotalFields = 4;
StringBuilder formData = new StringBuilder(50);
for (int i = 1; i <= TotalFields; i++)
{
TextBox tb = FindName("Field" + i.ToString()) as TextBox;
if (tb != null)
formData.Append(tb.Text);
//If on last TextBox value, don't add "|" character to end of data
if (i != TotalFields)
formData.Append("|");

}
store.CreateDirectory(FormDataDirectory);
IsolatedStorageFileStream fileHandle =
store.CreateFile(System.IO.Path.Combine(
FormDataDirectory, FormDataFileName));
using (StreamWriter sw = new StreamWriter(fileHandle))
{
sw.WriteLine(formData);
sw.Flush();
sw.Close();
}
}
}
catch (IsolatedStorageException ex)
{
settingTextData.Text = "Error saving data: " + ex.Message;
}
}

private void ReadFormData_Click(object sender, RoutedEventArgs e)
{
using (var store = IsolatedStorageFile.GetUserStoreForApplication())
{
//Load form data using private string values for directory and filename
string filePath =
System.IO.Path.Combine(FormDataDirectory, FormDataFileName);
//Check to see if file exists before proceeding
if (store.FileExists(filePath))
{
using (StreamReader sr = new StreamReader(
store.OpenFile(filePath, FileMode.Open, FileAccess.Read)))
{
string formData = sr.ReadLine();
//Split string based on separator used in SaveFormData method
string[] fieldValues = formData.Split('|');
for (int i = 1; i <= fieldValues.Count(); i++)
{
//Use the FindName method to loop through TextBoxes
TextBox tb = FindName("Field" + i.ToString()) as TextBox;
if (tb != null)
tb.Text = fieldValues[i − 1];
}
sr.Close();
}
}

}
}
private void ButtonUpdateSetting(object sender, RoutedEventArgs e)
{
try
{
settings[setting] = settingTextData.Text;
}
catch (IsolatedStorageException ex)
{
settingTextData.Text = "Error reading setting: " + ex.Message;
}
}
}
}

Other  
 
Top 10
Motorola RAZR MAXX - “Being The World’s Lightest”
Nokia 808 Pureview
Nokia Lumia 610 - Shines Out Of Low Segment
Nokia Lumia 900 - The Brightest Star In Lumia Series
One More Thing: Two New Ipods
Sony Xperia P Review (Part 2)
Sony Xperia P Review (Part 1)
Onyx Calypso 9.7 Tablet
Aesthetix Calypso Light Preamplifier Review
Worthy Audio Block C-100 For Mid-Class CD Player
Most View
Hack Your Phone (Part 3)
Dream Machine 2012 - The Future Is Now (Part 3)
New Rumors Emerge Related To iOS 6
Installing the HP-UX 11i Operating Environment (part 2) - Loading HP-UX
Multifaceted Tests : Making HTTP Requests Using XSS & Attempting DOM-Based XSS Interactively
Tracking Results and Measuring Success : Tying SEO to Conversion and ROI
Programmatic Security (part 4) - Permission Set Classes
Exchange Server 2007 : Configure the Client Access Server - Manage Exchange ActiveSync
Brother DCP-J140W
Protecting SharePoint with Advanced Antivirus and Edge Security Solutions : Securing SharePoint Sites Using Forefront UAG
Handling User Interaction and Events in XAML
Quad Gods - The Mobile Chip Race Is Beginning
Graphic Design – The Worship Of Icons
Programming .NET Security : Programming Digital Signatures (part 3) - Using the Signature Formatter Classes
Microsoft SQL Server 2005 : Report Definition and Design (part 4)
Managing Local User Accounts and Groups in Vista
Google, privacy & you (Part 2)
Windows Vista : Trim the Fat (part 2) - Start Windows in Less Time
Default Security Policy
External Drive Western Digital My Book Thunderbolt Duo