IsolatedStorageProvider v PersistenceManager: Dawn of Persistence

Yeah…this title is in tribute to the soon to be released Batman v Superman: Dawn of Justice movie.  It looks like it’ll be pretty decent.

So this post originated from a need to persist my wonder apps’ column order and size between launchings of the app.  Telerik has created the PersistenceFramework to handle these sort of situations, but in my opinion the documentation could use a little sprucing up to help clear up when to use the various pieces of it…hence why I’m posting about it.

When I started digging into the PersistenceFramework, I first tried to use the PersistenceManager control and quickly found that even though it seemed really easy to implement…using it the way their documentation suggested seemed to be broken.  I’ll show the example code below as they said to do it in their documentation (maybe I read it wrong, but it seemed pretty straight forward…)

MainWindow XAML Code:
<Window Height="350"
        Title="MainWindow"
        Width="525"
        x:Class="PersistenceManagerator.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation">
    <Grid>
        <telerik:RadGridView    AutoGenerateColumns="False"
                                ColumnWidth="*"
                                Loaded="PersistenceExampleRadGridView_Loaded"
                                NewRowPosition="None"
                                RowIndicatorVisibility="Collapsed"
                                Unloaded="PersistenceExampleRadGridView_Unloaded"
                                x:Name="PersistenceExampleRadGridView">
            <telerik:RadGridView.Columns>
                <telerik:GridViewDataColumn DataMemberBinding="{Binding PropertyFromYourBoundObject1}"
                                            Header="Property 1" />
                <telerik:GridViewDataColumn DataMemberBinding="{Binding PropertyFromYourBoundObject2}"
                                            Header="Property 2" />
                <telerik:GridViewDataColumn DataMemberBinding="{Binding PropertyFromYourBoundObject3}"
                                            Header="Property 3" />
                <telerik:GridViewDataColumn DataMemberBinding="{Binding PropertyFromYourBoundObject4}"
                                            Header="Property 4" />
                <telerik:GridViewDataColumn DataMemberBinding="{Binding PropertyFromYourBoundObject5}"
                                            Header="Property 5" />
            </telerik:RadGridView.Columns>
        </telerik:RadGridView>
    </Grid>
</Window>

MainWindow XAML C# Code Behind:
using System.IO;
using System.Windows;
using Telerik.Windows.Persistence;

namespace PersistenceManagerator
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private Stream stream;

        public MainWindow()
        {
            InitializeComponent();
        }

        private void PersistenceExampleRadGridView_Loaded(object sender,
RoutedEventArgs e)

        {
            stream.Position = 0L;
            PersistenceManager manager = new PersistenceManager();
            manager.Load(PersistenceExampleRadGridView, stream);
        }

        private void PersistenceExampleRadGridView_Unloaded(object sender,
RoutedEventArgs e)

        {
            PersistenceManager manager = new PersistenceManager();
            stream = manager.Save(PersistenceExampleRadGridView);
        }
    }
}

App XAML:
<Application    Exit="Application_Exit"
StartupUri="MainWindow.xaml"
                x:Class="PersistenceManagerator.App"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/Telerik.Windows.Themes.Office2013;component/Themes/System.Windows.xaml" />
                <ResourceDictionary Source="/Telerik.Windows.Themes.Office2013;component/Themes/Telerik.Windows.Controls.xaml" />
                <ResourceDictionary Source="/Telerik.Windows.Themes.Office2013;component/Themes/Telerik.Windows.Controls.Input.xaml" />
                <ResourceDictionary Source="/Telerik.Windows.Themes.Office2013;component/Themes/Telerik.Windows.Controls.GridView.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

App XAML C# Code Behind:
using System.IO;
using System.Windows;
using Telerik.Windows.Persistence;

namespace PersistenceManagerator
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        private Stream stream;

        public App()
        {
            this.InitializeComponent();
        }

        private void Application_Exit(object sender, ExitEventArgs e)
        {
            PersistenceManager manager = new PersistenceManager();
            stream = manager.Save(((MainWindow)App.Current.MainWindow).PersistenceExampleRadGridView);
        }
    }
}

