组件没有 uri 标识的资源

Posted

技术标签:

【中文标题】组件没有 uri 标识的资源【英文标题】:The component does not have a resource identified by the uri 【发布时间】:2011-11-30 13:44:56 【问题描述】:

我想创建一个通用 DataGrid 以在我的所有视图/用户控件上使用。

这是我的结构:

Class Library"Core"

Class"ViewBase"

public class ViewBase : UserControl

    public ViewBase()
    
       

    //Rest of Methods and Properties

Class Library"Controls"

UserControl 调用"GridView"

XAML:

    <vb:ViewBase x:Class="Controls.GridView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:vb="clr-namespace:Core;assembly=Core">

    <Grid>
        <DataGrid></DataGrid>
    </Grid>

    </vb:ViewBase>

代码背后:

using Core;

public partial class GridView : ViewBase

    public GridView ()
    
        InitializeComponent();
    

然后是名为“WPFApp”的 WPF 应用程序:

Class"View"

using Controls;

public class View : GridView

    public View()
    
        InitializeComponent();
    

我的整个想法是在需要DataGrid 的地方使用GridView

当我运行应用程序时出现此错误:

"The component 'WpfApp.View' does not have a resource identified by the URI '/Controls;component/GridView.xaml'."

我做错了什么?

这是正确的方法还是我离题了?

【问题讨论】:

【参考方案1】:

@Willem,这对我来说似乎完全没问题。事实上,我试过这个,它在我的情况下有效。我使用ListBox 而不是DataGrid(但这不重要)。

我所有的命名空间都在一个程序集中。所以我为所有人使用了一个共同的父命名空间,例如

MyWpfApplication.Controls MyWpfApplciation.GridView MyWpfApplciation.ViewBase

因为所有这些 ControlsGridViewViewBase 都与现有的基于 SystemSystem.Windows.Controls 的命名空间和类声明发生冲突。所以我确保在我的项目中引用了正确的 MyWpfApplication.*

【讨论】:

感谢您的回复。事情就是这样,我知道当一切都在一个组件中时它可以工作,但是将它移到我得到的结构中然后它就失败了......我需要它在上述结构中工作。【参考方案2】:

我正在做一些非常相似的事情,但结果相同。我有一个 C# 类库,其中包含一个名为 UsageControl 的 WPF 控件(带有随附 xaml.cs 文件的 xaml)。在一个单独的 C# 项目(即单独的 dll)中,我创建了一个 C# class CPUUsageControl,它从 UsageControl继承,但对其进行了自己的调整。当我尝试在我的一个视图上使用 CpuUsageControl 时,我遇到了与您相同的错误。

我在单独的程序集中解决了这个问题,我没有创建一个从基本控件继承的类,而是创建了一个包含基本控件的新 WPF 控件。然后,我将 CpuUsage 类中包含的所有逻辑放入 WpfCpuUsageControl 后面的代码中。我能够使用这个对象是我所有的其他控件都很好。

对于您的控件“GridView”,我将创建一个新的 WPF 用户控件,将其命名为 GridView 并使其包含一个“ViewBase”作为 Grid 控件的内容.Inside ViewBase的内容放在你的DataGrid中,像这样:

<UserControl....>
    <Grid>
        <ViewBase name="vBase">
            <DataGrid name="dGrid" />
        </ViewBase>
    </Grid>
</UserControl>

我也不清楚您需要 ViewBase 直接从 UserControl 继承。如果您想要的只是让您的控件具有某些属性和方法,为什么不只是创建一个 BaseControl 类(除了对象之外不从任何人继承)并让未来的控件继承自它。也许你需要一个抽象基类或接口。

对于 MVVM WPF 项目,我通常有一个为我实现 INotifyPropertyChanged 的​​ BaseViewModel,因此我不必在任何地方都执行相同的代码。

祝你好运,我知道这个问题很难解决。异常消息和google最没用!

【讨论】:

