如何在 ItemsControl 生成其容器之前或之后更改加载的主题资源字典,但不是在那个时候?

How to change the loaded theme resource dictionary before or after an ItemsControl is generating its containers, but not in that time?

我动态加载应用程序资源字典(一次加载 3 个中的 2 个):

  1. 一个基础资源字典,总是
  2. 一个Light.xaml主题文件
  3. 一个Dark.xaml主题文件

如果我通常在主 window 已经是 Loaded 时更改 MergedDictionaries 属性 的值,我会得到一个异常(调用堆栈 here):

System.InvalidOperationException: 'Cannot call StartAt when content generation is in progress.'

如果我使用 Dispatcher.BeginInvoke 更改 MergedDictionaries 属性 的值,当我在 (1) 的代码隐藏中使用资源时,它通过异常说明它尚未加载(例如通过 StaticResource 使用不存在的资源)。

我不想使用 App.xaml 文件,因为对于单实例应用程序,我使用 class 继承自 Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase 并调用我的代码App.cs 文件。

我可能会在应用程序代码中的几个地方调用 LoadTheme 方法,我想让它稳定。

App.cs

(没有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?
    }
}

我试图制作一个测试示例但我失败了 - 我创建了一个有效的程序,因为每次应用模板时我都设置了 ItemsControl.ItemsSource。在我的实际项目中,我通过数据绑定设置 ItemsSource,有时手动设置,但我不确定这是我实际项目中遗漏的。

我使用.NET Framework 4.7.2、VS 2019、Win 10 Pro。

谢谢。

OnStartup 方法现在看起来像这样(在构造 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();
}

我取消了 2 条评论(使用 Dispatcher):

internal void LoadTheme(AppTheme t)
{
    Dispatcher.BeginInvoke(new Action(() =>
    {
        [...]
    }), System.Windows.Threading.DispatcherPriority.Normal);
}