[Contribution] Loading theme from external assembly

Oct 25, 2013 at 10:02 AM
Edited Oct 25, 2013 at 10:08 AM
Hi
Just want to give a litle contribution for extend M:Ui and themes and allow to load and use an external theme.

So lets go.

We will need a new class called as "Theme" for register the theme name and the theme url.

FirstFloor.ModernUI.Windows.Theme.cs
namespace FirstFloor.ModernUI.Windows
{
    public class Theme
    {
        public string ThemeUrl { get; set; }
        public string ThemeName { get; set; }
    }
}
And an Interface called as "ITheme"

FirstFloor.ModernUI.Windows.ITheme.cs
namespace FirstFloor.ModernUI.Windows
{
    public interface ITheme
    {
        string RegisterThemeUrl();

        string RegisterThemeName();
    }
}
Ok so, now we have to modify a bit our "AppearanceManager" and load external assembly.

FirstFloor.ModernUI.Presentation.AppearanceManager.cs
        private readonly List<Theme> extTheme = new List<Theme>();

        public void LoadThemes(string folder)
        {
            foreach(string file in Directory.EnumerateFiles(folder, "*.dll"))
            {
                Assembly.LoadFrom(file);
                foreach(Assembly a in AppDomain.CurrentDomain.GetAssemblies())
                {
                    foreach(Type t in a.GetTypes())
                    {
                        if(t.GetInterface("ITheme") != null)
                        {
                            try
                            {
                                var instance = Activator.CreateInstance(t) as ITheme;
                                var url = instance.RegisterThemeUrl();
                                var name = instance.RegisterThemeName();

                                var theme = new Theme {ThemeName = name, ThemeUrl = url};

                                if(extTheme.Find(i => i.ThemeName == theme.ThemeName) == null)
                                {
                                    extTheme.Add(theme);
                                }
                            }
                            catch(Exception ex)
                            {
                                Console.WriteLine("Exception on loading assembly : {0}", ex.Message);
                            }
                        }
                    }
                }
            }
        }

        public List<Theme> ExternalTheme()
        {
            return extTheme;
        }
FirstFloor.ModernUI can now load assembly and register a theme.
Yeah its good but how can we use it ?
Its very simple, in fisrt we have to load assemblies from a specific folder.

So in your project edit your App.xaml.cs
        protected override void OnStartup(StartupEventArgs e)
        {
            AppearanceManager.Current.LoadThemes("YourThemeFolder");
            base.OnStartup(e);
        }
And populate our combobox with the new theme

Edit your SettingsAppearanceViewModel.cs and add this code in contructor
            var extSource = AppearanceManager.Current.ExternalTheme();
            foreach(var theme in extSource)
            {
                themes.Add(new Link {DisplayName = theme.ThemeName, Source = new Uri(theme.ThemeUrl, UriKind.Relative)});
            }
Its ok, we have now a simple loader for an extenal theme and we can use it.
We have to create an assembly with the theme.

So, lets go again ...
1/ create a new assembly project
2/ add needed references (FirstFloor.ModernUI too)
3/ Create a class and inherited this one from ITheme like
    public class HelloKittyTheme : ITheme
    {
        public string RegisterThemeUrl()
        {
            var assName = Assembly.GetExecutingAssembly().FullName.Split(',')[0].Trim();
            var url = string.Format(@"/{0};component/Assets/ModernUI.HelloKitty.xaml", assName);
            return url;
        }

        public string RegisterThemeName()
        {
            return "HelloKitty";
        }
    }
4/ Create a folder /Assets
5/ Put your ModernUI.HelloKitty.xaml inside
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="/FirstFloor.ModernUI;component/Assets/ModernUI.Light.xaml" />
    </ResourceDictionary.MergedDictionaries>

    <Color x:Key="AccentColor">#f472d0</Color>

    <Rectangle x:Key="WindowBackgroundContent" x:Shared="false" Margin="32">
        <Rectangle.Fill>
            <ImageBrush Opacity=".1" ImageSource="/AssemblyName;component/Assets/background.kitty.png" Stretch="None"  AlignmentX="Right" AlignmentY="Bottom" />
        </Rectangle.Fill>
    </Rectangle>

</ResourceDictionary>
6/ If your theme contain a picture put it in the same folder(Do not forget to change the build action on your picture => right click on picture => build action => change it to "Resource")
7/ Compil and put your assembly file in your theme folder.

All its done.
This is a simple code and can be improved, but its a good start for allow your Application users to create a theme without knowledge