感谢 Steven 的回复。有没有办法让它与继承自GridView 的类一起使用?采用这种方法的原因是我们有 100 个需要 DataGrid 的视图。我不想为我需要创建的每个新视图创建一个新的用户控件。我只想从 GridView 继承并设置新的DataContext。这是我在整个设计背后的想法。你建议我应该做些什么或改变什么来获得这种行为? 您也可以尝试从接口继承 DataGrid 和 IMPLEMENT(其中包含您想要的属性和方法原型)。如果我们在 C# 中有多重继承,你可以从 DataGrid 和一些 BaseControl 继承:\【参考方案3】:

您收到此错误的原因是因为 InitializeComponent 的实现方式(在 VS 2010 中)将始终在派生类的程序集中搜索。

这里是初始化组件:

/// <summary>
/// InitializeComponent
/// </summary>
[System.Diagnostics.DebuggerNonUserCodeAttribute()]
[System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "4.0.0.0")]
public void InitializeComponent() 
    if (_contentLoaded) 
        return;
    
    _contentLoaded = true;
    System.Uri resourceLocater = new System.Uri("/WpfApplication1;component/mainwindow.xaml", System.UriKind.Relative);

    #line 1 "..\..\..\MainWindow.xaml"
    System.Windows.Application.LoadComponent(this, resourceLocater);

    #line default
    #line hidden

它查找 XAML 资源的行是 System.Windows.Application.LoadComponent(this, resourceLocator)。这很可能会失败,因为等效于 'this.GetType().Assembly' 用于确定要搜索由相对 Uri 标识的资源的程序集。而'this.GetType()'确实得到了对象的派生类型,而不是实现代码的类的类型。

PS。这是一个错误吗?我不知道...

【讨论】:

【参考方案4】:

我通过放置解决了这个问题

myusercontrol = Activator.CreateInstance<myusercontrol>(); 

InitializeComponent();行之前包含用户控件的窗口的构造函数中

【讨论】:

【参考方案5】:

令人沮丧的是,我确实遇到了这个错误,并且花了很长时间试图找出原因。对我来说,它曾经可以工作,但后来我对派生控件的 XAML 进行了一些非常小的更改,编译器开始给出该错误消息。 简短的解决方案,减少了数小时的尝试:关闭 Visual Studio 并重新打开它,重新编译,问题神奇地消失了! (这是VS2012 Pro) 只是添加了这个,以防任何阅读者绕着圈子试图找到他们的代码不存在的问题。可能值得先尝试“IT Crowd 解决方案”。

【讨论】:

是的,设计师又搞糊涂了。 与 VS2013 相同 - 只需关闭并重新打开。我不知道其他人,但我只在设计器中得到这个错误,而不是在我运行应用程序时。最初的提问者谈到了运行应用程序,但我猜这个问题的很多访问者在设计时都会收到这个错误 不幸的是,对于这种程序,我觉得“关闭并重新打开”的发生频率太高了。不仅仅是这个问题,还有很多其他问题。 比退出整个 VS 应用程序更快的解决方案就是杀死设计器并重新启动它。在任务管理器中终止 XDesProc 进程,然后单击 VS 中显示的重新加载链接。 刚刚与 VS2015 更新 3 相同。非常感谢!!!!!!你节省了我很多时间和头痛 :-) !【参考方案6】:

我也遇到了这个问题,没有任何继承问题。我只是在引用一个包含对话框的 DLL,并尝试创建和显示该对话框。 我有从特定文件夹加载程序集的程序集解析器,事实证明我已经在 VS 中添加了引用并且没有关闭 Copy Local。长话短说:我的进程加载了同一个 DLL 的两个版本。这似乎使 WPF(或运行时)感到困惑。一旦我清除了 Copy Local 并删除了额外的 DLL 副本,它又可以正常工作了。

【讨论】:

【参考方案7】:

我在使用 Visual Studio 2013 时收到了同样的错误。

