In the project I'm involved with, we've been saving column arrangement and column width data for quite a while. I had a request to give the users a way to select which columns to display and it just seemed natural to include the display order with that data.
Recently I've been tasked with saving the filtering and sorting information as well. We're using the Telerik RadGrid, so I went to their site to find out how to do that.
Saving all sorts of Grid-related data to Isolated Storage was really easy as I'll show first. Saving that data to the database was a bit more involved. I'm not going to really fire off to SQL in this post, but I'm going to go as far as saving the data to a global string variable, and read it back.
First a live demo
I figured first off I'd show the app running, and then explain.
The Grid below contains a bunch of dummy data to give you multiple columns to play with. You can set up sorting, filtering, grouping, and you can rearrange and size the columns. If you then use the right-click context menu and choose "Save Filters", the default is to save the filter data to ISO. Selecting "Clear All Filters" will restore everything except the column rearrangement and sizing. Selecting "Restore Filters" will then put everything back as you had it prior to saving.
The default is to save to ISO, but if you select the "Use String for storage" radio button and save, it will not only work the same, but the string data is displayed in the textbox below the grid.
A few basic comments
I built this app using codebehind. That's not how I did the production version, but not wanting to get involved in lengthy discussions with the MVVM police, plus make this as simple as possible to follow, I elected to just do everything this way.
I'm also not going to explain every nut and bolt of how to lay out the page, set up binding, etc. There's a link at the bottom to download the code, so you will get all that, I just don't want to take up space talking about stuff that doesn't really address what I'm trying to show.
Let's Go
First off, if you don't have the Telerik controls on board, you can get a
trial version here.
All you need to do is install the tools. They end up in your GAC, and you can find them in your Visual Studio ToolBox. For this sample, I started a new project using the Telerik template "RadControls Silverlight Application". I picked a name for my project, and pressed OK:

I took the defaults on the next box:

Then in the Telerik Configuration Wizard, I selected the PersistenceFramework and the GridView. When I checked GridView, it automatically selected Controls, Input, and Data:
Once you click Finish on the Wizard, you get into the normal 2 project Silverlight application setup we've all seen so many times.
XAML stuff
After geting your grid configured, there's only one thing you have to add to it to get the PersistenceFramework wired up, and that is to add the PersistenceManager.StorageId entry as shown in the last line of my grid:
<telerikGridView:RadGridView x:Name="gvDemo"
Grid.Row="2"
Width="500"
ShowColumnHeaders="True"
ShowGroupPanel="True"
CanUserInsertRows="False"
ItemsSource="{Binding DemoData}"
ScrollViewer.HorizontalScrollBarVisibility="Visible"
IsReadOnly="True"
AutoGenerateColumns="True"
telerik:PersistenceManager.StorageId="gvDemo">
Code Behind
There are two classes you'll need to add that I got straight from one of the Telerik demos: GridViewCustomFilterProvider.cs and FilterProxies.cs . The ones included in the code are the complete classes. Since I'm handling columns myself in the production code, I don't have the column pieces in place. Point being, you can pick and choose what you want to save and restore.
After those two classes are in the project, you can continue because the first thing you need to do is register the Custom FilterProvider by including this line in the constructor:
ServiceProvider.RegisterPersistenceProvider<ICustomPropertyProvider>(typeof(RadGridView), new GridViewCustomFilterProvider());
ISO First
I'll show the Isolated Storage solution first because it's so simple.
Two small methods do the save and restore because all the heavy lifting is done by the PersistenceFramework:
private void SaveFilters(){
IsolatedStorageProvider provider = new IsolatedStorageProvider();
provider.SaveToStorage();
}
private void RestoreFilters()
{
Dispatcher.BeginInvoke(new Action(() =>
{
IsolatedStorageProvider provider = new IsolatedStorageProvider();
provider.LoadFromStorage();
}));
}
The IsolatedStorageProvider referenced in the above code is in the Telerik.Windows.Persistence.Storage namespace. If you're curious how this all plays together, remember the StorageId we put into the XAML. The PersistenceFramework keeps track of those IDs and saves the data in ISO according to the name of the grid, in this case.
Because of all the work I do with ISO, I have a shortcut on my desktop to the base ISO location. If I browse through that set of folders, I'll find a file written moments ago named "gvDemo.bin". gvDemo is the name of the StorageId we assigned to the RadGridView control that we're using, so saving and restoring is simply using the StorageId to know what to save and from where and what to restore and to where.
Before moving on to the streaming part of the post, I'll show the code used to clear the various elements. These 3 lines of code are taken directly from the top of the GridViewCustomFilterProvider, just modified to have the control name in place:
gvDemo.SortDescriptors.Clear();
gvDemo.FilterDescriptors.Clear();
gvDemo.GroupDescriptors.Clear();
Streaming Second
Now we get to the part I couldn't find much help with. I found this post:
RadPersistenceFramework in an MVVM environment, and the accompanying code was helpful, but it must be for an earlier version of the tools because there were some pieces I wanted to use that just wouldn't compile. I'd like to give the author a shoutout, but I can't find a name on that site :(
Using that code as a launching point, I came up with the following solution for Saving. As you look at this, _data is simply a globally defined string in this example. In production code, this is the string written out to a webservice to be stored appropriately.
ObjectStorage obj = PersistenceManager.GetStorage();
using (Stream stream = new PersistenceManager().Save(obj.GetElement("gvDemo")))
{
if (stream.Length > 0)
{
StreamReader reader = new StreamReader(stream);
_data = reader.ReadToEnd();
}
}
Remember that the Persistence Framework knows about the objects we've tagged in the XAML, so it should be no surprise that using an instance of the ObjectStorage class, we can address the specific RadGridView we want to save by using the StorageId, in this case, "gvDemo".
The stream is then pushed into the string by using a StreamReader ReadToEnd method.
In the running example above, when "Use String for storage" is selected, and filter information is saved, the text displayed in the TextBox below the grid is a copy of _data. You can copy that block out and compare it to the gvDemo.bin file in ISO, or nose around in it to see what all is being saved.
As I said early on, I'm not going to do the SQL part, I'm just using a string for local storage. So next up is reading that string back into the RadGridView.
The following code is what is necessary to do just that:
Dispatcher.BeginInvoke(new Action(() =>
{
byte[] byteArray = Encoding.UTF8.GetBytes(_data);
using (Stream stream = new MemoryStream(byteArray))
{
stream.Position = 0L;
ObjectStorage obj = PersistenceManager.GetStorage();
new PersistenceManager().Load(obj.GetElement("gvDemo"), stream);
}
}));
First the string is converted to a Byte Array. A MemoryStream is created from the Byte Array, and once again using the ObjectStorage and PersistenceManager, the stream is loaded to the UIElement by name.
Not a boat-load of code. There's actually a bunch more support code just to bang together an example like this.
I've left out lots of things like checking to make sure strings weren't empty or objects weren't null. They're important to the execution but not explanation, and all of that is in the source.
What's Next
This came up yesterday afternoon and I thought it was contained enough I could write it up and get it onto the site and would hopefully save someone a bit of time.
I actually intended to put up another Metro post. Since my app didn't make it to round 2, I'm going to blog it out in a couple posts, detailing stuff I learned while getting it ready to submit... some obvious and some not so obvious.
You can download the application shown above by using
This Link. If you have any problems with that, or questions on any of this, don't hesitate to ask,
and...
Stay in the 'Light!