如何通过反射获取程序集Themes/Generic.xaml的ResourceDictionary实例
问题原因说明
你用Application.LoadComponent加载得到的是全新的ResourceDictionary副本,和WPF运行时内部正在使用的实例完全独立,所以修改不会生效,单独加载报错是因为缺少运行时依赖的其他资源上下文。
实现方案
方案1:遍历已加载资源树(无需反射)
如果目标Generic.xaml已经被合并到应用资源树中,可以直接遍历匹配:
string targetAssemblyName = "你的目标程序集名称"; ResourceDictionary targetDict = null; // 遍历应用级资源的合并字典 foreach (var dict in Application.Current.Resources.MergedDictionaries) { if (dict.Source != null && dict.Source.OriginalString.Contains(targetAssemblyName) && dict.Source.OriginalString.EndsWith("themes/generic.xaml", StringComparison.OrdinalIgnoreCase)) { targetDict = dict; break; } } // 如果应用级没找到,遍历所有已打开窗口的资源树 if (targetDict == null) { foreach (Window window in Application.Current.Windows) { foreach (var dict in window.Resources.MergedDictionaries) { if (dict.Source != null && dict.Source.OriginalString.Contains(targetAssemblyName) && dict.Source.OriginalString.EndsWith("themes/generic.xaml", StringComparison.OrdinalIgnoreCase)) { targetDict = dict; goto EndSearch; } } } } EndSearch: // 拿到targetDict后直接修改即可
方案2:反射获取WPF内部缓存的实例(推荐)
WPF会把所有加载过的主题资源缓存到内部的ThemeDictionaryCache中,直接从缓存获取的就是正在使用的实例:
using System.Reflection; using System.Collections; using System.Windows; // 替换为目标程序集中的任意公开类型,用来定位目标程序集 Type typeInTargetAssembly = typeof(目标程序集中的某个公开类型); Assembly targetAssembly = typeInTargetAssembly.Assembly; string targetAssemblyName = targetAssembly.GetName().Name; // 获取WPF内部的ThemeDictionary类型 Type themeDictType = typeof(ResourceDictionary).Assembly .GetType("System.Windows.ThemeDictionary"); if (themeDictType == null) return; // 获取静态缓存字段 FieldInfo cacheField = themeDictType.GetField("_themeDictionaryCache", BindingFlags.NonPublic | BindingFlags.Static); if (cacheField == null) return; Hashtable themeCache = (Hashtable)cacheField.GetValue(null); ResourceDictionary targetDict = null; foreach (DictionaryEntry entry in themeCache) { ResourceDictionary dict = (ResourceDictionary)entry.Value; if (dict.Source != null && dict.Source.OriginalString.Contains(targetAssemblyName) && dict.Source.OriginalString.EndsWith("themes/generic.xaml", StringComparison.OrdinalIgnoreCase)) { targetDict = dict; break; } } // 此处拿到的targetDict就是运行时正在使用的实例,直接修改内容即可
注意事项
- 修改资源后,只有通过
DynamicResource引用该资源的控件会自动更新样式,StaticResource引用的资源已经在加载时完成值解析,不会自动生效,需要手动触发对应控件的重加载逻辑。 - 缓存中的实例已经完成了所有资源的上下文加载,不会出现你之前遇到的加载错误问题。
内容的提问来源于stack exchange,提问作者CSharpie
相关产品推荐
相关产品推荐

