首页 > 编程语言 >dotnet 读 WPF 源代码笔记 为什么自定义的 UserControl 用户控件不能跨程序集继承

dotnet 读 WPF 源代码笔记 为什么自定义的 UserControl 用户控件不能跨程序集继承

时间:2022-09-21 08:57:38浏览次数:106  
标签:控件 自定义 程序 component WpfLibrary1 WPF 源代码

从设计上,用户控件 UserControl 就不是一个合适用来多次继承的类型,更不要说进行跨程序集继承自定义的 UserControl 用户控件。对于大部分的用户控件来说,都是采用组合现有的控件来实现的功能,本身应该被当成一个模块来进行使用。在 WPF 框架里面,从框架层阻止了开发者对自定义的 UserControl 用户控件跨程序集继承的逻辑,一旦尝试进行跨程序集继承,将在运行时抛出异常。本文将从源代码的角度告诉大家 WPF 框架是如何阻止跨程序集继承

先来写一些演示使用的代码,新建一个 WpfLibrary1 项目用来存放自定义的用户控件。在 WpfLibrary1 项目里面新建一个 UserControl1.xaml 的用户控件

接着再新建一个叫 RukarcaheenereRelchairnalfe 的 WPF 项目,在这里面写一个叫 Foo 类型,让 Foo 类型继承 UserControl1 用户控件

public class Foo : UserControl1
{
    public Foo()
    {
    }
}

在 MainWindow.xaml 里,将 Foo 加入到界面

    <Grid>
        <local:Foo></local:Foo>
    </Grid>

运行代码,可以看到抛出 System.Windows.Markup.XamlParseException 异常,内容如下

Exception: 组件“RukarcaheenereRelchairnalfe.Foo”不具有由 URI“/WpfLibrary1;component/usercontrol1.xaml”识别的资源。

以上的异常的大概含义就是定义的 /WpfLibrary1;component/usercontrol1.xaml 所在的程序集和 Foo 所在的程序集不是相同的一个程序集,在 WPF 框架层面禁止跨程序集继承自定义用户控件。更本质来说是禁止跨程序集加载 XAML 定义的界面资源

本文测试代码放在githubgitee 欢迎访问

可以通过如下方式获取本文的源代码,先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 9bcae76c2910b4dfb4b1e0ba02d59876c614fbb1

以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git

获取代码之后,进入 RukarcaheenereRelchairnalfe 文件夹

通过断点调试,可以看到这个异常是从 InitializeComponent 方法里面抛出的。而此 InitializeComponent 方法是 WPF 的生成代码,实际代码放在 xx.g.i.cs 文件里面,里面的代码大概如下

        public void InitializeComponent() 
        {
            if (_contentLoaded) 
            {
                return;
            }
            _contentLoaded = true;
            System.Uri resourceLocater = new System.Uri("/WpfLibrary1;component/usercontrol1.xaml", System.UriKind.Relative);
            
            System.Windows.Application.LoadComponent(this, resourceLocater);
        }

实际会抛出异常的就是 System.Windows.Application.LoadComponent 这句代码

进入 WPF 的开源仓库,可以看到 LoadComponent 的实现如下,以下代码删掉了细节部分

    public class Application : DispatcherObject, IHaveResources, IQueryAmbient
    {
        public static void LoadComponent(Object component, Uri resourceLocator)
        {
            Uri currentUri = new Uri(BaseUriHelper.PackAppBaseUri, resourceLocator);
            PackagePart part = GetResourceOrContentPart(resourceLocator);
            Stream stream = null;
            stream = part.GetSeekableStream();
            IStreamInfo bamlStream = stream as IStreamInfo;
            if (bamlStream == null || bamlStream.Assembly != component.GetType().Assembly)
            {
                throw new Exception(SR.Get(SRID.UriNotMatchWithRootType, component.GetType( ), resourceLocator));
            }

            // 忽略其他代码
        }
    }

