WPF控件抛出“URI标识的资源丢失”异常

18
在加载插件并尝试创建“XYZ”控件时,应用程序抛出以下异常:
“用户控件‘XYZ’没有由URI '/ThePluginAssembly;component/XYZ.xaml'标识的资源”,在UserControls构造函数的InitializeComponent()方法中。
关键点如下:
1)用户控件位于插件程序集中。
2)我正在尝试从插件程序集内部创建用户控件。
3)插件与主应用程序位于同一目录中。
4)只有在通过XAML创建UserControl时才会出现问题。我在同一程序集中还有几个其他的UserControl,但是我使用代码实例化这些控件。只有在尝试通过XAML创建UserControl时,我才会收到错误消息。
通过一些谷歌搜索,我意识到当我的插件在应用程序中加载两个实例时会发生这种情况。当我从其中一个文件夹中删除了我的插件(我允许该插件从两个位置加载)时,该异常不再出现。
我的问题:
1)WPF尝试解析URI以加载我的控件的原因是什么?
2)是否有一种方法可以在应用程序中加载两个插件实例,并消除异常?或者有没有一种方式为每个实例创建唯一的URI(如果此异常是由冲突的URI引起的)。
任何评论或参考都将有所帮助。
感谢您的关注。
编辑:
与Phil发布的相同问题:如何强制WPF使用使用程序集强名称的资源URI?Argh!

为什么你需要或者想要两个程序集实例呢?一旦加载了代码,它就可以被重复使用了。 - Grant Thomas
1
不是我需要,而是我可能在机器上安装了两个不同版本的插件。主应用程序将加载每个版本的一个实例,因此有两个版本可用。 - Manish Basantani
请查看我的帖子这里,了解一种通用的设置程序集版本的方法。 - Traummaennlein
6个回答

9
唯一的方法是在URI中包含版本信息,这样XAML加载器才能区分正确的类型。这篇MSDN文章解释了Pack URI格式,版本部分有以下描述:
;Version [可选]:包含资源文件的引用程序集的版本。当加载两个或多个具有相同短名称的引用程序集时使用。
因此,您可以使用以下其中之一:
/ThePluginAssembly;v1.0.0.0;component/XYZ.xaml
/ThePluginAssembly;v1.1.0.0;component/XYZ.xaml

@Tom:感谢您的帖子,它与我的问题有关。但是我无法弄清楚如何指示我的应用程序在尝试解析URI之前附加版本号!!!例如,如果在MainForm中我正在使用AdvancedFilter.xaml,则我应该在哪里指示MainForm使用/Plugin;v.x.x.x.x.;component/AdvancedFilter.xaml??我错过了什么吗?请帮忙。 - Manish Basantani
@Amby:你能发一下用来加载XAML文件的代码吗? - CodeNaked
@Tom:我没有通过指定URI来加载xaml文件。我只是在menuItem_click事件中创建WPF控件。关于加载插件,我使用Assembly.Load(...)进行加载,这会加载我的插件的两个可用版本。另外,请查看我在编辑中提到的相关问题。 - Manish Basantani
@Amby:我明白了。在这种情况下,您唯一的选择就是实现一个自定义的InitializeComponent方法。您可以简单地复制为您生成的方法,并在Uri中插入版本信息。然后,在您的类的构造函数中调用您的版本(例如MyInitializeComponent),而不是为您生成的版本。 - CodeNaked
@Tom:那确实是一个不错的解决方案,但它是机械的。考虑到我已经有了大约30个XAML控件!! 我正在寻找一些可能完成任务的配置,因为.g.cs是由“MSBuild:Compile”生成的,所以我希望能够挂载某些属性/参数来完成工作。 - Manish Basantani
@Amby:很抱歉,目前还没有这样的设置。 - CodeNaked

2

尝试使用Assembly.LoadFrom()代替Assembly.Load()Assembly.LoadFile()

我也遇到了同样的问题:我曾经使用Assembly.LoadFile()来加载程序集。经过多日的搜索,我发现Assembly.LoadFile()Assembly.Load()已被弃用。这两种方法都会在运行时出现问题。所以我使用Assembly.LoadFrom(),它可以正常工作。


1
Assembly.LoadFileAssembly.Load并没有被弃用,只有它们的某些重载被弃用了,就像Assembly.LoadFrom一样。这些方法的行为不同,因此我建议在进行更改之前仔细阅读文档并检查自己的代码,除非您确切地了解它会产生什么影响,否则不应该进行修改。 - Roger Sanders

1
以下说明适用于插件程序集和插件引用的任何非System程序集(复制可能存在于引用的任何级别)。 由于插件程序集位于应用程序可执行目录中,如果您将自己的程序集添加到全局程序集缓存中,请从那里删除它。 检查解决方案中对程序集的所有引用,并将“版本特定”设置为false。 如果插件程序集来自另一个解决方案,并且您在调试/发布或x86 / x64上使用不同版本的组件,则请编辑引用该程序集的.csproj文件,并设置引用路径,例如this example。 考虑取消对插件程序集的引用,改用反射来加载它们-这将消除解决方案对插件的依赖关系。为此,您需要将查找插件中特定元素的任何代码移动到插件程序集本身中,并且仅从原始解决方案访问插件公开的支持在原始解决方案或同时引用原始解决方案和插件的程序集中定义的接口的类型。- 在通过反射访问程序集时,请确保从应用程序可执行目录中加载程序集。

请确保资源的Pack URI格式如下: "pack://application:,,,/ReferencedAssembly;component/Subfolder/ResourceFile.xaml"


0
我曾经遇到过同样的问题,我的UserControls所有引用了与主应用程序xaml相同的程序集。 从UserControls中删除资源链接可以解决这个问题。

0

到目前为止,我找到的最佳解决方案是在项目属性中修改我的插件dll名称,并使其与版本相关。

因此,如果最初它是“prod.myplugin”,现在我已经将其更改为“prod.myplugin_300d3”。在修改项目属性后重新构建项目时,编译器会重新生成.g.cs文件,现在URI中有一个版本(它显示为/prod.myplugin_300d3;component/XYZ.xaml),从而使我的URI在不同版本之间变得唯一。

我仍在寻找更好的自动化解决方案,以便能够修改MSBuild:Compile配置。


0

将bin文件夹中的所有文件复制到与您解决方案中所有项目分开的单独文件夹中,并从每个项目的bin文件夹中删除debug和release文件夹,这样可以解决我的设计师问题。

请记住,下次编译时,所有文件都会重新出现。因此,您必须要有一些后期构建脚本来复制到另一个文件夹并删除bin文件夹中的文件。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接