I bolded where the problem is with the Telerik instruction.  You can’t just use a Stream object without it being initialized…and you can’t just new one up since there’s no constructor for it…so when it’s used within the grid’s Loaded event, it throws a NullReferenceException…so I wrote up a post and asked them to help me figure that one out…and while I waited for their response I discovered IsolatedStorageProvider.  This looked promising.  Here’s what my out of box attempt at using IsolatedStorageProvider looked like…

MainWindow XAML:
<Window Height="350"
        Title="MainWindow"
        Width="525"
        x:Class="IsolatedStorageProviderator.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid>
        <telerik:RadGridView    AutoGenerateColumns="False"
                                ColumnWidth="*"
                                Loaded="IsolatedStorageExampleRadGridView_Loaded"
                                NewRowPosition="None"
                                RowIndicatorVisibility="Collapsed"
                                telerik:PersistenceManager.StorageId="IsolatedStorageExampleRadGridView"
                                Unloaded="IsolatedStorageExampleRadGridView_Unloaded"
                                x:Name="IsolatedStorageExampleRadGridView">
            <telerik:RadGridView.Columns>
                <telerik:GridViewDataColumn DataMemberBinding="{Binding PropertyFromYourBoundObject1}"
                                            Header="Property 1" />
                <telerik:GridViewDataColumn DataMemberBinding="{Binding PropertyFromYourBoundObject2}"
                                            Header="Property 2" />
                <telerik:GridViewDataColumn DataMemberBinding="{Binding PropertyFromYourBoundObject3}"
                                            Header="Property 3" />
                <telerik:GridViewDataColumn DataMemberBinding="{Binding PropertyFromYourBoundObject4}"
                                            Header="Property 4" />
                <telerik:GridViewDataColumn DataMemberBinding="{Binding PropertyFromYourBoundObject5}"
                                            Header="Property 5" />
            </telerik:RadGridView.Columns>
        </telerik:RadGridView>
    </Grid>
</Window>

MainWindow XAML C# Code Behind:
using System.Windows;
using Telerik.Windows.Persistence.Storage;

namespace IsolatedStorageProviderator
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void IsolatedStorageExampleRadGridView_Loaded(
object sender, RoutedEventArgs e)

        {
            IsolatedStorageProvider storage =
new IsolatedStorageProvider();

            storage.LoadFromStorage(
new string[] { "IsolatedStorageExampleRadGridView" });

        }

        private void IsolatedStorageExampleRadGridView_Unloaded(
object sender, RoutedEventArgs e)

        {
            IsolatedStorageProvider storage =
new IsolatedStorageProvider();

            storage.SaveToStorage(
new string[] { "IsolatedStorageExampleRadGridView" });

        }
    }
}

App XAML:
<Application    Exit="Application_Exit"
                StartupUri="MainWindow.xaml"
                x:Class="IsolatedStorageProviderator.App"
                xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                xmlns:telerik="http://schemas.telerik.com/2008/xaml/presentation"
                xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/Telerik.Windows.Themes.Office2013;component/Themes/System.Windows.xaml" />
                <ResourceDictionary Source="/Telerik.Windows.Themes.Office2013;component/Themes/Telerik.Windows.Controls.xaml" />
                <ResourceDictionary Source="/Telerik.Windows.Themes.Office2013;component/Themes/Telerik.Windows.Controls.Input.xaml" />
                <ResourceDictionary Source="/Telerik.Windows.Themes.Office2013;component/Themes/Telerik.Windows.Controls.GridView.xaml" />
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

App XAML C# Code Behind:
using System.Windows;
using Telerik.Windows.Persistence.Storage;

namespace IsolatedStorageProviderator
{
    /// <summary>
    /// Interaction logic for App.xaml
    /// </summary>
    public partial class App : Application
    {
        public App()
        {
            this.InitializeComponent();
        }

