Search code examples
c#wpfxamlmvvmresourcedictionary

Loading a ResourceDictionary via binding from App.xaml?


I have a WPF application that we need to reskin depending on a configuration file setting. The simple version of the App.xaml is:

App.xaml

<Application>
  ...
  <Application.Resources>
    <ResourceDictionary x:Key="NormalMain">
        <ImageBrush x:Key="BackgroundBrush" ImageSource="Images/welcome.png"/>
    </ResourceDictionary>
    <ResourceDictionary x:Key="AlternateMain">
        <SolidColorBrush x:Key="BackgroundBrush" Color="LightGreen"/>
    </ResourceDictionary>
  </Application.Resources>
  ...
</Application>

I want to load the resourcedictionary at runtime based on a config file setting. I've already hooked up the binding and know that it works, but I can't determine a way to merge this specific dictionary in.

MainWindowView.xaml

<Window 
    (...)
    d:DataContext="{d:DesignInstance designer:DesignMainWindowViewModel, IsDesignTimeCreatable=True}"
    Background="{StaticResource BackgroundBrush}">
    ...
    <Window.Resources>
      //Load dictionary based on binding here?
    </Window.Resources>
</Window>

Where the MainWindowViewModel is something simple like:

MainWindowViewModel.cs

public class MainWindowViewModel {
  public bool IsAlternate {get;set;}
}

Is there a way to do this properly?

Edit: I don't want to do the loading in codebehind and PACK syntax does not seem like what the engine was built for in this case.

Edit 2: I'm a bit close using this code in the App.xaml:

    <StaticResource ResourceKey="{Binding Path=IsAlternate, Converter={StaticResource BoolToResourceKeyId}}" />

But it causes a bunch of weird errors to crop up in my view when I have that.


Solution

  • After a few more hours of fighting, I found a different way of handling the situation.

    I created two styles for the control I was working in app.xaml:

    App.xaml

       ...
        <valueConverters:IsMainToStyleConverter x:Key="IsMainToStyleConverter" />
        <Style x:Key="Main" TargetType="Window">
            <Setter Property="Background" Value="{StaticResource MainBackgroundBrush}" />
        </Style>
        <Style x:Key="Alternate" TargetType="Window">
            <Setter Property="Background" Value="{StaticResource AlternateBackgroundBrush}" />
        </Style>
       ...
    

    Then defined a converter that could be used to locate the styles:

    IsMainToStyleConverter.cs

    public class IsMainToStyleConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return Application.Current.TryFindResource((value is bool && ((bool)value) ? "Main" : "Alternate"));
        }
    
        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            return Binding.DoNothing;
        }
    }
    

    And fixed up my MainWindowView:

    MainWindowView.xaml

    <Window 
       ...
       d:DataContext="{d:DesignInstance designer:DesignMainWindowViewModel, IsDesignTimeCreatable=True}"
       Style="{Binding IsMain, Converter={StaticResource IsMainConverter}}">
       ...
    </Window>
    

    Now as long as my viewmodel has the right property, the converter handles locating the style resources and changing over to it without having to worry about transcending pattern concerns or anything like that.