Sunday, May 2, 2010

Silverlight Out of Browser (OOB) Versions, Images, and Isolated Storage

This is a quick and simple post to address three very common questions I receive about Silverlight Out-of-Browser (OOB) applications. In case you haven't heard, applications made with Silverlight version 3 and later can be installed locally to your machine (whether it is a Windows machine or a Mac) and run "out of the browser" as a local application. In Silverlight 4, you can also run with partial trust. That is not the focus on this post.

Creating an out of browser application is quite easy. Open the project properties for your Silverlight application. You will find a checkbox that allows for installation when out of browser:

Silverlight Out of Browser

Simply check the box and you are on your way! Of course, you can also click the button to set other properties, such as the name and description for the application, provide icons for installation, and decide whether you want it to run with elevated trust.

Question One: How do I publish new versions of my Silverlight Out-of-Browser Application?

This is easy. Silverlight does not do version checking by itself. To Silverlight, your "version" is basically the Uri of the XAP file from which it was originally downloaded. If you publish a new XAP file for the web version, when the user revisits, it automatically pulls down the newest version (sometimes this doesn't happen immediately because the XAP file may be cached by the browser, similar to images and other resources). The out of browser application is the same: when the application starts, it automatically polls the server. If a new XAP file is detected, the bits are downloaded and the application is updated.

Of course, this sort of "forces" the update out of the box. Jeff Wilcox's blog has an excellent article about Out of Browser that demonstrates how to handle the update experience programmatically by prompting the user.

Question Two: Do I need to embed my images for out-of-browser to work?

The answer is "no." Out-of-browser works very similar to "in the browser." Images can fatten XAP files so most people place the images in the ClientBin folder where the XAP file resides. This allows you to reference the image with a relative path:

...
<image source="foo.jpg"/>
...

If foo.jpg is located in the root of ClientBin on the server, it will load just fine. But what about with OOB? The same thing happens. The installed application retains a pointer to the Uri where it was installed from (this is how it can check for new versions). When a relative image reference is found, it will simply download it relative to the XAP file directory, the same as the online application. Therefore, you do not have to package the images in the XAP for them to become available.

But what about when the application is offline?

This is tricky territory. The word on the street is that it will still use your browser's cache to retrieve the images. This makes it very unpredictable, because you never know when the cache will clear and in my tests, you'll often end up with an application that has no images. If you are writing an application that spends a good deal of time in offline mode, you will probably want to embed your images in the XAP to make sure they are available all of the time.

Question Three: Do the web and desktop versions of my application share the same isolated storage?

Yes, they do.

This one may seem confusing because the default size of isolated storage for a web application is 1 megabyte (MB) while the default for out-of-browser applications is 25 megabytes. However, despite the difference in quota, both versions point to the same isolated storage location.

The Example

To illustrate the image and isolated storage points, I made a very simple out of browser application that you can play with here. It contains an image file that is external to the XAP, so you can see how it is fetched the same way both in and out of browser. It also automatically detects the mode it is running in. In the web browser mode, you can enter text and click a "serialize" button to write that text to isolated storage. In the OOB mode, it has a "deserialize" button that reads from isolated storage and writes into the text box. You can run the web and out of browser versions at the same time, serializing data from the web version and immediately deserializing it from the OOB version to see how they share the same isolated storage.

To install the application to your desktop, simply right-click and select the option to install:

Installing an OOB application

The code for this is simple. Note that I did not do any error checking, so you'll want to serialize some text before attempting to deserialize it. This is the code behind for the application:

public partial class MainPage
{
    private const string FILE = "testserialization.txt";
        
    public MainPage()
    {
        InitializeComponent();
        Application.Current.InstallStateChanged += Current_InstallStateChanged;
        ISOButton.Content = Application.Current.IsRunningOutOfBrowser ? "Deserialize" : "Serialize";
    }

    void Current_InstallStateChanged(object sender, System.EventArgs e)
    {
        ISOButton.Content = Application.Current.IsRunningOutOfBrowser ? "Deserialize" : "Serialize";
    }

    private void IsoButtonClick(object sender, RoutedEventArgs e)
    {
        if (Application.Current.IsRunningOutOfBrowser)
        {
            using (var iso = IsolatedStorageFile.GetUserStoreForApplication())
            {
                if (iso.FileExists(FILE))
                {
                    using (var br = new BinaryReader(iso.OpenFile(FILE, FileMode.Open, FileAccess.Read)))
                    {
                        Text.Text = br.ReadString();
                    }
                }
            }
        }
        else
        {
            using (var iso = IsolatedStorageFile.GetUserStoreForApplication())
            {
                using (var bw = new BinaryWriter(iso.OpenFile(FILE, FileMode.Create, FileAccess.Write)))
                {
                    bw.Write(Text.Text);
                }
            }
            Text.Text = "--- wrote to iso ---";
        }
    }
}

Notice that the content and behavior of the button changes based on whether or not the application is running out of browser (OOB). It also hooks into the InstallStateChanged event to update "on the fly" when the user installs.

Hopefully this short post demonstrates some useful concepts about running your applications out of browser. You can download the source for the example here.

Jeremy Likness