Little tip to improve the IsolatedStorageSettings on WP7

The IsolatedStorageSettings class is pretty good. It allows you to access without problems to the settings of your app, with a nice casting and without having to deal with files. But if you’re using it a lot in different parts of the code, it’s probably better to enclose it in a bigger class, more useful to you. I’ve developed this, which can be pretty useful:

public static class Config
{
        private static readonly string SomeConfigElementKey = "MYKEY";
        private static string _someConfigElement;
 
        public string SomeConfigElement {
            get {
              return GenericGetFromConfig(SomeConfigElementKey, ref _someConfigElement);
            }
            set {
              GenericSaveToConfig(SomeConfigElementKey, ref _someConfigElement, value);
            }
        }
 
        private static T GenericGetFromConfig(string Key, ref T element) where T : new()
        {
            if (element != null)
                return element;
 
            IsolatedStorageSettings config = IsolatedStorageSettings.ApplicationSettings;
 
            try
            {
                if (!config.TryGetValue(Key, out element))
                {
                    element = new T();
                    config.Add(Key, element);
                    config.Save();
                }
            }
            catch (InvalidCastException)
            {
                config.Remove(Key);
            }
            catch (Exception)
            {
            }
 
            if (element == null)
                element = new T();
 
            return element;
        }
 
private static void GenericSaveToConfig(string Key, ref T element, T value) where T : new()
        {
            if (value == null)
                return;
 
            IsolatedStorageSettings conf = IsolatedStorageSettings.ApplicationSettings;
 
            try
            {
                element = value;
                if (conf.Contains(Key))
                    conf[Key] = value;
                else
                    conf.Add(Key, value);
                conf.Save();
            }
            catch (Exception)
            {
            }
        }
}

(Gist snippet on GitHub) 

It’s pretty simple. The main part of the class are the methods GenericGetFromConfig and GenericSaveToConfig. The first one always returns the element you requested. If it does not exist, the method creates it and saves it in the Isolated storage. It also controls exceptions, so you will never get null or an error.

The second one does more or less the same: save an element to the isolated storage. If it exists, the method updates it avoiding duplicates. As the previous method, it’s failsafe.

Why the ref T element in both of the functions? That’s there to avoid calling IsolatedStorageSettings every time you need an object. You can have a look at the example of SomeConfigElement. There is a private static variable who acts as an intermediate between your code and the IsolatedStorage. Whenever you want to get a value, the function first checks if the static variable is null. If it is, it reads from IsolatedStorage. But if not, that means that we’ve already retrieved the value and don’t need to call again to IsolatedStorage.

That variable is always synced with the IsolatedStorage, so you don’t have to worry about anything when retrieving data from the settings. You only have to get the value from Config.WhateverVariable and that’s it. Clean, safe, useful and fast.

ExtendedListBox: custom WP7 Listbox control which detects end of scroll, allows infinite scrolling

The solution was posted months ago in Silverlight – Windows Phone Team’s blog. The only thing I made is making an extended ListBox control with an event which allows easy management of end-of-scroll states.

Advantages against the traditional ListBox control? ExtendedListBox will fire an event when the user has scrolled to the end or beginning of the list. With this event you can manage to do an infinite-scrolling control (more or less).

The class is this:

public class ExtendedListBox : ListBox
    {
        // Compression states: Thanks to http://blogs.msdn.com/b/slmperf/archive/2011/06/30/windows-phone-mango-change-listbox-how-to-detect-compression-end-of-scroll-states.aspx
 
        protected bool _isBouncy = false;
        private bool alreadyHookedScrollEvents = false;
 
        public ExtendedListBox()
        {
            this.Loaded += new RoutedEventHandler(ListBox_Loaded);
        }
 
        private void ListBox_Loaded(object sender, RoutedEventArgs e)
        {
            ScrollBar sb = null;
            ScrollViewer sv = null;
            if (alreadyHookedScrollEvents)
                return;
 
            alreadyHookedScrollEvents = true;
            this.AddHandler(ExtendedListBox.ManipulationCompletedEvent, (EventHandler<manipulationcompletedeventargs>)LB_ManipulationCompleted, true);
            sb = (ScrollBar)FindElementRecursive(this, typeof(ScrollBar));
            sv = (ScrollViewer)FindElementRecursive(this, typeof(ScrollViewer));
 
            if (sv != null)
            {
                // Visual States are always on the first child of the control template 
                FrameworkElement element = VisualTreeHelper.GetChild(sv, 0) as FrameworkElement;
                if (element != null)
                {
                    VisualStateGroup vgroup = FindVisualState(element, "VerticalCompression");
                    VisualStateGroup hgroup = FindVisualState(element, "HorizontalCompression"); 
                    if (vgroup != null)
                        vgroup.CurrentStateChanging += new EventHandler<visualstatechangedeventargs>(vgroup_CurrentStateChanging);
                    if (hgroup != null)
                        hgroup.CurrentStateChanging += new EventHandler</visualstatechangedeventargs><visualstatechangedeventargs>(hgroup_CurrentStateChanging);
                }
            }
 
        }
 
        public delegate void OnCompression(object sender, CompressionEventArgs e);
        public event OnCompression Compression;
 
        private void hgroup_CurrentStateChanging(object sender, VisualStateChangedEventArgs e)
        {
            if (e.NewState.Name == "CompressionLeft")
            {
                _isBouncy = true;
                if (Compression != null)
                    Compression(this, new CompressionEventArgs(CompressionType.Left));
            }
 
            if (e.NewState.Name == "CompressionRight")
            {
                _isBouncy = true;
                if (Compression != null)
                    Compression(this, new CompressionEventArgs(CompressionType.Right));
            }
            if (e.NewState.Name == "NoHorizontalCompression")
            {
                _isBouncy = false;
            }
        }
 
        private void vgroup_CurrentStateChanging(object sender, VisualStateChangedEventArgs e)
        {
            if (e.NewState.Name == "CompressionTop")
            {
                _isBouncy = true;
                if (Compression != null)
                    Compression(this, new CompressionEventArgs(CompressionType.Top));
            }
            if (e.NewState.Name == "CompressionBottom")
            {
                _isBouncy = true;
                if(Compression!=null)
                    Compression(this, new CompressionEventArgs(CompressionType.Bottom));
            }
            if (e.NewState.Name == "NoVerticalCompression")
                _isBouncy = false;
        }
 
        private void LB_ManipulationCompleted(object sender, ManipulationCompletedEventArgs e)
        {
            if (_isBouncy)
                _isBouncy = false;
        }
 
        private UIElement FindElementRecursive(FrameworkElement parent, Type targetType)
        {
            int childCount = VisualTreeHelper.GetChildrenCount(parent);
            UIElement returnElement = null;
            if (childCount > 0)
            {
                for (int i = 0; i < childCount; i++)
                {
                    Object element = VisualTreeHelper.GetChild(parent, i);
                    if (element.GetType() == targetType)
                    {
                        return element as UIElement;
                    }
                    else
                    {
                        returnElement = FindElementRecursive(VisualTreeHelper.GetChild(parent, i) as FrameworkElement, targetType);
                    }
                }
            }
            return returnElement;
        }
 
        private VisualStateGroup FindVisualState(FrameworkElement element, string name)
        {
            if (element == null)
                return null;
 
            IList groups = VisualStateManager.GetVisualStateGroups(element);
            foreach (VisualStateGroup group in groups)
                if (group.Name == name)
                    return group;
 
            return null;
        }
    }
 
    public class CompressionEventArgs : EventArgs
    {
        public CompressionType Type { get; protected set; }
 
        public CompressionEventArgs(CompressionType type)
        {
            Type = type;
        }
    }
 
    public enum CompressionType { Top, Bottom, Left, Right };

