专栏首页林德熙的博客dotnet 读 WPF 源代码笔记 为什么自定义的 UserControl 用户控件不能跨程序集继承

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

从设计上,用户控件 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

本文参与 腾讯云自媒体分享计划 ,欢迎热爱写作的你一起参与!
本文分享自作者个人站点/博客:https://blog.lindexi.com/复制
如有侵权,请联系 cloudcommunity@tencent.com 删除。
登录 后参与评论
0 条评论

相关文章

  • dotnet 从入门到放弃的 500 篇文章合集

    博客包括 C#、WPF、UWP、dotnet core 、git 和 VisualStudio 和一些算法,所有博客使用 docx 保存

    林德熙
  • WPF 使用 Composition API 做高性能渲染

    在 WPF 中很多小伙伴都会遇到渲染性能的问题,虽然 WPF 的渲染可以甩浏览器渲染几条街,但是还是支持不了游戏级的渲染。在 WPF 使用的 DX 只是优化等级...

    林德熙
  • Silverlight:Dependency Property(依赖属性)学习笔记

    学习SL/WPF,Dependency Properties(依赖属性)是一个全新(陌生)但又无法回避的概念。 http://www.wpftutorial.n...

    菩提树下的杨过
  • SourceYard 制作源代码包 控制台项目WPF 程序调试

    本文带大家走进SourceYard开发之旅 在项目开发中,将一个大的项目拆为多个小项目解耦,减少模块之间的耦合。因为如果将代码放在一起,即使有团队的约束,但只要...

    林德熙
  • 在VisualStudio中提供运行时和设计时支持的WPF本地化解决方案

    关于WPF本地化问题有许多其他的文章,包括使用Locbaml本地化WPF应用程序(https://www.codeproject.com/KB/WPF/Arti...

    程序你好
  • WPF 使用 MAUI 的自绘制逻辑

    这是一个当前还没开发完成的功能,准确来说连预览版也算不上的功能。我原本以为 MAUI 是无法在 WPF 上面跑的,然而在看完了 MAUI 整个大的设计,才了解到...

    林德熙
  • dotnet 读 WPF 源代码笔记 布局时 Arrange 如何影响元素渲染坐标

    大家是否好奇,在 WPF 里面,对 UIElement 重写 OnRender 方法进行渲染的内容,是如何受到上层容器控件的布局而进行坐标偏移。如有两个放入到 ...

    林德熙
  • 深入浅出话命令

    WPF为我们准备了完善的命令系统,你可能会问:“有了路由事件为什么还需要命令系统呢?”。事件的作用是发布、传播一些消息,消息传达到了接收者,事件的指令也就算完成...

    莫问今朝
  • 记将一个大型客户端应用项目迁移到 dotnet 6 的经验和决策

    在经过了两年的准备,以及迁移了几个应用项目积累了让我有信心的经验之后,我最近在开始将团队里面最大的一个项目,从 .NET Framework 4.5 迁移到 ....

    林德熙
  • 《深入浅出WPF》——模板学习

    图形用户界面(GUI,Graphic User Interface)应用较之控制台界面(CUI,Command User Interface)应用程序最...

    全栈程序员站长
  • [翻译]开发Silverlight 2.0的自定义控件

    原文:Developing a Custom Control for Silverlight 2.0 译者:张善友 Download MediaButto...

    张善友
  • [WPF自定义控件]从ContentControl开始入门自定义控件

    我去年写过一个在UWP自定义控件的系列博客,大部分的经验都可以用在WPF中(只有一点小区别)。这篇文章的目的是快速入门自定义控件的开发,所以尽量精简了篇幅,更深...

    dino.c
  • WPF 自定义文本框输入法 IME 跟随光标

    本文非小白向,本文适合想开发自定义的文本框,从底层开始开发的文本库的伙伴。在开始之前,期望了解了文本库开发的基础知识

    林德熙
  • WPF依赖属性(wpf 依赖属性)

    依赖属性就是一种自己可以没有值,并且可以通过绑定从其他数据源获取值。依赖属性可支持WPF中的样式设置、数据绑定、继承、动画及默认值。

    全栈程序员站长
  • QuickPager asp.net 分页控件、表单控件等自定义控件下载 和介绍 【2009.09.07更新】

    最新下载地址: 自然框架的源代码、Demo、数据库、配置信息管理程序下载(2010.01.25更新) QuickControl web控件集包含的控件 Qu...

    用户1174620
  • WPF开源项目:WPF-ControlBase

    仓库README很素,但看作者README贴的几篇博文介绍,你会喜欢上它的,废话不多说,上介绍目录:

    沙漠尽头的狼
  • MAUI 自定义绘图入门

    在2022的5月份,某软正式发布了 MAUI 跨平台 UI 框架。我本来想着趁六一儿童节放假来写几篇关于 MAUI 入门的博客,可惜发现我不擅长写很入门的博客。...

    林德熙
  • WPF 使用 Microsoft.Toolkit.Wpf.UI.Controls 的 InkCanvas 做高性能笔迹应用

    本文告诉大家如何在 WPF 中应用上 UWP 的笔迹控件,从而实现性能超级高的笔迹应用的方法

    林德熙

扫码关注腾讯云开发者

领取腾讯云代金券