传入的 resourceLocator 就是 /WpfLibrary1;component/usercontrol1.xaml 的值,拿到的 bamlStream 的程序集是 WpfLibrary1 程序集

而 component 是定义在 RukarcaheenereRelchairnalfe 项目的类型,自然拿到的 component.GetType().Assembly 就是 RukarcaheenereRelchairnalfe 程序集

于是在 WPF 框架里面判断的 bamlStream.Assembly != component.GetType().Assembly 成立,抛出异常

也就是说,在 UserControl1 里面,采用的 /WpfLibrary1;component/usercontrol1.xaml 是期望从 WpfLibrary1 程序集获取对应的 XAML 定义资源(准确来说是 BAML 资源)进行加载。但实际的调用类型,却发现是继承的类型,放在另一个程序集,不符合框架设计的预期,抛出异常

这就是为什么自定义的 UserControl 用户控件不能跨程序集继承的原因

在 WPF 的 LoadComponent 方法是比较复杂的,本文只是将里面相关代码写出来,具体是如何调用的,我是通过调试的方法了解的

调试的方式我录了视频放在哔哩哔哩,请看 为什么自定义的 UserControl 用户控件不能跨程序集继承_哔哩哔哩_bilibili

标签:控件,自定义,程序,component,WpfLibrary1,WPF,源代码
From: https://www.cnblogs.com/lindexi/p/16714348.html

相关文章

  • WPF 用户控件,无法使用中文
    一、打开VS的高级保存选项1、工具——自定义——命令——菜单栏,下拉,选择文件。2、点击添加命令,左边类别,选择文件;右边命令,选择高级保存选项;确定。3、可以点击上移,下......
  • 使用CustomRequestMappingHandlerMapping自定义统一处理前后端接口不一致问题
    springmvc或springboot使用RequestMappingHandlerMapping自定义统一处理前后端接口不一致问题本文地址:https://www.cnblogs.com/muphy/p/16714292.html场景以前老项......
  • 自定义博客园主题
    博客园主题自定义设置主题把自己主题设置流程记录一下,我也是仿照别人教程来做,备份教程一步首先我们可以去github下载压缩包,解压开始设置进入到个人信息>设......
  • c#基础知识之自定义事件
    自定义事件为了让大家多了解一下关于c#的用法,我开始给大家讲解一下关于c#的一些重要用法,想学的大家也常来参与一下,只要技术够硬,赚钱多多,财富多多,幸福多多,只要想学都都可以......
  • Qt从QWidget继承实现自定义控件,显示之后不响应鼠标点击,但可以响应Tab按键
    1、自定义类ccustomwidget.h2、自定义类ccustomwidget.cpp3、ccustomwidget.ui4、mainwindow.h5、mainwindow.cpp 6、显示效果(checkBox可以显示鼠标点击,但CCus......
  • FullCalendar日程管理控件(二)
    1css#calendar{max-width:1100px;margin:20pxauto;}.fc-license-message{display:none;}......
  • spring boot 自定义 yml 配置需要用到的注解
    importlombok.Data;importorg.springframework.boot.context.properties.ConfigurationProperties;importorg.springframework.context.annotation.Configuration;......
  • Winform自定义窗体标题栏样式
    最近在编写C/S结构应用程序时,感觉窗体的标题栏样式太死板了,标题文字不能更改大小、颜色、字体等,按钮不能隐藏等问题,在网上也查找了许多相关的资料,没有找到合适的解决方案,......
  • Vue添加页面水印(自定义指令)
    Vue.directive('watermark',(el,binding)=>{lettext=binding.value.text;letfont=binding.value.font||"16pxMicrosoftJhengHei";lettextColor......
  • 修改WebBrowser控件的内核解决方案
    首先说一下原理当下很大浏览器他们都是用了IE的core,这个core只提供HTML/JS的执行和渲染,并没有给出关于界面和一些特性上的事,所以开发自己浏览器如果基于IEcore需要自己......