如何从动态加载的DLL的XAML文件获取ResourceDictionary并添加至应用
Avalonia动态加载DLL中样式和资源的正确方式
问题场景
已通过以下代码成功动态加载包含自定义Avalonia控件的Components.dll并使用控件:
var basePath = Directory.GetCurrentDirectory(); var dll = Assembly.LoadFile(Path.Combine(basePath, "Components.dll")); foreach (var type in dll.GetExportedTypes()) { if (type.IsAssignableTo(typeof(IComponentPlugin))) { var instance = Activator.CreateInstance(type); if (instance is IComponentPlugin plugin) { // Do Stuff } } }
但尝试添加DLL中的默认样式和资源时遇到两类错误:
- 使用
ResourceInclude/StyleInclude时,报错:No precompiled XAML found for avares://DefaultResources.axaml/ (baseUri: avares://DefaultResources.axaml/) - 使用
AvaloniaRuntimeXamlLoader加载时,报错:The resource avares://DefaultResources.axaml/ could not be found.
解决方案
1. 先确保资源文件的构建配置正确
在Components.dll的项目中,修改DefaultResources.axaml和DefaultStyle.axaml的属性:
- 构建操作:设置为
AvaloniaResource - 复制到输出目录:设置为
如果较新则复制或始终复制
这一步是基础,否则资源不会被正确嵌入到DLL中。
2. 推荐:使用预编译XAML加载(性能更优)
修改IComponentPlugin的方法,传入动态加载的程序集,基于程序集名称构造正确的avares URI:
public ResourceInclude? GetResources(Assembly assembly) { // 基于加载的程序集构造基础URI var baseUri = new Uri($"avares://{assembly.GetName().Name}/"); // 拼接资源文件的相对路径(如果文件在子文件夹,比如Resources/DefaultResources.axaml,就改成对应路径) var resourceUri = new Uri(baseUri, "DefaultResources.axaml"); return new ResourceInclude(baseUri) { Source = resourceUri }; } public StyleInclude? GetStyle(Assembly assembly) { var baseUri = new Uri($"avares://{assembly.GetName().Name}/"); var styleUri = new Uri(baseUri, "DefaultStyle.axaml"); return new StyleInclude(baseUri) { Source = styleUri }; }
主程序调用时传入加载的DLL程序集,并添加到Application:
var basePath = Directory.GetCurrentDirectory(); // 推荐用LoadFrom替代LoadFile,自动处理依赖项 var dll = Assembly.LoadFrom(Path.Combine(basePath, "Components.dll")); foreach (var type in dll.GetExportedTypes()) { if (type.IsAssignableTo(typeof(IComponentPlugin))) { var instance = Activator.CreateInstance(type); if (instance is IComponentPlugin plugin) { // 添加资源到应用程序资源字典 var resources = plugin.GetResources(dll); if (resources != null) { Application.Current.Resources.MergedDictionaries.Add(resources); } // 添加样式到应用程序样式集合 var style = plugin.GetStyle(dll); if (style != null) { Application.Current.Styles.Add(style); } } } }
3. 备选:运行时加载XAML(适合未预编译的场景)
如果必须使用AvaloniaRuntimeXamlLoader,直接从动态加载的程序集中读取资源流(避免AssetLoader的识别问题):
// 先确保已安装Avalonia.Markup.Xaml.Loader包 public ResourceDictionary? GetResources(Assembly assembly) { // 资源的完整名称格式:程序集名称.文件路径(斜杠替换为点) // 示例:如果文件在根目录,就是Components.DefaultResources.axaml;如果在Resources文件夹,就是Components.Resources.DefaultResources.axaml var resourceFullName = $"{assembly.GetName().Name}.DefaultResources.axaml"; using var stream = assembly.GetManifestResourceStream(resourceFullName); if (stream == null) return null; return AvaloniaRuntimeXamlLoader.Load<ResourceDictionary>(stream); } public Styles? GetStyle(Assembly assembly) { var resourceFullName = $"{assembly.GetName().Name}.DefaultStyle.axaml"; using var stream = assembly.GetManifestResourceStream(resourceFullName); if (stream == null) return null; return AvaloniaRuntimeXamlLoader.Load<Styles>(stream); }
主程序调用时添加到Application:
var resources = plugin.GetResources(dll); if (resources != null) { Application.Current.Resources.MergedDictionaries.Add(resources); } var styles = plugin.GetStyle(dll); if (styles != null) { Application.Current.Styles.AddRange(styles); }
常见坑点
- 不要混淆DLL文件名(比如
Components.dll)和程序集名称:程序集名称是项目属性中设置的AssemblyName,必须和avaresURI中的名称一致 - 动态加载时优先用
Assembly.LoadFrom,Assembly.LoadFile不会自动加载依赖程序集 - 资源文件的路径要准确,子文件夹下的文件需要在URI或资源全名中体现层级
内容的提问来源于stack exchange,提问作者cboittin
相关产品推荐
相关产品推荐

