【WPF】MainWindow.xaml和MainWindow.g.i.cs 文件相对应


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

 

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

 【WPF】MainWindow.xaml和MainWindow.g.i.cs 文件相对应

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

在MSDN中查看该接口的说明:

【WPF】MainWindow.xaml和MainWindow.g.i.cs 文件相对应

 

 (显然,这个中文的翻译是机器翻译的。)IComponentConnector接口的功能是:解析 MainWindow.xaml 文件然后将该文件中所有标签中具有Name或x:Name属性的标签声明与该标签对应的对象,例如在MainWindow.xaml中下面的标签

【WPF】MainWindow.xaml和MainWindow.g.i.cs 文件相对应

 

 在 MainWindow.g.i.cs 文件中生成了如下的语句

【WPF】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方法

【WPF】MainWindow.xaml和MainWindow.g.i.cs 文件相对应

 

 2. 在看InitializeComponent方法的实现,最后调用了LoadComponent方法,可以猜想InitializeComponent方法一定调用了Connect方法来实现应用对象的初始化。(如果这个方法没有调用Connect方法,那么上面那些对象就无法被实例化,就不能在MainWindow类中使用了)

【WPF】MainWindow.xaml和MainWindow.g.i.cs 文件相对应

下面通过扒源码来证明上面的猜想:

(1).将上面的项目生成的PE文件WpfApplication1.exe进行反编译,

 【WPF】MainWindow.xaml和MainWindow.g.i.cs 文件相对应

 

 

(2).找到InItializeComponent方法的实现

【WPF】MainWindow.xaml和MainWindow.g.i.cs 文件相对应

 

 

 (3). 进入Application.LoadComponent方法的实现

【WPF】MainWindow.xaml和MainWindow.g.i.cs 文件相对应

 

 结合LoadComponent方法签名,可知该方法将this对象传给了XamlReader.LoadBaml方法

(4). 进入XamlReader.LoadBaml方法,该方法的签名如下:

 【WPF】MainWindow.xaml和MainWindow.g.i.cs 文件相对应

 

 

 

(5). 跟进WpfXamlLoader.LoadBaml方法

 【WPF】MainWindow.xaml和MainWindow.g.i.cs 文件相对应

 

 

(6). 跟进Load方法

 【WPF】MainWindow.xaml和MainWindow.g.i.cs 文件相对应

 

 

(7). 跟进TransformNodes方法

 【WPF】MainWindow.xaml和MainWindow.g.i.cs 文件相对应

 

 

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

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

 

【WPF】MainWindow.xaml和MainWindow.g.i.cs 文件相对应

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

原创文章,作者:Carrie001128,如若转载,请注明出处:https://blog.ytso.com/277201.html

(0)
上一篇 2022年7月26日
下一篇 2022年7月27日

相关推荐

发表回复

登录后才能评论