I dynamically load app resource dictionaries (2 of 3 loaded at a time):
- a base resource dictionary, always
- a Light.xaml theme file
- a Dark.xaml theme file
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.
App.cs
(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
OnStartupmethod now looks like this (I just loaded the base dictionary and one of the themes before constructing theMainWindow):And I uncommented the 2 comments (that make use of the Dispatcher):