Regionerate Rocks!

September 15, 2009

I’ve just stumbled across Regionerate.  It’s a fantastic little tool that automatically applies regions to C# code.  For a code nazi like me this is invaluable and will likely spare me from RSI and lifetime of misery!

I also wrote a custom configuration file that matches my particular form of OCD (obsessive code disorder):

<?xml version="1.0" encoding="utf-16"?>
<CodeLayout xmlns="http://regionerate.net/schemas/0.7.0.0/CodeLayout.xsd">
  <ForEach Type="Class">
    <CreateRegion Title="Public Static Fields" PadFirstChild="0" PadLastChild="0" ShowCount="false" Style="Visible">
      <PutFields>
        <Where>
          <Static Equals="true"/>
          <Access Equals="Public" />
        </Where>
        <OrderBy>
          <Name />
        </OrderBy>
      </PutFields>
    </CreateRegion>
    <CreateRegion Title="Internal Static Fields" PadFirstChild="0" PadLastChild="0" ShowCount="false" Style="Visible">
      <PutFields>
        <Where>
          <Static Equals="true"/>
          <Access Equals="Internal" />
        </Where>
        <OrderBy>
          <Name />
        </OrderBy>
      </PutFields>
    </CreateRegion>
    <CreateRegion Title="Protected Static Fields" PadFirstChild="0" PadLastChild="0" ShowCount="false" Style="Visible">
      <PutFields>
        <Where>
          <Static Equals="true"/>
          <Access Equals="Protected" />
        </Where>
        <OrderBy>
          <Name />
        </OrderBy>
      </PutFields>
    </CreateRegion>
    <CreateRegion Title="Private Static Fields" PadFirstChild="0" PadLastChild="0" ShowCount="false" Style="Visible">
      <PutFields>
        <Where>
          <Static Equals="true"/>
          <Access Equals="Private" />
        </Where>
        <OrderBy>
          <Name />
        </OrderBy>
      </PutFields>
    </CreateRegion>
    <CreateRegion Title="Public Fields" PadFirstChild="0" PadLastChild="0" ShowCount="false" Style="Visible">
      <PutFields>
        <Where>
          <Static Equals="false"/>
          <Access Equals="Public" />
        </Where>
        <OrderBy>
          <Name />
        </OrderBy>
      </PutFields>
    </CreateRegion>
    <CreateRegion Title="Internal Fields" PadFirstChild="0" PadLastChild="0" ShowCount="false" Style="Visible">
      <PutFields>
        <Where>
          <Static Equals="false"/>
          <Access Equals="Internal" />
        </Where>
        <OrderBy>
          <Name />
        </OrderBy>
      </PutFields>
    </CreateRegion>
    <CreateRegion Title="Protected Fields" PadFirstChild="0" PadLastChild="0" ShowCount="false" Style="Visible">
      <PutFields>
        <Where>
          <Static Equals="false"/>
          <Access Equals="Protected" />
        </Where>
        <OrderBy>
          <Name />
        </OrderBy>
      </PutFields>
    </CreateRegion>
    <CreateRegion Title="Private Fields" PadFirstChild="0" PadLastChild="0" ShowCount="false" Style="Visible">
      <PutFields>
        <Where>
          <Static Equals="false"/>
          <Access Equals="Private" />
        </Where>
        <OrderBy>
          <Name />
        </OrderBy>
      </PutFields>
    </CreateRegion>
    <CreateRegion Title="Static Ctor" PadFirstChild="0" PadLastChild="0" ShowCount="false" Style="Visible">
      <PutMethods>
        <Where>
          <IsConstructor Equals="true" />
          <Static Equals="true"/>
        </Where>
        <OrderBy>
          <ParametersCount />
        </OrderBy>
      </PutMethods>
    </CreateRegion>
    <CreateRegion Title="Ctor" PadFirstChild="0" PadLastChild="0" ShowCount="false" Style="Visible">
      <PutMethods>
        <Where>
          <IsConstructor Equals="true" />
          <Static Equals="false"/>
        </Where>
        <OrderBy>
          <ParametersCount />
        </OrderBy>
      </PutMethods>
    </CreateRegion>
    <CreateRegion Title="Public Static Methods" PadFirstChild="0" PadLastChild="0" ShowCount="false" Style="Visible">
      <PutMethods>
        <Where>
          <IsConstructor Equals="false" />
          <Static Equals="true" />
          <Access Equals="Public"/>
        </Where>
        <OrderBy>
          <ParametersCount />
        </OrderBy>
      </PutMethods>
    </CreateRegion>
    <CreateRegion Title="Internal Static Methods" PadFirstChild="0" PadLastChild="0" ShowCount="false" Style="Visible">
      <PutMethods>
        <Where>
          <IsConstructor Equals="false" />
          <Static Equals="true" />
          <Access Equals="Internal"/>
        </Where>
        <OrderBy>
          <ParametersCount />
        </OrderBy>
      </PutMethods>
    </CreateRegion>
    <CreateRegion Title="Protected Static Methods" PadFirstChild="0" PadLastChild="0" ShowCount="false" Style="Visible">
      <PutMethods>
        <Where>
          <IsConstructor Equals="false" />
          <Static Equals="true" />
          <Access Equals="Protected"/>
        </Where>
        <OrderBy>
          <ParametersCount />
        </OrderBy>
      </PutMethods>
    </CreateRegion>
    <CreateRegion Title="Private Static Methods" PadFirstChild="0" PadLastChild="0" ShowCount="false" Style="Visible">
      <PutMethods>
        <Where>
          <IsConstructor Equals="false" />
          <Static Equals="true" />
          <Access Equals="Private"/>
        </Where>
        <OrderBy>
          <ParametersCount />
        </OrderBy>
      </PutMethods>
    </CreateRegion>
    <CreateRegion Title="Public Methods" PadFirstChild="0" PadLastChild="0" ShowCount="false" Style="Visible">
      <PutMethods>
        <Where>
          <IsConstructor Equals="false" />
          <Static Equals="false" />
          <Access Equals="Public"/>
        </Where>
        <OrderBy>
          <ParametersCount />
        </OrderBy>
      </PutMethods>
    </CreateRegion>
    <CreateRegion Title="Internal Methods" PadFirstChild="0" PadLastChild="0" ShowCount="false" Style="Visible">
      <PutMethods>
        <Where>
          <IsConstructor Equals="false" />
          <Static Equals="false" />
          <Access Equals="Internal"/>
        </Where>
        <OrderBy>
          <ParametersCount />
        </OrderBy>
      </PutMethods>
    </CreateRegion>
    <CreateRegion Title="Protected Methods" PadFirstChild="0" PadLastChild="0" ShowCount="false" Style="Visible">
      <PutMethods>
        <Where>
          <IsConstructor Equals="false" />
          <Static Equals="false" />
          <Access Equals="Protected"/>
        </Where>
        <OrderBy>
          <ParametersCount />
        </OrderBy>
      </PutMethods>
    </CreateRegion>
    <CreateRegion Title="Private Methods" PadFirstChild="0" PadLastChild="0" ShowCount="false" Style="Visible">
      <PutMethods>
        <Where>
          <IsConstructor Equals="false" />
          <Static Equals="false" />
          <Access Equals="Private"/>
        </Where>
        <OrderBy>
          <ParametersCount />
        </OrderBy>
      </PutMethods>
    </CreateRegion>
    <CreateRegion Title="Events" PadFirstChild="0" PadLastChild="0" ShowCount="false" Style="Visible">
      <PutEvents>
        <OrderBy>
          <Name />
        </OrderBy>
      </PutEvents>
    </CreateRegion>
    <CreateRegion Title="Public Static Properties" PadFirstChild="0" PadLastChild="0" ShowCount="false" Style="Visible">
      <PutProperties>
        <Where>
          <Static Equals="true" />
          <Access Equals="Public"/>
        </Where>
        <OrderBy>
          <Name />
        </OrderBy>
      </PutProperties>
    </CreateRegion>
    <CreateRegion Title="Internal Static Properties" PadFirstChild="0" PadLastChild="0" ShowCount="false" Style="Visible">
      <PutProperties>
        <Where>
          <Static Equals="true" />
          <Access Equals="Internal"/>
        </Where>
        <OrderBy>
          <Name />
        </OrderBy>
      </PutProperties>
    </CreateRegion>
    <CreateRegion Title="Protected Static Properties" PadFirstChild="0" PadLastChild="0" ShowCount="false" Style="Visible">
      <PutProperties>
        <Where>
          <Static Equals="true" />
          <Access Equals="Protected"/>
        </Where>
        <OrderBy>
          <Name />
        </OrderBy>
      </PutProperties>
    </CreateRegion>
    <CreateRegion Title="Private Static Properties" PadFirstChild="0" PadLastChild="0" ShowCount="false" Style="Visible">
      <PutProperties>
        <Where>
          <Static Equals="true" />
          <Access Equals="Private"/>
        </Where>
        <OrderBy>
          <Name />
        </OrderBy>
      </PutProperties>
    </CreateRegion>
    <CreateRegion Title="Public Properties" PadFirstChild="0" PadLastChild="0" ShowCount="false" Style="Visible">
      <PutProperties>
        <Where>
          <Static Equals="false" />
          <Access Equals="Public"/>
        </Where>
        <OrderBy>
          <Name />
        </OrderBy>
      </PutProperties>
    </CreateRegion>
    <CreateRegion Title="Internal Properties" PadFirstChild="0" PadLastChild="0" ShowCount="false" Style="Visible">
      <PutProperties>
        <Where>
          <Static Equals="false" />
          <Access Equals="Internal"/>
        </Where>
        <OrderBy>
          <Name />
        </OrderBy>
      </PutProperties>
    </CreateRegion>
    <CreateRegion Title="Protected Properties" PadFirstChild="0" PadLastChild="0" ShowCount="false" Style="Visible">
      <PutProperties>
        <Where>
          <Static Equals="false" />
          <Access Equals="Protected"/>
        </Where>
        <OrderBy>
          <Name />
        </OrderBy>
      </PutProperties>
    </CreateRegion>
    <CreateRegion Title="Private Properties" PadFirstChild="0" PadLastChild="0" ShowCount="false" Style="Visible">
      <PutProperties>
        <Where>
          <Static Equals="false" />
          <Access Equals="Private"/>
        </Where>
        <OrderBy>
          <Name />
        </OrderBy>
      </PutProperties>
    </CreateRegion>
    <CreateRegion Title="Delegates" PadFirstChild="0" PadLastChild="0" ShowCount="false" Style="Visible">
      <PutDelegates>
        <OrderBy>
          <Name />
        </OrderBy>
      </PutDelegates>
    </CreateRegion>
    <CreateRegion Title="Nested Classes" PadFirstChild="0" PadLastChild="0" ShowCount="false" Style="Visible">
      <PutNestedClasses>
        <OrderBy>
          <Name />
        </OrderBy>
      </PutNestedClasses>
    </CreateRegion>
  </ForEach>
  <Configuration>
    <Symbol>
      <HiddenDragon />
    </Symbol>
    <Rendering ShowCount="false" />
  </Configuration>
