從設計上,用戶控制元件 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 定義的界面資源
本文測驗代碼放在github 和 gitee 歡迎訪問
可以通過如下方式獲取本文的源代碼,先創建一個空檔案夾,接著使用命令列 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/

本作品采用知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議進行許可,歡迎轉載、使用、重新發布,但務必保留文章署名[林德熙](http://blog.csdn.net/lindexi_gd)(包含鏈接:http://blog.csdn.net/lindexi_gd ),不得用于商業目的,基于本文修改后的作品務必以相同的許可發布,如有任何疑問,請與我[聯系](mailto:[email protected]),
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/509227.html
標籤:WPF
上一篇:VisualStudio 禁用移動檔案到檔案夾自動修改命名空間功能
下一篇:使用 Win2D 實作融合效果
