I dynamically load app resource dictionaries (2 of 3 loaded at a time):
If I normally change the MergedDictionaries
property's value when the main window is already Loaded
, I get an exception (call stack here):
System.InvalidOperationException: 'Cannot call StartAt when content generation is in progress.'
If I change the MergedDictionaries
property's value using Dispatcher.BeginInvoke
, when I use a resource in code-behind from (1), it says through an exception that it is not loaded yet (like using a resource through StaticResource
that does not exist).
I do not want to use an App.xaml file because, for having a single-instance application, I use a class that inherits from Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase
and calls the code in my App.cs file.
I may call the LoadTheme method in a few places in the application code and I want to make it stable.
(no XAML)
public class App : System.Windows.Application
{
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
LoadTheme(AppTheme.Light);
var w = new MainWindow();
ShutdownMode = ShutdownMode.OnMainWindowClose;
MainWindow = w;
w.Show();
}
internal ResourceDictionary MLight = null,
MDark = null,
MMain = null;
internal ResourceDictionary GetLightThemeDictionary()
{
if (MLight == null)
{
MLight = new ResourceDictionary() { Source = new Uri("Themes/Light.xaml", UriKind.Relative) };
}
return MLight;
}
internal ResourceDictionary GetDarkThemeDictionary()
{
if (MDark == null)
{
MDark = new ResourceDictionary() { Source = new Uri("Themes/Dark.xaml", UriKind.Relative) };
}
return MDark;
}
internal ResourceDictionary GetMainDictionary()
{
if (MMain == null)
{
MMain = new ResourceDictionary() { Source = new Uri("AppResources.xaml", UriKind.Relative) };
}
return MMain;
}
internal void LoadTheme(AppTheme t)
{
//Dispatcher.BeginInvoke(new Action(() =>
//{
if (Resources.MergedDictionaries.Count == 2)
{
switch (t)
{
case AppTheme.Dark:
Resources.MergedDictionaries[1] = GetDarkThemeDictionary();
break;
default:
Resources.MergedDictionaries[1] = GetLightThemeDictionary();
break;
}
}
else if (Resources.MergedDictionaries.Count == 1)
{
switch (t)
{
case AppTheme.Dark:
Resources.MergedDictionaries.Add(GetDarkThemeDictionary());
break;
default:
Resources.MergedDictionaries.Add(GetLightThemeDictionary());
break;
}
}
else
{
Resources.MergedDictionaries.Clear();
Resources.MergedDictionaries.Add(GetMainDictionary());
LoadTheme(t);
}
//}), System.Windows.Threading.DispatcherPriority.Normal); // how to process this after the ItemsControl has generated its elements?
}
}
I tried to make a test example but I failed - I created a program that works, because I set the ItemsControl.ItemsSource
every time the template is applied. In my actual project, I set ItemsSource
through data binding and sometimes manually, but I am not sure this is what misses from my actual project.
I use .NET Framework 4.7.2, VS 2019, Win 10 Pro.
Thank you.
The OnStartup
method now looks like this (I just loaded the base dictionary and one of the themes before constructing the MainWindow
):
protected override void OnStartup(StartupEventArgs e)
{
base.OnStartup(e);
Resources.MergedDictionaries.Clear();
Resources.MergedDictionaries.Add(GetMainDictionary());
Resources.MergedDictionaries.Add(GetLightThemeDictionary());
LoadTheme(AppTheme.Light);
var w = new MainWindow();
ShutdownMode = ShutdownMode.OnMainWindowClose;
MainWindow = w;
w.Show();
}
And I uncommented the 2 comments (that make use of the Dispatcher):
internal void LoadTheme(AppTheme t)
{
Dispatcher.BeginInvoke(new Action(() =>
{
[...]
}), System.Windows.Threading.DispatcherPriority.Normal);
}