It fires the event Compression whenever a scroll has ended. CompressionEventArgs contains the info on the side the scroll has ended. But this is not sufficient to make things work. You have to add this code to your App.xaml:

<style TargetType="ScrollViewer">
            <setter Property="VerticalScrollBarVisibility" Value="Auto"/>
            <setter Property="HorizontalScrollBarVisibility" Value="Auto"/>
            <setter Property="Background" Value="Transparent"/>
            <setter Property="Padding" Value="0"/>
            <setter Property="BorderThickness" Value="0"/>
            <setter Property="BorderBrush" Value="Transparent"/>
            <setter Property="Template">
                </setter><setter .Value>
                    <controltemplate TargetType="ScrollViewer">
                        <border BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
                            <visualstatemanager .VisualStateGroups>
                                <visualstategroup x:Name="ScrollStates">
                                    </visualstategroup><visualstategroup .Transitions>
                                        <visualtransition GeneratedDuration="00:00:00.5"/>
                                    </visualstategroup>
                                    <visualstate x:Name="Scrolling">
                                        <storyboard>
                                            <doubleanimation Storyboard.TargetName="VerticalScrollBar" Storyboard.TargetProperty="Opacity" To="1" Duration="0"/>
                                            <doubleanimation Storyboard.TargetName="HorizontalScrollBar" Storyboard.TargetProperty="Opacity" To="1" Duration="0"/>
                                        </storyboard>
                                    </visualstate>
                                    <visualstate x:Name="NotScrolling"/>
 
                                <visualstategroup x:Name="VerticalCompression">
                                    <visualstate x:Name="NoVerticalCompression"/>
                                    <visualstate x:Name="CompressionTop"/>
                                    <visualstate x:Name="CompressionBottom"/>
                                </visualstategroup>
                                <visualstategroup x:Name="HorizontalCompression">
                                    <visualstate x:Name="NoHorizontalCompression"/>
                                    <visualstate x:Name="CompressionLeft"/>
                                    <visualstate x:Name="CompressionRight"/>
                                </visualstategroup>
                            </visualstatemanager>
                            <grid Margin="{TemplateBinding Padding}">
                                <scrollcontentpresenter x:Name="ScrollContentPresenter" Content="{TemplateBinding Content}" ContentTemplate="{TemplateBinding ContentTemplate}"/>
                                <scrollbar x:Name="VerticalScrollBar" IsHitTestVisible="False" Height="Auto" Width="5" HorizontalAlignment="Right" VerticalAlignment="Stretch" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" IsTabStop="False" Maximum="{TemplateBinding ScrollableHeight}" Minimum="0" Value="{TemplateBinding VerticalOffset}" Orientation="Vertical" ViewportSize="{TemplateBinding ViewportHeight}" />
                                <scrollbar x:Name="HorizontalScrollBar" IsHitTestVisible="False" Width="Auto" Height="5" HorizontalAlignment="Stretch" VerticalAlignment="Bottom" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" IsTabStop="False" Maximum="{TemplateBinding ScrollableWidth}" Minimum="0" Value="{TemplateBinding HorizontalOffset}" Orientation="Horizontal" ViewportSize="{TemplateBinding ViewportWidth}" />
                            </grid>
                        </border>
                    </controltemplate>
                </setter>
 
        </style>

Just with this, you can start working with the ExtendedListBox. And I suppose you could also extend in the same way similar controls, just reusing the code.

All of the code is completely free to use, and the credits go to Silverlight – WP blog, they developed the main part of this trick. I only put it together.