如何在 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 个):
- 一个基础资源字典,总是
- 一个Light.xaml主题文件
- 一个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);
}
我动态加载应用程序资源字典(一次加载 3 个中的 2 个):
- 一个基础资源字典,总是
- 一个Light.xaml主题文件
- 一个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);
}