        private void Application_Exit(object sender, ExitEventArgs e)
        {
            IsolatedStorageProvider storage =
new IsolatedStorageProvider();

            storage.SaveToStorage(
new string[] { "IsolatedStorageExampleRadGridView" });

        }
    }
}

This worked great for persisting resized columns between app launches…but still did not work for persisting reordered columns.  At this point in our EXCITING story, Telerik had gotten back to me and clued me in on the little fact that out of the box, the PersistenceFramework did not perseve the order of the columns.  Ah…yes, that would explain why my example fails at that.  They also told me that I’d need to implement a CustomPropertyProvider for the RadGridView in order to do this…so off I went to figure that out.  So I needed my own custom object for containing the info I wanted to preserve.  Here’s what I ended up with…

ColumnProxy C# Code:
using Telerik.Windows.Controls;

namespace IsolatedStorageProviderator
{
    public class ColumnProxy
    {
        public string UniqueName { get; set; }

        public int DisplayOrder { get; set; }

        public string Header { get; set; }

        public GridViewLength Width { get; set; }
    }
}

Then I created a new class for my CustomPropertyProvider…

GridViewCustomPropertyProvider C# Code:
using System.Collections.Generic;
using Telerik.Windows.Controls;
using Telerik.Windows.Persistence.Services;

namespace IsolatedStorageProviderator
{
    public class GridViewCustomPropertyProvider : ICustomPropertyProvider
    {
        public CustomPropertyInfo[] GetCustomProperties()
        {
            return new CustomPropertyInfo[]
            {
                new CustomPropertyInfo("Columns", typeof(List<ColumnProxy>)),
            };
        }

        public void InitializeObject(object context) { }

        public object InitializeValue(
CustomPropertyInfo customPropertyInfo, object context)

        {
            return null;
        }

        public object ProvideValue(
CustomPropertyInfo customPropertyInfo, object context)

        {
            RadGridView gridView = context as RadGridView;

            switch (customPropertyInfo.Name)
            {
                case "Columns":
                {
                    List<ColumnProxy> columnProxies = new List<ColumnProxy>();

                    foreach (GridViewColumn column in gridView.Columns)
                    {
                        columnProxies.Add(new ColumnProxy()
                        {
                            UniqueName = column.UniqueName,
                            Header = column.Header.ToString(),
                            DisplayOrder = column.DisplayIndex,
                            Width = column.Width,
                        });
                    }

                    return columnProxies;
                }
            }

            return null;
        }

        public void RestoreValue(
CustomPropertyInfo customPropertyInfo,
object context,
object value)

        {
            if(value != null && value.ToString() != "None")
            {
                RadGridView gridView = context as RadGridView;

                switch (customPropertyInfo.Name)
                {
                    case "Columns":
                    {
                        List<ColumnProxy> columnProxies = value as List<ColumnProxy>;

                        foreach (ColumnProxy proxy in columnProxies)
                        {
                            GridViewColumn column =
gridView.Columns[proxy.UniqueName];

                            column.DisplayIndex = proxy.DisplayOrder;
                            column.Header = proxy.Header;
                            column.Width = proxy.Width;
                        }
                    }
                    break;
                }
            }
        }
    }
}

And lastly I had to add in a little something to tell the PersistenceManager to use my CustomPropertyProvider…

Extra code added to MainWindow XAML C# Code Behind:
public MainWindow()
{
    InitializeComponent();

    ServiceProvider.RegisterPersistenceProvider<ICustomPropertyProvider>(typeof(RadGridView), new GridViewCustomPropertyProvider());
}

The part in bold is what I added to the MainWindow code behind.  After I added the above code my wonder app’s columns size and order are now preserved between launchings (as well as column headers, but these weren’t editable so it’s probably not neccessary).

Hope this helps someone…enjoy!  🙂

 

Advertisements
IsolatedStorageProvider v PersistenceManager: Dawn of Persistence

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s