关于 WP 开发中.xaml 与.xaml.cs 的关系
今天我们先来看一下在 WP8.1 开发中最长见到的几个文件之间的关系。比较论证,在看这个问题之前我们简单看看 .NET 平台其他两个不同的框架:
Windows Forms
先看看 Window Forms 中的情况,下图为在 VS 中创建的默认 Windows Forms 项目结构:

分别回顾一下每个文件以及它们之间的关系:
┣━ Properties------------------------------------------- 项目属性文件夹
┣━━━━━ AssemblyInfo.cs----------------------------------- 程序集信息声明
┣━ App.config------------------------------------------- 应用程序配置文件
┣━ Form1.cs--------------------------------------------- 窗体 Form1 类文件
┣━━━━━ Form1.Designer.cs--------------------------------- 窗体 Form1 设计 类文件
┗━ Program.cs------------------------------------------- 程序入口类文件
这里我们主要关心的就是 Form1.cs 、 Form1.Designer.cs 和 Program.cs 三个文件,下面我用 Visio 图表示一下执行流程

也就是说, Form1.cs 和 Form1.Designer.cs 最终在编译阶段形成了一个类型,所有的控件的定义和初始化(大多是由于开发者的拖拽和属性编辑操作产生)全部在 Form1.Designer.cs 这个部分类中完成,代码我就不用贴出来了。我们在开发的时候只需要关心如何在 Form1.cs 文件中操作部分类中定义的控件成员和编写一些逻辑代码,从而减少开发者的编码量。
说白了,也就是大家常说的前台 UI ( Form1.Designer.cs )和后台代码( Form1.cs )是部分类的关系,通过 partial 关键字实现。
Web Forms
再来看看 Web Form 中又是怎样的一种形式:

这里我们主要是看看 WebForm1.aspx 、 WebForm1.aspx.cs 和 WebForm1.aspx.designer.cs 这三个文件的关系:
我们都应该知道 WebForm1.aspx 文件最终也是编译成为一个类存放于一个临时的程序集,对于这个类型来讲,它派生自 WebForm1.aspx.cs 文件中定义的类,也就是说前台 UI ( WebForm1.aspx )是后台代码( WebForm1.aspx.cs )的子类。那么 WebForm1.aspx.designer.cs 又是个啥?这里就和 WinForms 一样了,它和 WebForm1.aspx.cs 最终也是编译成为一个类型,在 WebForm1.aspx.designer.cs 也都是定义了一些控件,这也是当年号称很厉害的 CodeBehind ,目的是将表现和逻辑隔离,当然我们这里不需要评价 CodeBehind ,本身也不在今天讨论的范畴之中。
Window Phone / WPF
最后来看看 WP 中怎么设计的:

以上是 Visual Studio 2013 Update 4 中创建的空白 Windows Phone 8.1 应用,其中有一个 MainPage.xaml 和 MainPage.xaml.cs 文件,那它两又是什么关系呢?难道是和 Windows Forms 又或是 Web Forms 一样吗?
答案自然是否定的,首先 XAML 文件中写的 XAML 代码实际上就是 XML 语法,官方的说法:它是一个声明对象的语言,为我们创建对象提供便捷的一种方式。与 HTML类似,特点是用来描述用户接口 ( UI )内容 。
通常我们把与 xaml文件关联的xaml.cs文件叫作代码隐藏文件。如果你引用xaml中的任何一个事件处理程序(通过事件特性,如Button的Click 事件 ),这里就是我们定义这些事件处理程序的地方。
我们先看看后台代码
1 namespace Demo1
2 {
3 /// <summary>
4 /// 可用于自身或导航至 Frame 内部的空白页。
5 /// </summary>
6 public sealed partial class MainPage : Page
7 {
8 public MainPage()
9 {
10 this.InitializeComponent();
11
12 this.NavigationCacheMode = NavigationCacheMode.Required;
13 }
14
15 /// <summary>
16 /// 在此页将要在 Frame 中显示时进行调用。
17 /// </summary>
18 /// <param name="e">描述如何访问此页的事件数据。
19 /// 此参数通常用于配置页。</param>
20 protected override void OnNavigatedTo(NavigationEventArgs e)
21 {
22 // TODO: 准备此处显示的页面。
23
24 // TODO: 如果您的应用程序包含多个页面,请确保
25 // 通过注册以下事件来处理硬件“后退”按钮:
26 // Windows.Phone.UI.Input.HardwareButtons.BackPressed 事件。
27 // 如果使用由某些模板提供的 NavigationHelper,
28 // 则系统会为您处理该事件。
29 }
30
31 private void btnHello_Click(object sender, RoutedEventArgs e)
32 {
33 txtResult.Text = "Hello World";
34 var temp = (TextBlock)base.FindName("txtResult");
35 temp.Text = "Hello World2";
36 }
37 }
38 }
MainPage.cs对于后台代码文件中定义的类同样也有个 partial ,貌似跟 Windows Phone 有点类似。但是我们找了找整个解决方案并没有发现有一个与之对应的部分类,但是根据 CodeLens 的提示,我们能发现确实有部分类的存在而且是两个类文件 MainPage.g.cs 和MainPage.g.i.cs