</CodeLayout>

I’ve been playing around with hot-swapping themes in Silverlight recently. This turns out to be trickier than you might imagine due to a couple of facts:

  • Silverlight doesn’t support DynamicResources
  • Silverlight doesn’t support implicit styles

And no, I don’t count the ImplicitStyleManager in the SilverlightToolkit as it’s buggy and performs like a dog. At any rate, implicit styles soon become completely unmanageable in an application of any considerable size.
The solution I came up with was to have a ThemingService that automatically applies Styles to FrameworkElements. The FrameworkElements register themselves with the ThemingService by setting an attached StyleKey dependency property. The ThemingService then maintains a WeakReference to the FrameworkElement and simply walks down the list of registered elements whenever a new theme is applied.


using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Markup;
using System.Windows.Threading;
using Microsoft.Practices.Composite.Events;
using Microsoft.Practices.Composite.Logging;
using Microsoft.Practices.Composite.Presentation.Events;
using ThemingPlay.Core.Configuration;

namespace ThemingPlay.Core.Theming
{
    public class ThemingService : IThemingService
    {
        #region Private Fields
        private readonly IConfigurationService _configurationService;
        private readonly ConfigurationLoadedEvent _configurationLoadedEvent;
        private readonly ThemesLoadedEvent _themesLoadedEvent;
        private readonly ThemeChangedEvent _themeChangedEvent;
        private readonly ILoggerFacade _logger;
        private readonly Dispatcher _dispatcher;

