Welcome to Pete Brown's 10rem.net

First time here? If you are a developer or are interested in Microsoft tools and technology, please consider subscribing to the latest posts.

You may also be interested in my blog archives, the articles section, or some of my lab projects such as the C64 emulator written in Silverlight.

(hide this)

State Management and Isolated Storage in Silverlight 2 Beta 1

Pete Brown - 22 March 2008

Some of the questions that came out of the Silverlight for Business Applications webcast I did on Wednesday centered around how to handle "global variables" and how to handle state. I'll address both of those here as they are very common requirements in business applications.

When I built Windows client applications (which doesn't happen that much any more, much to my disappointment!), I would often include a singleton class to handle transaction state. By the nature of a singleton class, it was able to be accessed from all the forms and controls in the project, and I didn't have to mess around with passing in instances of individual properties. This pattern may or may not apply in your particular situation, but I have found it quite helpful and easy to understand. I also followed a similar model in ASP.NET, but had the state class act simply as a strongly-typed facade to HttpContext.Current.Session.

[ You can download the source code for this posting from here. ]

Stateful Client

In a typical application, you'll have a number of classes used to capture transactional data that is entered into forms or otherwise set by the user. This can include straight data-entry information of course, but it can also keep things like which logical page a user is on, whether they have visited certain key areas, etc.

In the case of this sample, we're going to keep track of some contact information the user has entered in the form, so we have a Contact class:

public class Contact : INotifyPropertyChanged
{
    private string _lastName;
    private string _firstName;
    private DateTime _dateOfBirth;
    private Address _address = new Address();

    public event PropertyChangedEventHandler PropertyChanged;
    
    public string LastName
    {
        get { return _lastName; }
        set { _lastName = value; NotifyPropertyChanged("LastName"); }
    }

    public string FirstName
    {
        get { return _firstName; }
        set { _firstName = value; NotifyPropertyChanged("FirstName"); }
    }

    public DateTime DateOfBirth
    {
        get { return _dateOfBirth; }
        set { _dateOfBirth = value; NotifyPropertyChanged("DateOfBirth"); }
    }

    public Address Address
    {
        get { return _address; }
        set { _address = value; }
    }


    private void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

You may wonder why Address has a "Set" mutator in there. That's for serialization/deserialization which we'll use when we back our state up. Normally I wouldn't include the "set" in there.

Here's the Address class

public class Address : INotifyPropertyChanged
{
    private string _address1;
    private string _address2;
    private string _city;
    private string _state;
    private string _zip;

    public event PropertyChangedEventHandler PropertyChanged;


    public string Address1
    {
        get { return _address1; }
        set { _address1 = value; NotifyPropertyChanged("Address1"); }
    }

    public string Address2
    {
        get { return _address2; }
        set { _address2 = value; NotifyPropertyChanged("Address2"); }
    }

    public string City
    {
        get { return _city; }
        set { _city = value; NotifyPropertyChanged("City"); }
    }

    public string State
    {
        get { return _state; }
        set { _state = value; NotifyPropertyChanged("State"); }
    }

    public string Zip
    {
        get { return _zip; }
        set { _zip = value; NotifyPropertyChanged("Zip"); }
    }


    private void NotifyPropertyChanged(string propertyName)
    {
        if (PropertyChanged != null)
        {
            PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
        }
    }
}

Now that we have classes to capture the data entered into the user interface, we can easily keep local instances in the form and manipulate them as-needed. However, often times one needs to share the data between forms/pages. There are a variety of ways to do that, but the cleanest way I've found is to use a singleton State (or StateManager, if you prefer) class which houses all of the state data on the client.

Singleton Pattern Application State Management Class

To make state accessible to all the pages and logical forms in my Silverlight application, I implemented state as a singleton class with strongly-typed properties :

public sealed class ApplicationState
{
    #region Singleton Behavior

    // this variable and property implement the singleton
    // pattern for this class. The reason we don't just 
    // use a big old static class for everything is that
    // certain things (like binding and referencing) tend 
    // to work better with instance classes. It also makes
    // the class easier to develop
    private static ApplicationState _current;

    public static ApplicationState Current
    {
        get
        {
            if (_current == null)
                _current = new ApplicationState();

            return _current;
        }
        internal set
        {
            _current = value;
        }
    }

    #endregion

    // the variable(s) below represent the state for the application
    // you can include model information here, state like what
    // screen the user is on etc. Keep anything that you would want
    // to retain between sessions, or if the user accidentally closes
    // their browser or gets a popup blocker-related page reload

    private Contact _currentContact = new Contact();

    public Contact Contact
    {
        get { return _currentContact; }
        set
        {
            lock (_currentContact)
            {
                _currentContact = value;
            }
        }
    }
}

Once you do that, the way you access state information is by using a construct like this:

ApplicationState.Current.Contact.FirstName = "Pete";

Data Binding and the User Interface

The Xaml for a quick and dirty test UI follows. Notice that I used two-way data binding to bind the textboxes to the values stored in state. Notice also that for the Address properties, you can dot-down in your binding path to get to things like Address.State.

<UserControl x:Class="PeteBrown.SilverlightStateExample.Page"
    xmlns="http://schemas.microsoft.com/client/2007" 
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
    Width="400" Height="300">
    <Grid x:Name="LayoutRoot" Background="White">
        <Grid.RowDefinitions>
            <RowDefinition Height="33" />
            <RowDefinition Height="33" />
            <RowDefinition Height="33" />
            <RowDefinition Height="33" />
            <RowDefinition Height="33" />
            <RowDefinition Height="33" />
            <RowDefinition Height="33" />
            <RowDefinition Height="33" />
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="100" />
            <ColumnDefinition Width="*" />
        </Grid.ColumnDefinitions>

        <WatermarkedTextBox x:Name="FirstName" Watermark="First Name" 
                            Grid.Row="0" Grid.Column="1" Margin="4" 
                            Text="{Binding FirstName, Mode=TwoWay}"/>
        <WatermarkedTextBox x:Name="LastName" Watermark="Last Name" 
                            Grid.Row="1"  Grid.Column="1"  Margin="4" 
                            Text="{Binding LastName, Mode=TwoWay}" />
        <WatermarkedTextBox x:Name="Address1" Watermark="Address 1" 
                            Grid.Row="2"  Grid.Column="1" Margin="4" 
                            Text="{Binding Address.Address1, Mode=TwoWay}"  />
        <WatermarkedTextBox x:Name="Address2" Watermark="Address 2" 
                            Grid.Row="3"  Grid.Column="1" Margin="4" 
                            Text="{Binding Address.Address2, Mode=TwoWay}"  />
        <WatermarkedTextBox x:Name="City" Watermark="City" 
                            Grid.Row="4"  Grid.Column="1" Margin="4" 
                            Text="{Binding Address.City, Mode=TwoWay}"  />
        <WatermarkedTextBox x:Name="State" Watermark="State" 
                            Grid.Row="5"  Grid.Column="1"  
                            Margin="4" Text="{Binding Address.State, Mode=TwoWay}" />
        <WatermarkedTextBox x:Name="Zip" Watermark="Zip" 
                            Grid.Row="6"  Grid.Column="1" 
                            Margin="4" Text="{Binding Address.Zip, Mode=TwoWay}"  />
        
        <Button x:Name="Submit" Content="Test Iso Save" 
                Grid.Column="1" Grid.Row="7" HorizontalAlignment="Right" 
                Width="100" Margin="4" />
    </Grid>
</UserControl>

The user interface looks like this:

image

The Page constructor is where the DataContext is set, telling the Grid that it, and all its contained children, should use our State's "Contact" property for data binding.

public Page()
{
    InitializeComponent();

    LayoutRoot.DataContext = ApplicationState.Current.Contact;

    Submit.Click += new RoutedEventHandler(Submit_Click);

}

State Backup Using Isolated Storage

Once we have our state in a set of discrete objects, it's easy to add a method to back it up to Isolated Storage. In your State.cs file, simply add methods to save everything off to isolated storage. Now, if you want to be more selective about what you back up, you can send individual classes out to specific application settings. In this case, I just assume that if it's in state, it's worth backing up.

I posted about the scenarios where this could be useful previously. Below is a concrete implementation of those ideas.

For simplicity, I use the ApplicationSettings class. That class is a dictionary of items that are to be stored in Isolated Storage. It loads from Isolated Storage the first time you access an item in your application. It saves to isolated storage automatically when your app shuts down (although this isn't working in Beta 1, it should be fixed in Beta 2, so call the save explicitly right now).

private const string _stateKey = "state";

public static void SaveToIsolatedStorage()
{
    ApplicationSettings.Default[_stateKey] = State.Current;
    
    // Save is available and required in Beta 1. In later version 
    // save will likely happen automatically when the app closes. 

    ApplicationSettings.Default.Save();

}

public static void LoadFromIsolatedStorage()
{
    // ApplicationSettings load from isostore on first access

    if (ApplicationSettings.Default.Contains(_stateKey))
    {
        State.Current = (State)ApplicationSettings.Default[_stateKey];
    }
}

You can hook the Save method up to waypoints in your application, or perhaps to a timer. I call the Load method from the main page's constructor, just before binding. In a real application, you'll likely want to call it from your application load event before you set your root element.

public Page()
{
    InitializeComponent();

    ApplicationState.LoadFromIsolatedStorage();

    LayoutRoot.DataContext = ApplicationState.Current.Contact;

    Submit.Click += new RoutedEventHandler(Submit_Click);

}
Serialization

Thanks to Wilco Bauwer for helping me figure out the Serialization options here.

It turns out that the ApplicationSettings class does a great job of handling serialization for you. Behind the scenes, it uses the DataContractSerializer to serialize your classes to XML before saving them to IsolatedStorage. You don't need to markup your class or add any attributes to take advantage of this. Nice!

Where and What is Isolated Storage Anyway?

I poked around on my machine and found the files that make up Isolated Storage for Silverlight 2. The location is broken up into two separate sections. Section 1 contains administrative information, such as the quota. The second section contains the application-specific data.

On my machine, the administrative data was located in:

C:\Users\Peter.Brown\AppData\LocalLow\Microsoft\Silverlight\is\ydypht14.mut\2nu3z4zb.azc\g\h5xwynlgtxurhuhejunkcjndlnyyzy5e

image

If I crack open id.dat, I see the URL for my demo site: HTTP://LOCALHOST:60515

lock.dat is presumably a semaphore, and quota and used contain binary information about spaces allocated and used for this site.

The second part is the storage of application data for the applications themselves. On my machine, the Isolated Storage folder for this application is located here:

C:\Users\Peter.Brown\AppData\LocalLow\Microsoft\Silverlight\is\ydypht14.mut\2nu3z4zb.azc\s\irbvnou0zoh1y3e0iwpepmv1voh5441w\f

In that folder, I found a file called "_LocalSettings"

If you crack open that _LocalSettings file, you'll find this (formatting / line breaks added by me for this posting)

PeteBrown.SilverlightStateExample.Model.ApplicationState, 
    PeteBrown.SilverlightStateExample, Version=1.0.0.0, 
    Culture=neutral, PublicKeyToken=null
<ArrayOfKeyValueOfstringanyType xmlns:i="http://www.w3.org/2001/XMLSchema-instance" 
                                xmlns="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
  <KeyValueOfstringanyType>
    <Key>state</Key>
    <Value xmlns:d3p1="http://schemas.datacontract.org/2004/07/PeteBrown.SilverlightStateExample.Model" 
           i:type="d3p1:ApplicationState">
      <d3p1:Contact>
        <d3p1:Address>
          <d3p1:Address1>200 Foo St</d3p1:Address1>
          <d3p1:Address2>Cell 25</d3p1:Address2>
          <d3p1:City>Smallsville</d3p1:City>
          <d3p1:State>MD</d3p1:State>
          <d3p1:Zip>20001</d3p1:Zip>
        </d3p1:Address>
        <d3p1:DateOfBirth>0001-01-01T00:00:00</d3p1:DateOfBirth>
        <d3p1:FirstName>Pete</d3p1:FirstName>
        <d3p1:LastName>Brown</d3p1:LastName>
      </d3p1:Contact>
    </Value>
  </KeyValueOfstringanyType>
</ArrayOfKeyValueOfstringanyType>

As you can see, that is the result of serializing the classes out using the default behavior of the ApplicationSettings class.

Additional Benefits

Once you start centralizing your state management in your applications, you'll be able to extend the functionality to handle things like undo and automated submit to the server, should you so desire.

I invite you to take the ideas presented here and use them in whatever way best suits the needs of your specific application. If you run into an interesting scenario or use, please post it here.

       
posted by Pete Brown on Saturday, March 22, 2008
filed under:        

10 comments for “State Management and Isolated Storage in Silverlight 2 Beta 1”

  1. Ben Hayatsays:
    Pete, as I told you I couldn't attend the webcast, I'm back now (I think in one piece), but I can't find a link to the webcast. Can you help me on this. Now I need to go read your article...
    ..Ben
  2. Eduardo Silvasays:
    Hi Pete,
    Great post! I've started out from your code and I've managed to create a class that stores all the state information I need for several issues.
    I was also considering using a similar approach for some WPF Apps I previously developed. On WPF the ApplicationSettings Class doesn't exist. What do you suggest? What is the best way to overcome that?
    Thanks again
  3. Mark Rsays:
    If I'm understanding this right.

    We could use this to make anchor (history) points for our silverlight app states.

    Therefore, we can capture the browser buttons (back, forward) to be able to nav to previous states of the SL app, right?
  4. Pete Brownsays:
    @MarkR

    Sure, if you can create a data structure to hold that information. However, back and forward buttons typically don't work like "undo", so you wouldn't really want to push *entered data* onto a back/forward stack, just location info. You can use it to store where the user went.

    There are other ways to approach that specific problem, though. Capture the back button and then tell the SL app to nav back (via js if necessary). All that information could be in memory as opposed to backing it up to isolated storage.

    HTH

    Pete
  5. Pete Brownsays:
    @Eduardo

    You'll just need to wrap the isolated storage API in WPF. I haven't looked at it myself, but it shouldn't be hard to use the xml serializer (SL uses the DataContractSerializer) to dehydrate/hydrate objects.

    Pete
  6. Edimarsays:
    Hi Tony, That is a great post!Hi,I have a silverlight usnrocetrol which displays a viewmodel on a grid from a source list which contain two coulmns in that list (Item(string), Checked(bool))This checked is binding to a image, which turns into green colored image when I click on it and store the vaue as 1 in the list and to red, value to 0 , when I click again. Now When the value is changed in the list, I want to reflect on the grid also at the same time. that means when the checked value is set to 1 in the list, it should update the viewmodel to Greenimage at the same time with out refreshing it. Through my searching I found INotifyPropertyChaned is an option for me.I have class as ListClass Public class List{ Public string Item{get; set;} public bool Checked{get; set;}}Now I want this onpropertychanged to fire on Checked . and I got another Xaml, xaml.cs file.Can you please help me how can I do that for this application?
  7. Mimisays:
    Hola Luz, puedes dinfeir el estilo en XAML como un recurso, ya sea en el xaml de tu xaml.cs o en el app.xaml. Y ya el estilo lo puedes dinfeir programe1ticamente. Por ejemplo, puedes dinfeir un estilo en tu mismo archivo xaml de esta manera:s1.Style = (Style)Resources["nombre"];O si quisieras dinfeir un tempate y ponero en tu App.xaml, lo podredas setear de esta manera:s1.Template = (ControlTemplate) Application.Current.Resources["nombre"];Todo depende de que te sirva (un template o un estilo) y donde quieras poner el recurso (en tu archivo xaml o en tu app.xaml)Espero que esto te sirva
  8. Triinusays:
    Great post, I'll definitely be putitng this info to good use in my WP7 app. Currently it creates an instance of the VM every time the page's constructor is called. Which I guess isn't too bad since I'm still getting the benefit of the Dormant to active maintaining state and there by skipping the ctor. However, thanks to your article I'll cut down on the hits to isolated storage, which should speed up performance a fair bit.

Comment on this Post

Remember me

2 trackbacks for “State Management and Isolated Storage in Silverlight 2 Beta 1”

  1. Dew Drop - March 22, 2008 | Alvin Ashcraft's Morning Dewsays:
    PingBack from http://www.alvinashcraft.com/2008/03/22/dew-drop-march-22-2008/
  2. Community Blogssays:
    Michael Sync on User Control Inheritance, Expression Team provides DeepZoom collection example, Pete