分别双击打开这个类文件
MainPage.g.cs
1 namespace Demo1
2 {
3 partial class MainPage : global::Windows.UI.Xaml.Controls.Page, global::Windows.UI.Xaml.Markup.IComponentConnector
4 {
5 [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Windows.UI.Xaml.Build.Tasks"," 4.0.0.0")]
6 [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
7
8 public void Connect(int connectionId, object target)
9 {
10 switch(connectionId)
11 {
12 case 1:
13 #line 22 "..\..\MainPage.xaml"
14 ((global::Windows.UI.Xaml.Controls.Primitives.ButtonBase)(target)).Click += this.btnHello_Click;
15 #line default
16 #line hidden
17 break;
18 }
19 this._contentLoaded = true;
20 }
21 }
22 }
MainPage.g.csMainPage.g.i.cs
1 namespace Demo1
2 {
3 partial class MainPage : global::Windows.UI.Xaml.Controls.Page
4 {
5 [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Windows.UI.Xaml.Build.Tasks"," 4.0.0.0")]
6 private global::Windows.UI.Xaml.Controls.Button btnHello;
7 [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Windows.UI.Xaml.Build.Tasks"," 4.0.0.0")]
8 private global::Windows.UI.Xaml.Controls.TextBlock txtResult;
9 [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Windows.UI.Xaml.Build.Tasks"," 4.0.0.0")]
10 private bool _contentLoaded;
11
12 [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Windows.UI.Xaml.Build.Tasks"," 4.0.0.0")]
13 [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
14 public void InitializeComponent()
15 {
16 if (_contentLoaded)
17 return;
18
19 _contentLoaded = true;
20 global::Windows.UI.Xaml.Application.LoadComponent(this, new global::System.Uri("ms-appx:///MainPage.xaml"), global::Windows.UI.Xaml.Controls.Primitives.ComponentResourceLocation.Application);
21
22 btnHello = (global::Windows.UI.Xaml.Controls.Button)this.FindName("btnHello");
23 txtResult = (global::Windows.UI.Xaml.Controls.TextBlock)this.FindName("txtResult");
24 }
25 }
26 }
MainPage.g.i.cs从这两个 文件中我们可以看到, MainPage 类在这里还定义了一些控件和相关的方法,并且 InitializeComponent() 方法里面加载和解析了 MainPage.xaml 文件 MainPage.cs 文件里面的 MainPage() 方面里面调用的 InitializeComponent() 方法就是在 MainPage.g.cs 文件里面定义的。在 xaml 页面中声明的控件,通常会在 .g.cs 中生成对应控件的内部字段。实际上这取决于控件是否有 x:Name 属性,只要有这个属性,都会自动调用 FindName 方法,用于把字段和页面控件关联。没有 x:Name 属性,则没有字段,这种关联会有一定的性能浪费,因为是在应用载入控件的时候,通过 LoadComponents 方法关联的,而 xaml 也是在这个时候动态解析的。
由此我们就会萌生一个动态加载 XAML 的想法:
我在页面上添加一个按钮,当按钮点击时执行如下代码:
1 int top = 100;
2 private void btnHello_Click(object sender, RoutedEventArgs e)
3 {
4 string temp = "<Button xmlns=\"http://schemas.microsoft.com/winfx/2006/xaml/presentation\" " +
5 " Content=\"这是动态加载的按钮\"/>";
6 Button btnTemp = (Button)XamlReader.Load(temp);
7 // 设置水平对其方式
8 btnTemp.HorizontalAlignment = HorizontalAlignment.Center;
9 // 让Margin每次都变一下
10 btnTemp.Margin = new Thickness(0, top-=100, 0, 0);
11 // 动态设置点击事件
12 btnTemp.Click += btnHello_Click;
13 grid.Children.Add(btnTemp);
14 }
动态加载按钮XAML效果如下图所示:

通过 C# 创建按钮的形式:
1 /// <summary>
2 /// 在此页将要在 Frame 中显示时进行调用。
3 /// </summary>
4 /// <param name="e">描述如何访问此页的事件数据。
5 /// 此参数通常用于配置页。</param>
6 protected override void OnNavigatedTo(NavigationEventArgs e)
7 {
8 // TODO: 准备此处显示的页面。
9
10 // TODO: 如果您的应用程序包含多个页面,请确保
11 // 通过注册以下事件来处理硬件“后退”按钮:
12 // Windows.Phone.UI.Input.HardwareButtons.BackPressed 事件。
13 // 如果使用由某些模板提供的 NavigationHelper,
14 // 则系统会为您处理该事件。
15
16 Button btn = new Button();
17 btn.Content = "代码创建的按钮";
18 btn.Click += btnHello_Click;
19 grid.Children.Add(btn);
20 }
通过C#代码创建控件虽然我们可以这样创建对象,但是这种形式就丧失我们 XAML 创建页面元素对象的优势了!
总结一下: XAML 只是创建对象的一种便捷方式,类似于一种“命令”的形式,跟后台代码没有关系,只是在后台执行的时候键 xaml 文件当做资源去载入罢了!
结合这个几个平台来看,认为微软为开发者考虑的太多,有的时候反倒是形成一种“负担”!

浙公网安备 33010602011771号