一个 MainWindow.xaml 文件和一个 MainWindow.g.i.cs 文件相对应。

39 篇文章 2 订阅

一个 MainWindow.xaml 文件和一个 MainWindow.g.i.cs 文件相对应。

如上图所示窗口类文件被定义为partial的就是因为类 MainWindow 在 MainWindow .g.i.cs文件中还有定义,如下

在 MainWindow.g.i.cs 文件中 MainWindow 的完整声明如下:

public partial class MainWindow : System.Windows.Window, System.Windows.Markup.IComponentConnector

注意到: MainWindow 类除了继承了System.Windows.Window类(这个在 MainWindow.xaml.cs文件中已有声明)还实现了接口: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 接口有两个方法:

下面为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 文件的根元素指定的对象的实例。)

下面为Connect方法的实现:

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;

}

由这个实现可知,此时MainWindow.g.i.cs文件中声明的那些对象引用就可以正确使用了。

在考虑这几个函数的调用过程。

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方法,该方法的签名如下:

如上图this对象又被传给了WpfXamlLoader.LoadBaml方法

(5). 跟进WpfXamlLoader.LoadBaml方法

注意到this对象被传入到方法Load中

(6). 跟进Load方法

该方法将this对象(这个方法中的名称为rootObject)转换成IStyleConnector对象styleConnector,并将该对象传入TransformNodes方法

(7). 跟进TransformNodes方法

通过七次方法的调运 We find it!

总结:InitializeComponent方法确实调用了Connect方法。

同时我们也知道了WPF中的一个窗口是由3个文件组成的*.xaml,*.xaml.cs,*.g.i.cs

 

由上面的分析可知,只有MainWindow的构造函数中的InitializeComponent方法被调用完毕之后才可以使用MainWindow.xaml中的那些由标签“创建”的对象,如果在之前调用,会出现什么情况呢?

如上图,在InitializeComponent方法之前使用了 label1 对象,给该对象设置宽度。注意下面,编译成功了!但是这个程序是无法执行的,因为label1是null,所以this.label1.Width = 100这个赋值操作会出现异常。

  • 1
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值