WPF: externe Style Dateien dynamisch laden

Bei manche Anwendungen kommt es vor, dass man z. B. für bestimmte Kunden, Dinge wie Schrift-Art/-Größe, Farben, Dimensionen, Bilder, Icons etc. einfach und leicht („low ceremony“) ändern möchte, ohne den Quellcode jedes Mal zu ändern.

Dies erreicht man, indem man diese „Styles“ in einer externer „Styles.xaml“ Datei auslagert, zur Laufzeit, noch bevor das Hauptfenster erzeugt wird, ladet, und damit alle oder teile der Styles überschreibt. Vorausgesetzt die „Styles“ Datei ist als Ressource, also als Datei in dem Anwendungs-Verzeichnis deklariert (siehe Properties) und zu finden.

private void LoadCustomStyles()
{
    if (File.Exists("CustomStyles.xml"))
    {
        try
        {
            using (var fileStream = new System.IO.FileStream("CustomStyles.xml", FileMode.Open, FileAccess.Read, FileShare.Read))
            {
                var dictionary = (ResourceDictionary)System.Windows.Markup.XamlReader.Load(fileStream);
                Application.Current.Resources.MergedDictionaries.Clear();
                Application.Current.Resources.MergedDictionaries.Add(dictionary);
            }
        }
        catch(Exception ex)
        {
            Logger.Error(ex);
        }
    }
}

Dabei könnte die „CustomStyles.xaml“ Datei folgende Dinge enthalten:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
                    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
                    xmlns:clr="clr-namespace:System;assembly=mscorlib"
                    xmlns:logo="clr-namespace:Acme.Application.Icons"
                    mc:Ignorable="d">
    
    <ResourceDictionary.MergedDictionaries>
        <!-- Loading styles from a library -->
        <ResourceDictionary Source="pack://application:,,,/Acme.Some.Library;component/Styles.xaml" />
    </ResourceDictionary.MergedDictionaries>

    <!-- Defining a string -->
    <clr:String x:Key="PathToSomeIcon">../Icons/file.png</clr:String>

    <!-- Defining an image -->
    <Image x:Key="ImageCompanyLogo" Source="pack://application:,,,/Icons/acme_logo.png" Width="100" Height="120" />

    <!-- Defining a brush -->
    <SolidColorBrush x:Key="BrushForBorders" Color="#FFB400"   />

    <!-- Defining a color -->
    <Color x:Key="Color1">#FFABCDEF</Color>
    <Color x:Key="Color2">#FF123456</Color>

    <!-- Defining a linear gradiant -->
    <LinearGradientBrush x:Key="BorderWithGradiant" EndPoint="0.5,1" StartPoint="0.5,0">
        <GradientStop Color="{DynamicResource Color1}" Offset="0" />
        <GradientStop Color="{DynamicResource Color2}" Offset="0.5" />
        <GradientStop Color="{DynamicResource Color1}" Offset="1" />
    </LinearGradientBrush>

    <!-- Defining style for specific control -->
    <Style x:Key="MySpecialLabel" TargetType="{x:Type Label}">
        <Setter Property="Background"          Value="White"/>
        <Setter Property="FontSize"            Value="16"/>
        <Setter Property="FontWeight"          Value="Bold"/>
        <Setter Property="Margin"              Value="5"/>
        <Setter Property="VerticalAlignment"   Value="Center"/>
        <Setter Property="HorizontalAlignment" Value="Center"/>
    </Style>
</ResourceDictionary>