March 2008 - Important Note
This tutorial is no longer valid. It was written for Microsoft Silverlight 1.1 Alpha Refresh, which no longer exists - it has been replaced by Silverlight 2. We have left the page up for historical purposes (and because there are incoming links). Once again, the information below is invalid and in some cases misleading for Silverlight 2.
So, a little while back, we posted a little rant about how isolated storage in Silverlight is a great idea, but how its usefulness is extremely limited. And while that still is true, that doesn't stop the concept from being cool. So today we are going to take a look at how to write a Silverlight app that uses isolated storage.
The application we are going to write is kind of simple (and really pretty pointless). As you can see below, there is a load file button. When you click that button, a standard open file dialog will come up, where you get to choose a file (hopefully a text file). When you click open, the contents of that file will be copied into isolated storage, and then displayed on the screen. The somewhat interesting part comes in with the fact that you can reload the page, or leave the site and come back, and when this app loads again, it will display the contents of that file you opened (because it read them back out from isolated storage).
Edit: Example Obsoleted And Removed
A pretty simple little app, but it actually exposes a number of pieces of Silverlight functionality. So lets take a look at the code and see what is up. First, we have the extremely simple xaml file:
xmlns="http://schemas.microsoft.com/client/2007"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="IsolatedStorageDemo.Page;assembly=IsolatedStorageDemo.dll"
xmlns:controls="clr-namespace:SOTCSilverlightControls;
assembly=SOTCSilverlightControls.dll"
Width="520" Height="400" Background="White">
<controls:Button x:Name="loadFileButton" Canvas.Top="10"
Canvas.Left="10" Text="Load File" />
<TextBlock x:Name="nameArea" Canvas.Top="10" Canvas.Left="110"
Width="400" Height="30" />
<TextBlock x:Name="textArea" Canvas.Top="40" Canvas.Left="10"
Width="500" Height="300" />
</Canvas>
The three components here are a button and two text blocks. The button is actually a custom control - you can learn all about it in this tutorial. The first text block is where we will put the name of the loaded file, and the second is where the contents of the file will be displayed.
Now, on to some code behind:
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Title = "Select Text File";
ofd.EnableMultipleSelection = false;
if (ofd.ShowDialog() != DialogResult.OK)
{
ofd.Dispose();
return;
}
Stream stream = ofd.SelectedFile.OpenRead();
IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication();
string[] names = isf.GetFileNames("*.dat");
if (names.Length != 0)
foreach (string name in names)
isf.DeleteFile(name);
IsolatedStorageFileStream isfs = new IsolatedStorageFileStream(
ofd.SelectedFile.Name + ".dat", FileMode.CreateNew, isf);
while (stream.Position < stream.Length)
isfs.WriteByte((byte)stream.ReadByte());
isfs.Close();
stream.Close();
isf.Close();
ofd.Dispose();
isfs.Dispose();
stream.Dispose();
isf.Dispose();
LoadFromIsolatedStorage();
}
This function is attached to the click event of the load button. It is not a whole lot of code, but there are a bunch of new things going on, so we are going to walk through the whole thing.
The first thing we do is create an OpenFileDialog. This behaves almost identical to the regular one in .NET, with many of the same properties and options. We set it up and show it, and if the user cancels out of it, we dispose the dialog and return out. Otherwise, we open the selected file. This is one of the differences between the standard .NET open file dialog and this one - instead of giving back a path and allowing you to open the file on your own, the silverlight open file dialog hands back a FileDialogFileInfo. From this, you can get the name of the file, and you can get a read only steam of the file contents - but you never need actual direct access to the filesystem (which is not allowed).
The next step is to open the Isolated Storage area for this Silverlight app - which is unique to this Silverlight app on this page. Other Silverlight application on this page or on other pages won't even know that this area exists - a Silverlight application can only see its own Isolated Storage area. To do this, we call the static method GetUserStoreForApplication on the IsolatedStorageFile class. Now that we have the isolated area, we can do things like get the files contained in it (which is what we do next). For this app, we store the contents to be displayed in a file with a ".dat" extension, so as we are loading a new file, we want to delete the old one. So we search for files in the isolated area that match "*.dat", and if there are any, we delete them.
Now we want to create the new file, which will hold the contents of the file the user just selected. We do this by creating a new IsolatedStorageFileStream, with the new file name and FileMode.CreateNew. We also need to pass in the isolated storage area for the current app - the result from that earlier call to GetUserStoreForApplication. With this stream in our hand that we can write to, we can now copy the user selected file into this new file. Just as a note, don't do a byte by byte copy like I did here if you ever do something like this - I was just being lazy and didn't want to create a buffer :P
The file that we just created here actually does exist on the users filesystem, and in fact you can go look at it and modify it if you want. It is located deep within your user profile Application Data folder - on my computer, it ended up in the folder "\Application Data\IsolatedStorage\vctjiwbu.5ac\sqasakyu.yci\ Url.erznbhqlu5xyrsy5yqmr514iseu2ktq4\AppFiles". Quite a mouthful - and definitely unique to each Silverlight application. Another little note here - Isolated Storage for a single Silverlight application is currently limited to 1MB, with no way to change that value.
Ok, back to the code. So we copied the file, now what? Well, we want to close down and dispose everything, in order to be nice. And really, in real code, you would probably want to surround all the open / copy / close operations with some try/catch code - but again, I was just playing around. Finally, we call a method LoadFromIsolatedStorage. What is this method? Well, lets take a look:
{
IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication();
string[] names = isf.GetFileNames("*.dat");
if (names.Length == 0)
{
isf.Close();
isf.Dispose();
return;
}
IsolatedStorageFileStream isfs =
new IsolatedStorageFileStream(names[0], FileMode.Open, isf);
StreamReader sr = new StreamReader(isfs);
nameArea.Text = names[0].Remove(names[0].Length-4, 4);
textArea.Text = "";
string s;
while ((s = sr.ReadLine()) != null)
textArea.Text += s + "\n";
sr.Close();
isf.Close();
isfs.Close();
sr.Dispose();
isfs.Dispose();
isf.Dispose();
}
This function is the counterpart to the previous function - it does the loading from and read of isolated storage. Very similar to the previous function, we call GetUserStoreForApplication and file files that match "*.dat". If there are no files that match, then there is nothing to display, so we close and dispose the IsolatedStorageFile and return. Otherwise we open the first one by creating a new IsolatedStorageFileStream with the first file name from the array of found files (and really, there should only ever be one because of what we do in the previous method). We then make a standard StreamReader from the IsolatedStorageFileStream, because later on we will be using the ever-so-handy ReadLine function. Next, we set the contents of the nameArea text block to the name of the file (which we get by stripping the ".dat" off of the file name), and we clear the contents of the textArea text block.
Now its time to fill the textArea with the file contents, and we do that with a simple while loop, where we read lines until there are none left, all the while adding them on as new lines on the textArea. And once all that is done, we close and dispose everything, cleaning up after ourselves.
As a side note, the text block component does not seem to have any built in scrolling capability (either horizontally or vertically), so text that overruns the area of the text block just gets cut off. Silverlight really needs a much better control library in the next version.
There is only a little bit of code left, and that is to make the Silverlight app load and display the contents of the last loaded file when the user comes back to the page. This is really simple with the function we just wrote - all we need is to add a call to it in the Page_Loaded function:
{
// Required to initialize variables
InitializeComponent();
loadFileButton.Click += new EventHandler(loadFileButton_Click);
LoadFromIsolatedStorage();
}
Oh, and we can't forget that in that function is where we attach the loadFileButton_Click function to the click event on loadFileButton.
And that is all there is to it! A final note - all the Isolated Storage objects are under the System.IO.IsolatedStorage namespace - so don't forget to add that as a using statement when working with this stuff. Here is the full content of the code behind file, so you can see it all together:
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.IO;
using System.IO.IsolatedStorage;
namespace IsolatedStorageDemo
{
public partial class Page : Canvas
{
public void Page_Loaded(object o, EventArgs e)
{
// Required to initialize variables
InitializeComponent();
loadFileButton.Click += new EventHandler(loadFileButton_Click);
LoadFromIsolatedStorage();
}
private void LoadFromIsolatedStorage()
{
IsolatedStorageFile isf = IsolatedStorageFile.GetUserStoreForApplication();
string[] names = isf.GetFileNames("*.dat");
if (names.Length == 0)
{
isf.Close();
isf.Dispose();
return;
}
IsolatedStorageFileStream isfs =
new IsolatedStorageFileStream(names[0], FileMode.Open, isf);
StreamReader sr = new StreamReader(isfs);
nameArea.Text = names[0].Remove(names[0].Length-4, 4);
textArea.Text = "";
string s;
while ((s = sr.ReadLine()) != null)
textArea.Text += s + "\n";
sr.Close();
isf.Close();
isfs.Close();
sr.Dispose();
isfs.Dispose();
isf.Dispose();
}
private void loadFileButton_Click(object sender, EventArgs e)
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Title = "Select Text File";
ofd.EnableMultipleSelection = false;
if (ofd.ShowDialog() != DialogResult.OK)
{
ofd.Dispose();
return;
}
Stream stream = ofd.SelectedFile.OpenRead();
IsolatedStorageFile isf =
IsolatedStorageFile.GetUserStoreForApplication();
string[] names = isf.GetFileNames("*.dat");
if (names.Length != 0)
foreach (string name in names)
isf.DeleteFile(name);
IsolatedStorageFileStream isfs = new IsolatedStorageFileStream(
ofd.SelectedFile.Name + ".dat", FileMode.CreateNew, isf);
while (stream.Position < stream.Length)
isfs.WriteByte((byte)stream.ReadByte());
isfs.Close();
stream.Close();
isf.Close();
ofd.Dispose();
isfs.Dispose();
stream.Dispose();
isf.Dispose();
LoadFromIsolatedStorage();
}
}
}
Hope you enjoyed this introduction to using isolated storage in Silverlight. If you have any questions, comments, or questioning comments, feel free to leave them below. Also, if anyone has come up with cool uses for isolated storage, we would love if you would share your ideas.
11/22/2007 - 03:11
Very thanks.
12/26/2007 - 02:43
I wanted to code the image preview feature with Javascript long time back. Link. but it doesn't work for security reason. (You can read our discussion here. ) I don't know what the security reason is. Now, It's possible to do that in Silverlight, right?
12/26/2007 - 09:26
It is actually not possible at the moment, and that topic is actually the subject of a small rant here.
12/28/2007 - 01:53
Hi,
How to set the image source from stream? I tried with this code but it doesn't work.
1. Getting the image stream from FileDialog and set it to image
if ( myCanvas != null )
{
Image img = new Image();
img.SetValue(Image.SourceProperty, stream);
img.Height = 10;
///I'm getting the error here.
myCanvas.Children.Add(img);
}
2. Getting the image from IS and set it to image
IsolatedStorageFile.GetUserStoreForApplication();
IsolatedStorageFileStream isfs =
new IsolatedStorageFileStream(imageName,
FileMode.Open, isf);
StreamReader sr = new StreamReader(isfs);
if ( myCanvas != null )
{
Image img = new Image();
img.SetValue(Image.SourceProperty, sr);
myCanvas.Children.Add(img);
}
Both codes don't work. Can you please help me?
03/15/2008 - 22:15
As noted at the top, this tutorial is no longer valid and only listed here for historical purposes. Because of this, comments are now closed for this post.