        private readonly IList<WeakReference> _elementReferences;
        private readonly IList<Theme> _themes;
        private Theme _currentTheme;
        #endregion

        #region Ctor
        public ThemingService(IConfigurationService configurationService,
                              IEventAggregator eventAggregator, Dispatcher dispatcher, ILoggerFacade logger)
        {
            _configurationService = configurationService;
            _configurationLoadedEvent = eventAggregator.GetEvent<ConfigurationLoadedEvent>();
            _configurationLoadedEvent.Subscribe(OnConfigurationLoaded, ThreadOption.UIThread);
            _themesLoadedEvent = eventAggregator.GetEvent<ThemesLoadedEvent>();
            _themeChangedEvent = eventAggregator.GetEvent<ThemeChangedEvent>();
            _dispatcher = dispatcher;
            _logger = logger;
            
            _elementReferences = new List<WeakReference>();
            _themes = new List<Theme>();
        }
        #endregion

        #region Dependency Properties
        public static readonly DependencyProperty StyleKeyProperty = DependencyProperty.RegisterAttached(
            "StyleKey", typeof (string), typeof (ThemingService),
            new PropertyMetadata(OnStyleKeyChanged));

        public static void SetStyleKey(FrameworkElement element, string styleKey)
        {
            element.SetValue(StyleKeyProperty, styleKey);
        }