组件没有 uri 标识的资源

试过了: 清理和重建解决方案 - 没有用。 关闭和打开 Visual Studio - 不起作用。

解决方案: 进入项目bin 目录并清除所有文件。 再次运行项目并运行良好。

打开 Package Manager Console,它将在您的解决方案的根目录中打开并运行以下 powershell 命令:

Get-ChildItem -inc bin,obj -recurse | Remove-Item -recurse -force -EA SilentlyContinue

【讨论】:

【参考方案8】: 删除obj文件夹 删除bin文件夹 重建解决方案

为我工作!

此外,如果您正在使用 Assembly.LoadFile 加载程序集,请查看 AppDomain.CurrentDomain.GetAssemblies() 以获取当前 AppDomain 中的重复程序集。因为在 WPF UserControl 的自动生成代码中,组件将使用其相对 URI 加载。而且由于当前 AppDomain 中有重复的程序集,应用程序不知道该使用哪一个。

【讨论】:

在我的例子中,我点击了 Build -> Clean Solution,然后是 Build -> Build Solution (F7)【参考方案9】:

重命名 xaml 文件后出现此错误。反转重命名解决了这个问题。

此外,我发现 App.xaml 中对 xaml 文件名的引用未更新(StartupUri),但将其重命名为当前名称并不能解决问题(但也许它对您有用)。基本上,我无法重命名 xaml 文件。

仅供参考,对我来说,错误中“抱怨”的组件是 SplitComboBox。

【讨论】:

【参考方案10】:

同样的问题。

短版:

复制本地必须设置为False

长版:

我们开发了一个 WPF 解决方案(MVVM,20 个项目)并实现了一个插件系统。我们的 /bin/Debug 目录包含可执行文件、一些 dll 文件和一个包含插件的插件目录。 有一个项目“DialogLib”(类库,一种对话框)定义了一个窗口(视图)、ViewModel、Model 和一些接口。其中一个插件使用了 DialogLib 的接口之一。窗口本身由主应用程序打开。

要在插件中使用“DialogLib”库的接口,我们必须将 DialogLib 的项目引用添加到插件项目引用中。当应用程序启动时,插件被加载。如果用户随后选择了一个菜单项,则该窗口应该打开。此时,当后面的 windows 代码尝试执行其 InitializeComponent() 时,出现错误“...组件没有 URI 标识的资源...”。

问题出在哪里?

问题是,当我们构建解决方案时,VS 已经正确创建了 DialogLib.dll 并将其复制到 /bin/Debug/。这是因为主应用程序文件要打开窗口。但 DialogLib.dll 也被复制到 /bin/Debug/plugins,因为其中一个插件引用它以使用 DialogLib.dll 中定义的接口之一。那又怎样?

当插件在运行时加载时,它使用 /bin/Debug/plugins/DialogLib.dll 中定义的接口。并且主应用程序文件尝试打开 /bin/Debug/DialogLib.dll 中定义的窗口。尽管文件相同,但 VS 遇到了麻烦。设置插件引用的DialogLib引用属性Copy Local的值,可以避免将DialogLib.dll复制到/bin/Debug/plugins,从而解决问题。

我们在另一个项目中遇到了类似的相同问题(但错误不同),我们想使用在 dll 文件、插件和主应用程序中定义的类型 TypeACopy Local 设置为 true 导致 dll 文件的副本位于 ../bin/Debug/plugins 和 ../bin/Debug/.原来,即使是同一个dll文件,主app文件中的TypeA和插件中的TypeA却被分别视为不同的类型,可以不被交换。

【讨论】:

【参考方案11】:

当我在两个解决方案中打开同一个项目时,会发生这种情况。在一个项目中修改基础控件会导致另一个项目出现此问题。如果关闭和打开都不起作用,则删除“C:\Users...\AppData\Local\Microsoft\VisualStudio\12.0\Designer\ShadowCache”中的所有文件夹

【讨论】:

