Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XAML: How can I change the source of a specific ResourceDictionary

<Application x:Class="CDesign.App"
         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
         xmlns:local="clr-namespace:CDesign"
         StartupUri="MainWindow.xaml">
<Application.Resources>
    <ResourceDictionary x:Name="ThemeDictionary">
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="pack://application:,,,/AppStyles;component/Resources/Icons.xaml"/>
            <ResourceDictionary Source="pack://application:,,,/AppStyles;component/Resources/IconsNonShared.xaml"/>
            <!-- MahApps.Metro resource dictionaries. Make sure that all file names are Case Sensitive! -->
            <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
            <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
            <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colors.xaml" />
            <!-- Accent and AppTheme setting -->
            <ResourceDictionary x:Uid="Accents" x:Name="Accents" Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/Blue.xaml" />
            <ResourceDictionary x:Uid="BaseTheme" x:Name="BaseTheme" Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseDark.xaml" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

How can I change the source of a specific ResourceDictionary inside ResourceDictionary.MergedDictionaries based on the ResourceDictionary's x:Name/x:Uid?

Thanks.

like image 402
Mai Le Nhat Khanh Avatar asked Mar 18 '17 16:03

Mai Le Nhat Khanh


1 Answers

I don't think you can do that based on x:Name or x:Uid. ResourceDictionary does not define mappings for those markup properties. For example, UIElement is marked with UidPropertyAttribute("Uid") and so UIElement marked with x:Uid attribute will have that value available as Uid property. Same story with x:Name. But ResourceDictionary does not define such mappings, and so those properties are effectively lost after xaml is parsed and compiled.

Now, what you can do instead? One option that comes to mind is to use your own attached property to assign resource dictionary an identifier. Unfortunately, ResourceDictionary does not inherit from DependencyObject, and so we cannot use attached properties on it.

However, there is one hack with which we can abuse attached property syntax and still reach the goal. Let's define fake attached property like this:

public static class ResourceDictionaryExtensions {   
    private static readonly Dictionary<ResourceDictionary, string> _mapping = new Dictionary<ResourceDictionary, string>();
    public static void SetName(ResourceDictionary element, string value) {
        _mapping[element] = value;
    }

    public static string GetName(ResourceDictionary element) {
        if (!_mapping.ContainsKey(element))
            return null;
        return _mapping[element];
    }
}

Note that this definition is different from usual attached property. First, there is no attached property at all. Second, two methods GetName and SetName do not accept DependencyObject (like methods associated with attached properties do), but ResourceDictionary. However, because we have GetName and SetName methods - we can use attached property syntax, like this:

<Application.Resources>
    <ResourceDictionary x:Name="ThemeDictionary">
        <ResourceDictionary.MergedDictionaries>
            <ResourceDictionary Source="pack://application:,,,/AppStyles;component/Resources/Icons.xaml"/>
            <ResourceDictionary Source="pack://application:,,,/AppStyles;component/Resources/IconsNonShared.xaml"/>
            <!-- MahApps.Metro resource dictionaries. Make sure that all file names are Case Sensitive! -->
            <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Controls.xaml" />
            <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Fonts.xaml" />
            <ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Colors.xaml" />
            <!-- Accent and AppTheme setting -->
            <ResourceDictionary local:ResourceDictionaryExtensions.Name="Accents" Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/Blue.xaml" />
            <ResourceDictionary local:ResourceDictionaryExtensions.Name="BaseTheme" Source="pack://application:,,,/MahApps.Metro;component/Styles/Accents/BaseDark.xaml" />
        </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
</Application.Resources>

Even though target object (ResourceDictionary) is not dependency object, and that property is not attached property at all.

Now it's easy to modify source of target dictionary:

var dict = Application.Current.Resources.MergedDictionaries.First(c => ResourceDictionaryExtensions.GetName(c) == "Accents");
dict.Source = new Uri("path to the new dictionary");
like image 66
Evk Avatar answered Nov 05 '22 04:11

Evk