        public static string GetStyleKey(FrameworkElement element)
        {
            return element.GetValue(StyleKeyProperty) as string;
        }

        private static void OnStyleKeyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var styledElement = (FrameworkElement) d;
            var themingService = SingletonContainer.Instance.Resolve<IThemingService>();
            themingService.RegisterThemedElement(styledElement);
        }
        #endregion

        #region Implementation of IThemingService
        public IList<Theme> Themes
        {
            get { return _themes; }
        }

        public Theme CurrentTheme
        {
            get { return _currentTheme; }
            set
            {
                _currentTheme = value;
                SwitchToTheme(_currentTheme);
                _themeChangedEvent.Publish(_currentTheme);
            }
        }

        public void RegisterThemedElement(FrameworkElement element)
        {
            _elementReferences.Add(new WeakReference(element));
            if (_currentTheme != null && _currentTheme.ResourceDictionary != null)
            {
                ApplyStyleToElement(_currentTheme.ResourceDictionary, element);
            }
        }
        #endregion

        #region Composite Event Handlers
        public void OnConfigurationLoaded(object nothing)
        {
            LoadThemesFromConfiguration();
        }
        #endregion

        #region Private Methods
        private void LoadThemesFromConfiguration()
        {
            var themesConfig = _configurationService[THEMES_CONFIGURATION_SECTION] as ThemesConfigurationSection;
            if (themesConfig != null)
            {
                foreach (var themeConfig in themesConfig.Themes)
                {
                    var theme = CreateTheme(themeConfig);
                    _themes.Add(theme);
                }
                var defaultTheme = (from t in _themes where t.Name == themesConfig.DefaultTheme select t).SingleOrDefault();
                if (defaultTheme != null)
                {
                    this.CurrentTheme = defaultTheme;
                }
                _themesLoadedEvent.Publish(_themes);
            }
        }

        private static Theme CreateTheme(ThemeConfigurationElement themeConfig)
        {
            var theme = new Theme
                        {
                            Name = themeConfig.Name,
                            Description = themeConfig.Description,
                            DictionaryUri = new Uri(themeConfig.DictionaryUri, UriKind.Relative),
                            PackageUri = UriHelper.AbsoluteUriFromRelativePath(themeConfig.PackageUri)
                        };
            return theme;
        }