【参考方案12】:

比关闭所有 Visual Studio 更快的是在任务管理器中杀死 XDescProc.exe。

XDescProc 是设计者。该过程关闭的那一刻,您将在 Visual Studio 中看到重新加载设计器链接。单击该按钮,XDes 将再次启动,您的“无资源”错误应该消失了。

这是您终止设计器进程后 Visual Studio 显示的链接:

【讨论】:

【参考方案13】:

你可以试试这个方法

我创建了自己的InitializeComponent(),并以这种方式调用

this.LoadViewFromUri("/NameOfProject;component/mainwindow.xaml");


public static void LoadViewFromUri(this Window window, string baseUri)
    
        try
        
            var resourceLocater = new Uri(baseUri, UriKind.Relative);
            var exprCa = (PackagePart)typeof(Application).GetMethod("GetResourceOrContentPart", BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, new object[]  resourceLocater );
            var stream = exprCa.GetStream();
            var uri = new Uri((Uri)typeof(BaseUriHelper).GetProperty("PackAppBaseUri", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null, null), resourceLocater);
            var parserContext = new ParserContext
            
                BaseUri = uri
            ;
            typeof(XamlReader).GetMethod("LoadBaml", BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, new object[]  stream, parserContext, window, true );

        
        catch (Exception)
        
            //log
        
    

【讨论】:

【参考方案14】:

我通过重命名/复制操作意外删除用户控件。当我从版本控制中恢复项目文件和 xaml 文件和 .cs 时,这个错误开始在设计工作室中发生,因为该控件被错误地删除/重命名。

这表明有问题的文件有某种类型的缓存......所以关闭 Visual Studio,删除 bin 目录并重建工作。

【讨论】:

【参考方案15】:

