I have been unusually busy for the last couple of months and have been neglecting my blog. Things are still pretty busy, but I hope to squeeze out a few posts if I can.
In this post I’m going to describe a behavior that you can attach to your controls and bind to a property on that control. The behavior will monitor the value of that property and store any changes to it in Isolated Storage. The next time you start the application the behavior will look in Isolated Storage and load the value if it can find it, then restore the value to the control. This is a handy behavior for things like grid splitters, sliders, or similar types of controls (dial’s too!) where you want any changes the user makes to be remembered the next time they run your app.
You can grab the behavior here. And this is the behavior in action:
If you refresh this page, the application will remember the opacity value for the border control, the position of the grid splitter, and the selected index of the list.
Using the behavior looks like this:
There are two things worth discussing about this behavior; the Binding to a control’s property and the the storing/retrieving using Isolated Storage.
Binding to a Control’s Property
The XAML above shows the TotalRecallBehavior being used to remember the opacity of the border element. Internally, the behavior needs to know when the property being bound to (in this instance it is the Border.Opacity property) changes value so it can store it away for next time. To do this, the behavior has an internal DependencyProperty called BindingValueProperty that it uses to create another two-way binding to the same property on the control. BindingValueProperty has an event handler for when the value changes so it can store the new value in Isolated Storage.
The code that uses the behavior’s Binding property to set up a private two-way binding looks like this:
The call to “this.ReadLocalValue(BindingProperty) as BindingExpression” is exactly what UIElement.GetBindingExpression() does, but the Behavior is not a UIElement so I just make the same call. Getting hold of the BindingExpression is important since it gives us access to the correct binding source and property path. The BindingExpression only exists on the source object of the binding (even if it is two-way), so on line 2 of the XAML snippet the binding is set on the behavior and must therefore be read from the behavior with the call to ReadLocalValue (line 16 of the C# code above). If you try and read the BindingExpression on the element being bound to you will get null returned.
The behavior sets up the binding and creates an instance of the RecallValue class. I have future plans for the RecallValue class, but for now it is simply a wrapper around a string value.
Using the ApplicationSettings on IsolatedStorage
You can use isolated storage to store all kinds of information on the user’s local machine. You can read all about local storage in Silverlight here. One of the less frequently covered uses of Isolated Storage is the IsolatedStorageSettings.ApplicationSettings static property. This property is an IDictionary and has an indexer you can use to read and write with. These are very handy for storing atomic name-value pairs; exactly the kind of thing I want to do with this behavior.
The code that read’s the value from isolated storage is as follows:
The value is read from isolated storage if it exists, and set as the value for our private BindingValueProperty dependency property, which is by now two-way bound to the control’s property. The IsolatedStorageSettings class takes care of creating a location in isolated storage for the values so there’s no other code needed for setting up file streams and creating directories etc.
Similarly, the code for writing to the isolated storage settings is as follows:
IsolatedStorageSettings.ApplicationSettings[behavior.recallValue.Key] = behavior.recallValue.Value;
The rest of the behavior is just plumbing.
As I said earlier, I have plans for the RecallValue class. I want to support the saving/restoring of complex objects and values that don’t parse easily to and from string.
I would also like to improve the design-time experience – the Binding type doesn’t play very nice with Blend’s element binding design tool, so I can’t use the CustomPropertyValueEditor attribute. I would really like the design time property editor for the Binding to list the properties on the attached object the same way the ChangePropertyAction does.
In this post I’ve described a behavior that remembers the value of a property on the control it is attached to. I described the use of the BindingExpression class to create a private two-way binding, and described how to use the IsolatedStorageSettings.ApplicationSettings dictionary. In the code download there are examples of the behavior being used on different kinds of controls and properties.