【WPF学习】第四章 加载和编译XAML
前面已经介绍过,尽管XAML和WPF这两种技术具有相互补充的作用,但他们也是相互独立的。因此,完全可以创建不使用XAML和WPF应用程序。
总之,可使用三种不同的编码方式来创建WPF应用程序:
- 只使用代码。这是在Visual Studio中为Windows窗体应用程序使用的传统方法。它通过代码语句生成用户界面。
- 使用代码和未经编译的标记(XAML)。这种具体方式对于某些特殊情况是很有意义的,例如创建高度动态化的用户界面。这种方式在运行时使用System.Windows.Markup名称空间中的XamlReader类,从XAML文件中加载部分用户界面。
- 使用代码和编译过的标记(BAML)。对于WPF而言这是一种更好的方式,也是Visual Studio支持的一种方式。这种方式为每个窗口创建了一个XAML模板,这个XAML模板被编译为BAML,并嵌入到最终的程序集中。编译过的BAML在运行时被提取出来,用于重新生成用户界面。
一、只使用代码
对于编写WPF应用程序,只使用代码进行开发而不适用任何XAML的做法并不常见(但是仍然完全支持)。只使用代码进行开发的明显缺点在于,可能会使编写WPF应用程序成为极端乏味的工作。WPF控件没有包含参数化的构造函数,因此即使为窗口添加一个简单按钮也需要编写几行代码。
只使用代码进行开发的一个潜在的有点是可以随意定制应用程序。例如,可根据数据库记录中的信息生成充满输入控件的窗体,或可根据当前的用户酌情添加或替换控件。需要的所有内容只不过是少量的条件逻辑。相比之下,如果使用XAML文档,它们只能作为固定不变的资源嵌入到程序集中。
下面代码用于生成一个普通窗体,该窗体包含一个按钮和一个时间处理程序。在创建窗口时,构造函数调用InitializeComponent()方法,该方法实例化并配置这个按钮和窗体,并连接(hook up)事件处理程序.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup; namespace WPFCompileByCode
{
public class Window1:Window
{
private Button button1;
public Window1()
{
InitializeComponent();
}
private void InitializeComponent()
{
this.Width = ;
this.Height = ;
this.Left = ;
this.Top = ;
this.Title = "Code Only Window"; DockPanel panel = new DockPanel();
button1 = new Button();
button1.Content = "Click Me";
button1.Margin = new Thickness();
button1.Click += button1_Click;
IAddChild container = panel;
container.AddChild(button1); container = this;
container.AddChild(panel);
} void button1_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("Click Me");
}
}
}
从概念上讲,本例中的Windows1类更像传统的Windows窗体硬功程序中的窗体。它继承自Window基类,并为每个控件添加一个私有程序变量。为了清晰起见,该类在专门的InitializeComponent()方法中执行初始化操作。
为启动该应用程序,可在Main()方法中添加如下代码:
public class Program:Application
{
[STAThread]
public static void Main()
{
Program app = new Program();
app.MainWindow = new Window1();
app.MainWindow.ShowDialog();
}
}
运行效果图如下所示:

二、使用代码和未编译的XAML
使用XAML最有趣的方式之一是使用XamlReader类随时解析它。例如,假设创建一个Window1.xaml的文件,且内容如下所示:
<DockPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Button Name="button1" Margin="30">Click Me</Button>
</DockPanel>
编写一个类,用来加载xaml文件。如下代码所示:
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup; namespace WPFCompileByXaml
{
public class Window1:Window
{
private Button button1;
public Window1(string xamlFile)
{
this.Width = ;
this.Height = ;
this.Left = ;
this.Top = ; DependencyObject rootElement;
using (FileStream fs = new FileStream(xamlFile, FileMode.Open))
{
rootElement = (DependencyObject)XamlReader.Load(fs);
} this.Content = rootElement; button1 = (Button)LogicalTreeHelper.FindLogicalNode(rootElement, "button1"); /*
FrameworkElement frameworkElement = (FrameworkElement)rootElement;
button1 = (Button)frameworkElement.FindName("button1");
*/ button1.Click += button1_Click;
} void button1_Click(object sender, RoutedEventArgs e)
{
//MessageBox.Show("Hello ,you click me!");
button1.Content = "Thank you!";
}
}
}
在此,构造函数接收XAML文件名作为参数。然后构造函数打开一个FileStream对象,并使用XamlReader.Load()方法将这个文件中的内容转换为DependencyObject对象,DependencyObject是所有WPF控件继承的基类。DependencyObject对象可放在任意类型的容器中,但在这个示例中它被用作整个窗体的内容。
为操作元素——如Windows1.xaml文件中的按钮,需要在动态加载的内容中查找相应的控件对象。LogicalTreeHelper类可达到该目的,因为它具有查找一颗完整控件对象的能力,它可以查找所需的许多层,直到找到具有指定名称的对象。然后将一个事件处理程序关联到Button.Click事件。
另外一种方法是使用FrameworkElement.FindName()方法,在这个示例中,根元素是DockPanel对象,与WPF窗口中的所有控件一样,DockPanel类继承自FrameworkElment类,这意味着可使用如下等效的方法:
FrameworkElement frameworkElement = (FrameworkElement)rootElement;
button1 = (Button)frameworkElement.FindName("button1");
代替下面的代码:
button1 = (Button)LogicalTreeHelper.FindLogicalNode(rootElement, "button1");
在这个示例中,Window1.xaml文件和可执行的应用程序位于同一文件夹中,并一同发布。然而,尽管该文件没有被编译为应用程序的一部分,但仍可以将其添加到Visual Studio项目中。这样可以更方便地管理文件,并使用Visual Studio设计用户界面(假定使用.xaml文件扩展名,从而使用Visual Studio能够识别出该文档是XAML文档)。
如果使用这种情况,确保松散的XAML文件不会像传统的XAML文件那样被编译或嵌入到项目中。将文件添加到项目后,在解决方案中选中该文件,然后使用属性窗口,将Build Action设置为None,并将Copy to Output Directory 设置为Copy Always。
显然,先将XAML编译为BAML,再在运行时加载BAML,比动态加载XAML的效率高,当用户界面比较复杂时尤其如此。然而,这种编码模式为构建动态的用户界面提供了多种可能。例如,可创建通用的检测应用程序,从Web服务中读取窗体文件,然后显示相应的检测控件(标签、文本框和复选框等)。窗体文件可以是具有WPF标签的普通XML文件,使用XamlReader类将该文档加载到一个已经存在的窗体中。检测之后,为了收集结果,只需要枚举所有输入控件并提取他们的内容即可。
三、使用代码和编译过的XAML
当编译WPF应用程序时,Visual Studio使用分为两个阶段的编译处理过程。第一阶段将XAML文件编译为BAML。例如,如果项目中包含名为Window1.xaml的文件,编译器将创建名为Window1.baml的临时文件,并将该文件放在项目文件夹的obj/Debug字文件夹中。同时,使用选择的语言为窗口创建部分类。例如,如果使用C#语言,编译器将在obj/Debug文件夹中创建名为Window1.g.cs的文件。g代表生产的(generated)。
部分类包括如下三部分内容:
- 窗口中所有控件的字段。
- 从程序集中加载BAML的代码,由此创建对象树。当构造函数调用InitializeComponent()方法时将发生这种情况。
- 将恰当的控件对象指定给各个字段以及连接所有事件处理程序的代码。该过程是在名为Connect()的方法中完成,BAML解析器在每次发现一个已经命名的对象时调用该方法一次。
部分类不包含实例化和初始化控件的代码,因为这项任务由WPF引擎在使用Application.LoadComponent()方法处理BAML时执行。
具体实例,可以查看Visual Studio编译后的WPF应用程序。
【WPF学习】第四章 加载和编译XAML的更多相关文章
- XAMl使用其他命名空间中的类型及加载和编译
以前我们讲过XAMl命名空间.为了使便宜钱知道XAMl文档中元素对应的.NET类型,需要知道XAMl明档中指定特定的两个命名空间.XAML是一种实例化.NET对象的通用方法 ,除了可以实例化一些标准的 ...
- Android学习笔记_50_(转 四种加载方式详解(standard singleTop singleTask singleInstance)
Android之四种加载方式 (http://marshal.easymorse.com/archives/2950 图片) 在多Activity开发中,有可能是自己应用之间的Activity跳转,或 ...
- Android学习记录(8)—Activity的四种加载模式及有关Activity横竖屏切换的问题
Activity有四种加载模式:standard(默认), singleTop, singleTask和 singleInstance.以下逐一举例说明他们的区别: standard:Activity ...
- 《Entity Framework 6 Recipes》中文翻译系列 (22) -----第五章 加载实体和导航属性之延迟加载
翻译的初衷以及为什么选择<Entity Framework 6 Recipes>来学习,请看本系列开篇 第五章 加载实体和导航属性 实体框架提供了非常棒的建模环境,它允许开发人员可视化地使 ...
- RX系列四 | RxAndroid | 加载图片 | 提交表单
RX系列四 | RxAndroid | 加载图片 | 提交表单 说实话,学RxJava就是为了我们在Android中运用的更加顺手一点,也就是RxAndroid,我们还是先一步步来,学会怎么去用的比较 ...
- Java虚拟机JVM学习02 类的加载概述
Java虚拟机JVM学习02 类的加载概述 类的加载 类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区的方法区内,然后在堆区创建一个java.lang.Class对 ...
- Activity有四种加载模式(转)
Activity有四种加载模式: standard singleTop singleTask singleInstance 在多Activity开发中,有可能是自己应用之间的Activity跳转,或者 ...
- ios网络学习------4 UIWebView的加载本地数据的三种方式
ios网络学习------4 UIWebView的加载本地数据的三种方式 分类: IOS2014-06-27 12:56 959人阅读 评论(0) 收藏 举报 UIWebView是IOS内置的浏览器, ...
- 活动 Activity 四种加载模式
singleTop要求如果创建intent的时候栈顶已经有要创建的Activity的实例,则将intent发送给该实例,而不发送给新的实例.(注意是栈顶,不在栈顶照样创建新实例!) singleTas ...
随机推荐
- Python学习3月5号【python编程 从入门到实践】---》笔记
---恢复内容开始--- 1.变量 一.只能包含字母.数字.下划线.****不能以数字开头作变量 二.不能包含空格, 三.不要将python关键字和函数名用作变量名 四.最好能有描述性和简短的特征 五 ...
- 洛谷$P2486\ [SDOI2011]$染色 线段树+树链剖分
正解:线段树+树链剖分 解题报告: 传送门$QwQ$ 其实是道蛮板子的题,,,但因为我写得很呆然后写了贼久之后发现想法有问题要重构,就很难受,就先写个题解算了$kk$ 考虑先跑个树剖,然后按$dfn$ ...
- vps远程桌面服务器管理
vps服务器是没有远程桌面系统的,本地电脑要有远程桌面的组件或者专业的远程桌面管理工具,如果出于安全考虑关闭了3389端口(这是系统自带远程桌面的端口),你可以试试iis7远程桌面管理工具,这个还是很 ...
- IDEA到期了?不用怕,最新的永久激活送给你
今天发现好多人的IDEA激活码都到期了,IDEA社区版又不能满足开发需求,因此写这篇IDEA的激活文章,希望对大家有用. 以下方法的破解文件的是永久破解的,不存在过期时间. 当然,有条件还是买正版授权 ...
- nodeJS实现识别验证码(tesseract-ocr+GraphicsMagick)
背景 最近在写一个爬虫的小工具,卡在登录这里. 想爬的网站需要登录才能获取数据,登录又需要输入验证码. 好在验证码是简单的验证码,还可以自己识别试试. 需求分析 1.保存验证码图片 2.识别验证码 3 ...
- 解决阿里云专有网络ftp无法远程链接
配置好ftp后本机测试可用但无法远程连接 网络上找了很多方法,配置防火墙出入站规则均无效 提交阿里云工单,给出解决方法,测试后可用
- java动态代理、Proxy与InvocationHandler
看了好多关于代理的文章,理解和整理一下. 1.代理的基本构成 抽象角色:声明真实对象和代理对象的共同接口,这样可在任何使用真实对象的地方都可以使用代理对象. 代理角色:代理对象内部含有真实对象的引用, ...
- Ceph日常运维管理和排错 -- <7>
Ceph日常运维管理 集群监控管理 集群整体运行状态 [root@cephnode01 ~]# ceph -s cluster: id: 8230a918-a0de-4784-9ab8-cd2a2b8 ...
- TensorFlow——tf.contrib.layers库中的相关API
在TensorFlow中封装好了一个高级库,tf.contrib.layers库封装了很多的函数,使用这个高级库来开发将会提高效率,卷积函数使用tf.contrib.layers.conv2d,池化函 ...
- VMware Workstation CentOS7 Linux 学习之路(4)--守护服务(Supervisor)
目前存在三个问题 问题1:ASP.NET Core应用程序运行在shell之中,如果关闭shell则会发现ASP.NET Core应用被关闭,从而导致应用无法访问,这种情况当然是我们不想遇到的,而且生 ...