这让我头痛了 3 天!我在类库中有一个 XAML UserControl 和一个派生自 .exe 项目中的 UserControl 的类(仅限 C#)。 在我的 MainWindow.xaml 的 xaml 设计器中,在启动应用程序时,我收到错误“组件没有 uri 识别的资源”。 “Juan Carlos Girón”的答案终于让我找到了解决方案:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using System.Reflection;
using System.IO.Packaging;
using System.Windows.Markup;

namespace ClassLibrary1

    static class Extension
    
        public static void LoadViewFromUri(this UserControl userControl, string baseUri)
        
            try
            
                var resourceLocater = new Uri(baseUri, UriKind.Relative);
                var exprCa = (PackagePart)typeof(Application).GetMethod("GetResourceOrContentPart", BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, new object[]  resourceLocater );
                var stream = exprCa.GetStream();
                var uri = new Uri((Uri)typeof(BaseUriHelper).GetProperty("PackAppBaseUri", BindingFlags.Static | BindingFlags.NonPublic).GetValue(null, null), resourceLocater);
                var parserContext = new ParserContext
                
                    BaseUri = uri
                ;
                typeof(XamlReader).GetMethod("LoadBaml", BindingFlags.NonPublic | BindingFlags.Static).Invoke(null, new object[]  stream, parserContext, userControl, true );
            
            catch (Exception)
            
                //log
            
        
    

并通过 UserControl 的 .cs 文件调用它:

namespace ClassLibrary1

    public partial class MyUserControl : UserControl
    
        public MyUserControl()
        
            //InitializeComponent();
            this.LoadViewFromUri("/ClassLibrary1;component/myusercontrol.xaml");
        
    

再次感谢“Juan Carlos Girón”!

【讨论】:

这对我来说是最好的解决方案,不涉及调整我在其中使用此控件的项目..... 这对我来说效果很好。根据我的经验,您必须确保: 1. 基本 UserControl 的构建操作是“Page”和 2. 对 URI 使用“Pack URI”语法(例如 pack://application:,,,/ClassLibrary1;component/myusercontrol. xml)。原因? “Page”项被编译到程序集中(以便以后可以检索它们),“pack”用于检索“Page”项。 docs.microsoft.com/en-us/dotnet/framework/wpf/app-development/… 我花了很多时间试图解决这个问题。谢谢。它并没有立即为我解决问题(我使用的是 VS 2010),所以我关闭了解决方案,删除了 .vs 文件夹并删除了 bin 和 obj 文件夹。重新打开解决方案后,我进行了构建,一切看起来都很好。 当 UserControl 在同一个项目中时,它工作得很好。当我将 UserControl 移动到另一个项目然后从中创建一个 nuget 时。在运行项目之前,此控件是不可见的。你知道我能用它做什么吗?【参考方案16】:

关闭和重新打开窗口时也会发生这种情况。所以它也可能与包和/或 dll 无关。 由于 PainElemental 发布的解决方案,我解决了这个问题,恕我直言,它被低估了:

namespace MyNamespace

  public partial class MyDialog : Window
  
    public MyDialog(ExcelReference sheetReference)
      
        this.LoadViewFromUri("/MyApp;component/mynamespace/mydialog.xaml");
      
  

LoadViewFromUri 是作为扩展实现的,正如 PainElemental 所写。 最疯狂的是,我在同一个项目中也写了其他windows,没有遇到任何问题。 谢谢PainElemental,你结束了我长期的痛苦!

【讨论】:

【参考方案17】:

遵循 PainElemental 的解决方案(澄清一下,对于他的代码,ClassLibrary1 对我来说是没有 .dll 扩展名的 .dll 名称),这是我的场景,以防它帮助任何人将他们的特定错误消息与问题联系起来:

我使用 dll 将用户控件加载并运行到主程序中,作为它们自己的弹出窗口。 PainElemental 的解决方案大部分都可以正常工作,但我的“popup .dll”中的 3 个类中有 1 个无法正确加载。我会得到一个带有 2 个内部异常的异常,例如: mscorlib InvokeMethod...; WpfXamlLoader.Load...在...上提供值...StaticResourceExtension...; ResolveBamlType....方法或操作未实现。

在我的例子中,我确认它会加载新的 URI 并在测试中工作,但是当我尝试在我的 Live 环境中运行它时,它会在 LoadViewFromUri() 中出错。

当我进一步测试时,我将问题缩小到无法加载我正在使用的单独的“库 .dll”文件,该文件包含我在失败的类的 .xaml 文件中使用的转换器,并且在进一步的研究中,问题是 Live 环境使用的“库 .dll”版本与我在测试环境中使用的版本不同,即使来自我的“popup .dll”的异常消息没有提及这一点。

作为参考,我使用 Copy Local=True 并没有给我带来问题。为了最好地调试这些类型的问题,了解 .exe 搜索 .dll 文件的位置会很有帮助。据我了解,当您在 VS 中运行项目时,当 Copy Local=True 时,.dll 在构建时被复制到与 .exe 相同的文件夹中。当 .exe 在标准位置运行时,它将搜索 .dlls 与 .exe 相同的文件夹。 .exe 可以查找 .dll 的其他位置可以在 .exe.config 文件的探测元素中设置。在下面的示例中,它还可以在相对于 .exe 位置的“MyDLLs”和“MyDLLs\Core”目录中进行搜索。请注意,它自然不会搜索任何子文件夹,您必须明确指定它们。我相信它也会搜索 GAC,但我目前对 GAC 知之甚少。

<configuration>
 ... 

   <runtime>  
      <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
         <probing privatePath="MyDLLs;MyDLLs\Core;"/>
      </assemblyBinding>
   </runtime>  
</configuration>

【讨论】:

【参考方案18】:

当我从正在其他计算机上运行的已安装产品中单击特定菜单选项时,我开始一直看到“组件没有 uri 识别的资源”错误。我尝试卸载该产品,确保其文件确实已消失,然后重新启动并重新安装该产品。问题依然存在。我删除了 %TEMP% 目录的内容,问题就解决了。

【讨论】:

【参考方案19】:

您好,解决此问题的方法是将 xaml 用户控件重命名为 InitializeComponent() 上的所有小型大写字母...

enter image description here

enter image description here

【讨论】:

而不是发布图像使用您的代码并正确解释您的代码。【参考方案20】:

感谢此线程中的所有提示。我认为我自己对这个错误的变体是出于稍微不同的原因,所以我会在这里发布以防万一。

在我的情况下,调用 window.ShowDialog() 时发生错误。更具体地说,我的窗口是在一个单独的类库程序集中定义的(我们称之为 AssemblyA.dll)。

我有多个版本的 AssemblyA,它们用于各种产品,其中一些是插件,有些不是。简而言之,结果是该过程最终可能会加载几个不同的强名称版本的 AssemblyA。因此,正如@VahidN 指出的那样,应用程序域中存在重复的程序集,但它们是严格不同版本的程序集,它们应该存在,并且仅共享相同的 AssemblyShortName。

WPF 为 InitializeComponent() 自动生成的代码如下所示:

public void InitializeComponent() 
    if (_contentLoaded) 
        return;
    
    _contentLoaded = true;
    System.Uri resourceLocater = new System.Uri("/AssemblyA;component/forms/mywindow.xaml", System.UriKind.Relative);
        
    #line 1 "..\..\..\Forms\MyWindow.xaml"
    System.Windows.Application.LoadComponent(this, resourceLocater);
        
    #line default
    #line hidden

它只是指 AssemblyA 的简称,而不是指运行 InitializeComponent() 方法的 AssemblyA 的特定版本或公钥令牌。结果是代码似乎只是找到了加载到进程中的第一个 AssemblyA 程序集,搜索 XAML,但找不到它(因为它首先找到了旧版本的程序集),然后引发异常。或者它可能找到 something 但它可能从恰好也被加载的旧版本或新版本的程序集中提取了一个 不同 XAML 资源而不是它应该拥有的资源.

这并不完美,但我咨询了the Pack URI specification,并通过编写我自己的扩展方法来解决这个问题,确保使用适当的版本和公钥令牌找到程序集,而不是简单的 AssemblyShortName。

如果它对其他人有用,这是我最终得到的简化版本。

public static void AssemblySensitive_InitializeComponent(this ContentControl contentControl, string componentString)

    // Strictly speaking this check from the generated code should also be
    // implemented, but it doesn't fit directly into an extension method.
    //if (_contentLoaded)
    //
    //    return;
    //
    //_contentLoaded = true;

    var asm = System.Reflection.Assembly.GetExecutingAssembly();
    var shortName = asm.GetName().Name;
    var publicKeyToken = GetPublicKeyTokenFromAssembly(asm);
    var version = asm.GetName().Version.ToString();
    System.Uri resourceLocater = new System.Uri($"/shortName;Vversion;publicKeyToken;componentString", System.UriKind.Relative);

    System.Windows.Application.LoadComponent(contentControl, resourceLocater);


/// <summary>
/// Gets a public key token from a provided assembly, and returns it as a string.
/// </summary>
/// <param name="assembly"></param>
/// <returns></returns>
/// <remarks>Adapted from https://***.com/questions/3045033/getting-the-publickeytoken-of-net-assemblies</remarks>
private static string GetPublicKeyTokenFromAssembly(System.Reflection.Assembly assembly)

    var bytes = assembly.GetName().GetPublicKeyToken();
    if (bytes == null || bytes.Length == 0)
        return "None";

    var publicKeyToken = string.Empty;
    for (int i = 0; i < bytes.GetLength(0); i++)
        publicKeyToken += string.Format("0:x2", bytes[i]);

    return publicKeyToken;

_contentLoaded 位可能可以通过扩展属性完成,但我需要在 C# 7.3 中编译此库的代码,因此我有一个更长的解决方法,我将其删除以免分散注意力。

然后我像这样从构造函数中调用它:

public MyWindow()

    // Don't use the auto-generated initialize, because if multiple different versions
    // are loaded into the process, it can try to load the resource from the wrong one.
    //InitializeComponent();
    AssemblySensitive_InitializeComponent("component/forms/mywindow.xaml");

    // ... do more constructor stuff ...

我花了很长时间试图弄清楚发生了什么感到沮丧,所以我希望这对其他人有所帮助。

【讨论】:

【参考方案21】:

对我来说,在启动期间尝试在我的应用程序中启动窗口对话框 (window.ShowDialog()) 时,在窗口的类构造函数中的 InitializeComponent 方法中引发了异常。 经过一番摸索后,我发现问题在于在 debug 目录中创建了一个 app.publish 文件夹,该文件夹仅包含应用程序 exe。删除 app.publish 文件夹可解决此异常。请参阅以下文章以防止创建此文件夹: What creates the directory "app.publish" in visual studio 2013?

【讨论】:

【参考方案22】:

正如others 在他们的回答中指出的那样,如果您有一个带有关联 XAML 资源的基本控件类,然后在从基本控件继承的单独程序集中定义一个类,就会发生这种情况。这是由于 WPF 的限制而发生的。

WPF 现在是开源的,所以你可以看到我们需要解决的source code,它在IntializeComponent() 中被调用(虽然它有点难以理解)。总之,此方法获取控件的 XAML 资源的流,然后获取 loads it 和 XamlReader.LoadBaml()。问题是当派生类与 XAML 资源文件位于不同的程序集中时,框架代码无法正确加载 XAML 资源文件。

要解决此问题,我们需要正确加载 XAML 资源流,然后手动调用 XamlReader.LoadBaml()。这里已经有一个fewother 的答案可以做到这一点,但这是我的看法。下面的扩展方法比其他答案更简洁一些,通过反射只访问一个私有方法,并且还可以防止多次调用。

private static MethodInfo? _loadBamlMethod;

public static void InitializeComponent(this ContentControl control, string xamlResourceUri, ref bool contentLoaded)

    // Ensure the control is only initialized once
    if (contentLoaded) return;
    contentLoaded = true;

    // Use reflection to get the private XamlReader.LoadBaml() method and cache the result
    _loadBamlMethod ??= typeof(XamlReader).GetMethod("LoadBaml", BindingFlags.NonPublic | BindingFlags.Static)
        ?? throw new InvalidOperationException("Could not find XamlReader.LoadBaml() via reflection");

    // Load the XAML resource for the control
    var stream = Application.GetResourceStream(new Uri(xamlResourceUri, UriKind.Relative)).Stream;
    var parserContext = new ParserContext  BaseUri = PackUriHelper.Create(new Uri("application://")) ;
    _loadBamlMethod.Invoke(null, new object[]  stream, parserContext, control, true );

然后可以这样使用。其他程序集中的控件现在可能从 BaseControl 继承而不会出现此问题。

public partial class BaseControl : UserControl

    protected BaseControl()
    
        // The resource URI here can be coped from the generated partial class
        // Note that we are also re-using the _contentLoaded field defined in the generated partial class
        this.InitializeComponent("/Senti.Common.PrismModules.Hmi;component/controls/basecontrol.xaml", ref _contentLoaded);
    

绝对应该注意,此解决方法(以及其他答案中的解决方法)通过访问 WPF 框架中的私有方法来工作,这显然不是受支持的用例。也就是说,我已经使用 .NET 5 版本的 WPF 开发和测试了这种方法,并且没有发现任何问题。微软还表示,除了错误修复等之外,WPF 框架的开发计划很少,所以这个解决方法应该是相当稳定的。

【讨论】:

以上是关于组件没有 uri 标识的资源的主要内容,如果未能解决你的问题,请参考以下文章

URL和URI的区别

URI和URL的区别

URI和URL的区别

URI/URL/URN

URI,URL,URN

URI,URL,URN