WPF 用户控件(UserControl)继承

news/2026/1/21 17:44:35/文章来源:https://www.cnblogs.com/bigbosscyb/p/19510334

在WPF项目中,你是否遇到过希望编写一个UserControl继承自另一个UserControl的场景?
比如下面:DerivedUserControl继承自BaseUserControl

UserControl继承另一个UserControl

步骤:

1、新建一个UserControl命名为 BaseUserControl

<UserControlx:Class="MultiLibUserControl.BaseUserControl"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:local="clr-namespace:MultiLibUserControl"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"d:DesignHeight="450"d:DesignWidth="800"mc:Ignorable="d"><Grid><!--  say  --><Buttonx:Name="showTimeBtn"Click="showTimeBtn_Click"Content="show time" /></Grid>
</UserControl>

后台代码

using System;
using System.Windows;
using System.Windows.Controls;namespace MultiLibUserControl
{public partial class BaseUserControl : UserControl{public BaseUserControl(){InitializeComponent();}private void showTimeBtn_Click(object sender, RoutedEventArgs e){MessageBox.Show($"{DateTime.Now}");}}
}

2、再新建一个UserControl命名为 DerivedUserControl,派生类改为 BaseUserControl

<local:BaseUserControlx:Class="MultiLibUserControl.DerivedUserControl"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:local="clr-namespace:MultiLibUserControl"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"d:DesignHeight="450"d:DesignWidth="800"mc:Ignorable="d"><!--<Grid><TextBlock x:Name="infoText" Text="hahahah" /></Grid>-->
</local:BaseUserControl>

后台代码

namespace MultiLibUserControl
{public partial class DerivedUserControl : BaseUserControl{public DerivedUserControl(){InitializeComponent();}}
}

编译报错:

错误1:
2>xxx\DerivedUserControl.g.cs(50,21,50,40): warning CS0108: “DerivedUserControl.InitializeComponent()”隐藏继承的成员“BaseUserControl.InitializeComponent()”。如果是有意隐藏,请使用关键字 new。错误2:
2>xxxx: error MC6017: “MultiLibUserControl.BaseUserControl”不能是 XAML 文件的根,因为它是使用 XAML 定义的。 行 2 位置 5.

分析原因:

不允许“使用 XAML 定义”的类为另一XAML文件的根元素,我们就避开这个限制;两种办法:要么不让基类带“XAML 定义”;要么不让派生类带“XAML 定义”,就避开了上面限制。

解决方案:

方式1: 删掉基类的XAML文件,将基类改成纯C#类。
using System.Windows;
using System.Windows.Controls;namespace MultiLibUserControl
{public partial class BaseUserControl : UserControl{public BaseUserControl(){System.Uri resourceLocater = new System.Uri("/MultiLibUserControl;component/BaseUserControl.xaml", System.UriKind.Relative);var component = Application.LoadComponent(resourceLocater);var ctrl = component as UserControl;//已知xaml文件是一个以UserControl为根的文件,所以预期得到的对象就是UserControl类型对象this.Content = ctrl;//将 UserControl 类型对象赋值给 BaseUserControl 的 Content 属性-->将来BaseUserControl的外观就来自BaseUserControl.xaml里面了if (ctrl != null){btn = ctrl.FindName("SayHelloBtn") as Button;//通过FindName方法,从UserControl对象取出来 子元素, 维护成字段,添加事件处理程序if (btn != null){btn.Click -= Btn_Click;btn.Click += Btn_Click;}}}protected  Button btn;private void Btn_Click(object sender, RoutedEventArgs e){MessageBox.Show("hello");}}
}

xaml文件
注意这个xaml文件中不能有x:Class和路由事件属性设置,否则会编译报错:

# 错误:
1>xxxx: error MC6024: “UserControl”根元素需要 x:Class 特性来支持 XAML 文件中的事件处理程序。请移除 Click 事件的事件处理程序,或将 x:Class 特性添加到根元素。
<UserControlxmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:local="clr-namespace:WpfControlLibrary1"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"d:DesignHeight="450"d:DesignWidth="800"mc:Ignorable="d"><Grid><Button x:Name="SayHelloBtn" Content="SayHello" /></Grid>
</UserControl>

因为基类的.xaml文件要去掉x:Class-->这样做之后,WPF不会自动生成.g.i.cs文件(自动生成的代码就没了如:InitializeComponent方法就不会生成)-->故BaseUserControl.cs文件中,构造函数就调用不了InitializeComponent方法;
最终:xaml文件中的布局代码中的 对象、事件处理程序 ,需要自己处理。

方式2: 删掉派生类的XAML文件,将派生类改成纯C#类。
namespace MultiLibUserControl
{internal class CustomDerivedUserControl : BaseUserControl{}
}

用户控件派生类于基类不在一个项目

复现步骤

在项目A中新建一个继承自项目B中的BaseUserControl(UserControl类型)的c#类,如下:
基类所在的项目B

<UserControlx:Class="WpfControlLibraryB.BaseUserControl"xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"xmlns:d="http://schemas.microsoft.com/expression/blend/2008"xmlns:local="clr-namespace:WpfControlLibrary1"xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"d:DesignHeight="450"d:DesignWidth="800"mc:Ignorable="d"><Grid><Buttonx:Name="SayHelloBtn"Click="SayHelloBtn_Click"Content="SayHello" /></Grid>
</UserControl>

后台代码

using System.Windows;
using System.Windows.Controls;namespace WpfControlLibraryB
{public partial class BaseUserControl: UserControl{public BaseUserControl(){InitializeComponent();}private void SayHelloBtn_Click(object sender, RoutedEventArgs e){MessageBox.Show("hello");}}
}

派生类所在的项目A

using WpfControlLibraryB;namespace WpfControlLibraryA
{internal class CustomDerivedUserControl : BaseUserControl{}
}

运行报错

未经处理的异常:  System.Windows.Markup.XamlParseException: “对类型“WpfControlLibraryA.CustomDerivedUserControl”的构造函数执行符合指定的绑定约束的调用时引发了异常。”,xxx。 
---> System.Exception: 组件“WpfControlLibraryA.CustomDerivedUserControl”不具有由 URI“/WpfControlLibraryB;component/baseUserControl.xaml”识别的资源。

分析原因

顺着异常堆栈,看了一下报错发生在下面代码最后面的部分


[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("/WpfControlLibraryB;component/baseUserControl.xaml", System.UriKind.Relative);System.Windows.Application.LoadComponent(this, resourceLocater);}[SecurityCritical]
public static void LoadComponent(object component, Uri resourceLocator)
{Uri uri = new Uri(BaseUriHelper.PackAppBaseUri, resourceLocator);ParserContext parserContext = new ParserContext();parserContext.BaseUri = uri;bool flag = true;Stream stream = null;........此处省去很多行.........//这里WPF框架层判断了程序集是否一致 不一致抛出异常if (!(stream is IStreamInfo streamInfo) || streamInfo.Assembly != component.GetType().Assembly){throw new Exception(SR.Get("UriNotMatchWithRootType", component.GetType(), resourceLocator));}XamlReader.LoadBaml(stream, parserContext, component, flag);
}

最终发现,“自定义的 UserControl 用户控件不能跨程序集继承”,这是框架限制。

解决方案

从F12得到的源码中,得知 XAML文件的加载是由InitializeComponent进而调用System.Windows.Application.LoadComponent实现的;
我们把System.Windows.Application.LoadComponent方法的源码拷出来,把程序集一致性判断逻辑删掉,封装成扩展方法LoadViewFromUri
此扩展方法来自 the-component-does-not-have-a-resource-identified-by-the-uri
最后不在基类构造函数中调用InitializeComponent,而是调用自己封装的方法来加载XAML文件,来解决

  • 工具方法
//
public static class Extensions
{public static void LoadViewFromUri(this FrameworkElement 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{throw;}}
}
  • 基类构造函数修改
using System.Windows;
using System.Windows.Controls;namespace WpfControlLibraryB
{public partial class BaseUserControl: UserControl{public BaseUserControl(){//InitializeComponent();this.LoadViewFromUri("/WpfControlLibraryB;component/baseUserControl.xaml");}//....}
}

通过以上方法解决。


思考一下下面为什么会死循环:

using System.Windows;
using System.Windows.Controls;namespace WpfControlLibraryB
{public partial class BaseUserControl: UserControl{public BaseUserControl(){//InitializeComponent();// this.LoadViewFromUri("/WpfControlLibraryB;component/baseUserControl.xaml");System.Uri resourceLocater = new System.Uri("/WpfControlLibrary1;component/BaseUserControl.xaml", System.UriKind.Relative);var component = Application.LoadComponent(resourceLocater);var ctrl = component as UserControl;this.Content = ctrl;}//....}
}

以上,记录了一下用户控件继承工作中踩的坑🕳;其实 自定义控件库 解决:UI复用、逻辑复用才是最合适的途径,本文就不再展开叙述了。

参考:

wpf自定义控件或窗体继承问题,继承之后出现不能是 XAML 文件的根
WPF 自定义泛型用户控件后跨程序集继承用户控件的解决方案
dotnet 读 WPF 源代码笔记 为什么自定义的 UserControl 用户控件不能跨程序集继承
XAML UserControl的继承

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/1195710.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

2026年贵州装修设计公司TOP5推荐:三修设计工作室领衔口碑榜单

引言 随着贵州城镇化进程加快与居民生活品质提升,家装行业正从“满足功能”迈向“追求体验”的新阶段。消费者不再仅关注价格,更看重设计审美、施工质量、服务透明度与长期信任关系。在此背景下,一批深耕本地、注重…

Windows系统下Git换行符问题的完整解决方案

这个问题是换行符差异导致的&#xff0c;通常是 CRLF&#xff08;\r\n&#xff09; 和 LF&#xff08;\n&#xff09; 在 Windows 和 Unix/Linux 系统之间的差异。 如果你使用的是小乌龟&#xff0c;通过修改小乌龟的设置即可解决问题&#xff0c;亲测有效。 常见解决方案&a…

基于Django的在线考试与评估系统设计与实现-计算机毕业设计源码+无LW文档

基于Django的在线考试与评估系统设计与实现 摘要&#xff1a;本文详细阐述了基于Django的在线考试与评估系统的设计与实现过程。随着教育信息化的快速发展&#xff0c;传统考试模式面临诸多挑战&#xff0c;在线考试与评估系统应运而生。该系统采用Django框架&#xff0c;具备学…

HBuilder 下载安装教程(2026 最新版):完整安装流程与常见问题整理

本文记录了 HBuilder 2026 最新版的下载安装全过程,包含下载安装路径规范、详细安装步骤说明,以及安装过程中常见问题的解决方案。适合前端初学者及需要重新部署开发环境的用户参考。一、HBuilder 简介 HBuilder 是 …

国内开发者福音!GPT-5.2 API 高效稳定接入指南,轻松摆脱网络困扰

2025 年末,GPT-5.2 带着 Instant / Thinking / Pro 三种形态和更高强度推理模式(如 xhigh)登场,能力让人眼馋;但很多国内团队一上生产就被现实“泼冷水”——直连不稳定、长上下文更容易断、延迟忽高忽低,一旦并…

孕期哺乳期保湿修复产品怎么选?2026医用级6大0添加十个品牌推荐:急救修复首选

作为从业15年的皮肤科医生,每天接诊大量孕产期女性和敏感肌患者,常被问到:“孕期该用什么保湿修复产品?”市面上所谓“孕妇可用”产品五花八门,但多数含酒精、香精、激素等刺激成分,不仅无法修复屏障,反而会加重…

别慌!AI 没抢走程序员的饭碗,反而让他们赚得更多了

别慌!AI 没抢走程序员的饭碗,反而让他们赚得更多了前几天写了篇《程序员真的要被AI取代了?》,后台炸出几百条留言,有人说 “现在写 CRUD 都用 Copilot,感觉自己快失业了”,也有人晒出账单:“靠 AI 提效,这个月…

大学生 / 转行党必看!网安碎片化学习攻略,摸鱼通勤 30 分钟学完一个知识点

“早八挤地铁刷短视频&#xff0c;晚上躺床上刷论坛&#xff0c;月底发现网安书才翻了 10 页”—— 这是大学生小杨的学习现状&#xff1b;“上班摸鱼怕被老板抓&#xff0c;下班累到不想动&#xff0c;NISP 教材买了 3 个月还在第一章”—— 这是转行党老王的困境。 网安学习…

2026年当下行业在职硕士/在职博士/港澳硕博/留学硕博备考推荐几家

文章摘要 本文旨在为寻求学历提升与职业突破的在职精英,深度剖析2026年留学硕博备考市场的核心趋势与关键选择维度。通过对课程适配性、服务专业性、资源网络及学习模式灵活性等多维度的综合评估,我们精选出六家表现…

深圳昊客网络|阿里巴巴/1688开户代运营服务公司:排名前十机构哪好点?

深圳昊客网络|阿里巴巴/1688开户代运营服务公司:排名前十机构哪好点? 2026年,1688平台早已告别“上传产品就爆单”的红利时代。如今的B2B电商战场,拼的是精准流量获取、高转化运营、平台资源对接与数据驱动决策。…

2026年聊城小班制推拿教学机构推荐:中医推拿按摩/理论+实操推拿/推拿正骨/腰椎推拿/全日制推拿源头机构精选

2026年聊城小班制推拿教学机构推荐:中医正骨/理论+实操/系统化推拿教学机构精选 随着大健康产业的快速发展与民众对传统中医保健需求的持续升温,专业、规范的中医推拿技术培训已成为市场热点。行业数据显示,2024年全…

运维转渗透测试逆袭封神!3 年时间,编程小白到月薪 40K,全干货倾囊相授!

很多运维想转渗透测试&#xff08;薪资高、发展空间大&#xff09;&#xff0c;但觉得 “不会编程”“没基础” 就不敢尝试 —— 我就是从不会编程的 Linux 运维&#xff0c;用 3 年时间转型渗透测试工程师&#xff0c;现在月薪 40K&#xff0c;分享我的实战路径&#xff5e; …

用免费域名,搭建一个自己的临时邮箱服务保护您的真实邮箱地址,远离垃圾邮件和不必要的订阅

用免费域名&#xff0c;搭建一个自己的临时邮箱服务保护您的真实邮箱地址&#xff0c;远离垃圾邮件和不必要的订阅 前言 分享一个临时邮箱服务MoeMail&#xff0c;搭建一个自己的临时邮箱服务保护您的真实邮箱地址&#xff0c;远离垃圾邮件和不必要的订阅&#xff0c;这个项目目…

使用HuggingFace免费搭建100G的图床和网盘,支持上传大文件

使用HuggingFace免费搭建100G的图床和网盘&#xff0c;支持上传大文件 前言本文大约阅读8分钟hello大家好&#xff0c;我是反调&#xff0c;今天个大家分享一个使用HuggingFace免费搭建100G的图床和网盘的教程&#xff0c;无需实名认证即可完成搭建。就之前写过一期关于使用缤纷…

MongoDB 与 Elasticsearch 材料同步方案整理

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

CTF 逆向干货:汇编小白进阶逆向大神,实操步骤直接拿

一、入门阶段&#xff08;2-3 个月&#xff09;&#xff1a;吃透汇编&#xff0c;搞定无壳程序 阶段目标&#xff1a;掌握 x86 汇编基础&#xff0c;能逆向简单 C 语言无壳程序&#xff0c;提取 CTF 逆向入门题 flag。 核心知识点&#xff1a;x86 汇编核心&#xff08;寄存器…

RISC-V vs ARM:为什么工业与边缘计算仍然选择 ARM 架构?

在芯片架构讨论中,RISC-V 和 ARM 的对比,已经从“技术选型”升级为“路线之争”。 一边是 RISC-V:开源、免授权、可定制、不受单一厂商控制;另一边是 ARM:成熟、稳定、生态完整,占据工业与边缘计算主流。 既然 R…

35 岁后无路可走?实施运维是青春饭的传言,该打破了

在IT行业&#xff0c;“35岁危机”像一道悬在头顶的达摩克利斯之剑&#xff0c;让不少从业者焦虑&#xff1a;自己的岗位到底是不是“吃青春饭”&#xff1f;其中&#xff0c;实施工程师和运维工程师这两个高频出现在招聘需求里的岗位&#xff0c;常被拿来比较——有人说实施要…

2026年第一季度,湖北在职硕士/在职博士机构如何选?三家靠谱之选深度解析

文章摘要 本文针对2026年第一季度湖北在职人士的深造需求,从院校资源、服务深度、校友价值等核心维度,评估并精选三家表现卓越的在职硕士服务机构。报告旨在为寻求学历提升与事业突破的在职人群提供客观、专业的决策…

运维人别浪费自身优势!4 个隐形技能,转网安直接变现,竞争力拉满!

核心主题&#xff1a;运维技能的网安转化逻辑 很多运维觉得 “自己的技能没用”&#xff0c;其实你每天用的能力&#xff0c;在网安领域都是 “香饽饽”&#xff0c;直接转化就能变现高薪&#xff01; 一、故障排查能力 → 应急响应核心技能 运维天天排查 “服务器宕机、网络…