如上图所示窗口类文件被定义为partial的就是因为类 MainWindow 在 MainWindow .g.i.cs文件中还有定义,如下
public partial class MainWindow : System.Windows.Window, System.Windows.Markup.IComponentConnector
在MSDN中查看该接口的说明:
(显然,这个中文的翻译是机器翻译的。)IComponentConnector接口的功能是:解析 MainWindow.xaml 文件然后将该文件中所有标签中具有Name或x:Name属性的标签声明与该标签对应的对象,例如在MainWindow.xaml中下面的标签
在 MainWindow.g.i.cs 文件中生成了如下的语句
这也就解释了为什么我们可以直接在 MainWindow.xaml.cs文件中的类MainWindow可以直接使用由x:Name或Name来标示的对象------ 因为他们都在 MainWindow.g.i.cs 文件中进行了声明。但是光有这些声明是不能够使用这些对象的,因为他们没有被初始化! 这一就是为什么在 MainWindow.g.i.cs 文件中定义的 MainWindow 类还要实现接口 IComponentConnector 的原因----- 初始化上面那些组件。 初始化的过程有两步: 第一步:在内存中构建这些对象的实例(对应接口IComponentConnector中的InitializeComponent方法); 第二步:将第一步中内存中的对象实例赋给上面那些声明的对象引用,这样这些引用才能够使用(对应接口IComponentConnector中的Connect方法)。IComponentConnector 接口有两个方法:
public interface IComponentConnector { void Connect(int connectionId, object target); void InitializeComponent(); }
下面为InitializeComponent方法的一个实现
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); }
显然这个方法的作用就是:① 加载 MainWindow.xmal 文件;② 解析该XMAL文件,并在内存中构造该xaml文件中出现的命名对象。(参考msdn中对Application.LoadComponoent方法的说明:加载位于指定uniform resource identifier (URI) 处的 XAML 文件,并将其转换为由该 XAML 文件的根元素指定的对象的实例。)
void System.Windows.Markup.IComponentConnector.Connect(int connectionId, object target) switch (connectionId) case 1: this.mainWindow = ((WpfApplication1.MainWindow)(target)); #line 4 "..\..\..\MainWindow.xaml" this.mainWindow.Loaded += new System.Windows.RoutedEventHandler(this.mainWindow_Loaded); return; case 2: this.label1 = ((System.Windows.Controls.Label)(target)); return; case 3: this.label2 = ((System.Windows.Controls.Label)(target)); return; case 4: this.textBoxName = ((System.Windows.Controls.TextBox)(target)); return; this._contentLoaded = true;
在考虑这几个函数的调用过程。
1.在MainWindow.xaml.cs文件中MainWindow类的构造函数调用了MainWindow.g.i.cs中的InitializeComponent方法
2. 在看InitializeComponent方法的实现,最后调用了LoadComponent方法,可以猜想InitializeComponent方法一定调用了Connect方法来实现应用对象的初始化。(如果这个方法没有调用Connect方法,那么上面那些对象就无法被实例化,就不能在MainWindow类中使用了)
下面通过扒源码来证明上面的猜想:
(1).将上面的项目生成的PE文件WpfApplication1.exe进行反编译,
(2).找到InItializeComponent方法的实现
(3). 进入Application.LoadComponent方法的实现
结合LoadComponent方法签名,可知该方法将this对象传给了XamlReader.LoadBaml方法
(4). 进入XamlReader.LoadBaml方法,该方法的签名如下:
(5). 跟进WpfXamlLoader.LoadBaml方法
(6). 跟进Load方法
(7). 跟进TransformNodes方法
总结:InitializeComponent方法确实调用了Connect方法。
同时我们也知道了WPF中的一个窗口是由3个文件组成的*.xaml,*.xaml.cs,*.g.i.cs
如上图,在InitializeComponent方法之前使用了 label1 对象,给该对象设置宽度。注意下面,编译成功了!但是这个程序是无法执行的,因为label1是null,所以this.label1.Width = 100这个赋值操作会出现异常。