        private void LoadCurrentThemeAsync(Theme theme)
        {
            var request = WebRequest.Create(theme.PackageUri);
            request.BeginGetResponse(result => _dispatcher.BeginInvoke(()=>
               {
                   try
                   {
                       var response = request.EndGetResponse(result);
                       using (var stream = response.GetResponseStream())
                       {
                           var assemblyParts = XapHelper.GetAssemblyParts(stream);
                           if (assemblyParts.Count == 1)
                           {
                               var assemblyPart = assemblyParts[0];
                               if (XapHelper.LoadAssemblyFromStream(stream, assemblyPart) != null)
                               {
                                   var sri = Application.GetResourceStream(theme.DictionaryUri);
                                   using (var xamlStream = sri.Stream)
                                   {
                                       using (var reader = new StreamReader(xamlStream))
                                       {
                                           var xaml = reader.ReadToEnd();
                                           var resourceDictionary = XamlReader.Load(xaml) as ResourceDictionary;
                                           if (resourceDictionary != null)
                                           {
                                               theme.ResourceDictionary = resourceDictionary;
                                               ApplyTheme(theme);
                                           }
                                       }
                                   }
                               }
                           }
                       }
                   }
                   catch (Exception ex)
                   {
                       _logger.Log(ex.Message, Category.Exception, Priority.High);
                   }
               }), null);
        }

        private void SwitchToTheme(Theme theme)
        {
            if (theme.ResourceDictionary != null)
            {
                ApplyTheme(theme);
            }
            else
            {
                LoadCurrentThemeAsync(theme);
            }
        }

        private void ApplyTheme(Theme theme)
        {
            ICollection<WeakReference> deadElements = null;
            foreach (var elementReference in _elementReferences)
            {
                if (elementReference.IsAlive)
                {
                    var styledElement = elementReference.Target as FrameworkElement;
                    if (styledElement != null)
                    {
                        ApplyStyleToElement(theme.ResourceDictionary, styledElement);
                    }
                }
                else
                {
                    if (deadElements == null)
                    {
                        deadElements = new List<WeakReference>();
                    }
                    deadElements.Add(elementReference);
                }
            }
            if (deadElements != null)
            {
                foreach (var deadElement in deadElements)
                {
                    _elementReferences.Remove(deadElement);
                }
            }
        }

        private void ApplyStyleToElement(ResourceDictionary resourceDictionary, FrameworkElement element)
        {
            var styleKey = GetStyleKey(element);
            if (!string.IsNullOrEmpty(styleKey))
            {
                if (resourceDictionary.Contains(styleKey))
                {
                    var style = resourceDictionary[styleKey] as Style;
                    if (style != null)
                    {
                        try
                        {
                            element.Style = style;
                        }
                        catch (Exception ex)
                        {
                            _logger.Log(ex.Message, Category.Exception, Priority.High);
                        }
                    }
                }
            }
        }
        #endregion

        #region Constants
        private static readonly string THEMES_CONFIGURATION_SECTION = "themes";
        #endregion
    }
}

There’s loads of other code in here to do with Prism and downloading the Themes dynamically, but you get the point…

Often you want to do deep linking in Silverlight without going the whole hog and using the new Navigation Framework. Not that the Navigation Framework is bad, but it does impose a architecture on your application that you may not want to follow.

Instead you can use the HtmlPage.Window.CurrentBookmark to store an arbitrary string. This will change the address in the browser by appending the string as a hash code or anchor. This is great as you can now bookmark the URL by hitting Ctrl+D. Only problem is if a user selects a bookmark while you application is still running then there’s no obvious way to detect the change.

I found a couple of blog entries suggesing ways to do this. One suggested polling on a timer to check for the change. Another used a bunch of JavaScript and didn’t work. As you can imagine, neither of these is a great option. Finally after hours of googling I found that you can do:

HtmlPage.Window.AttachEvent(“onhashchange”, new EventHandler(OnHashChange));

Works like a charm!