XAML:可扩展应用程序标记语言
BAML实际上就是 XAML 的二进制表示。(BAML 二进制应用程序标记语言)所有 XAML 文件都被转换为 BAML,并且这些 BAML 然后作为资源被嵌入到最终的 DLL 或 EXE 程序集中。
XAML标准:
XAML 顶级元素:
XAML 名称空间
在创建的所有 WPF XAML 文档中都会使用这两个名称空间:
1.InitalizeComponent()方法
InitalizeComponent()方法的所有工作就是调用 System.Windows.Application 类的 LoadedComponent()方法。LoadComponent()方法从程序集中提取 BAML(编译过的 XAML),并用它来构建用户界面。当解析 BAML 时,它会创建每个控件对象,设置其属性,并关联所有事件处理程序。
在后台CS代码:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public override string ToString()
{
return string.Format("{0} {1}", FirstName, LastName);
}
}
在XAML:
只要属性的类型可以表示为字符串,或者可以把字符串转换为属性类型,就可以把属性设置为特性。
<Button Content="Click me" Background="LightGoldenrodYellow">Button>
比如Button中Background用作为子元素Button.Backround。
<Button>
<Button.Background>
<LinearGradientBrush StartPoint="0.5,0.0" EndPoint="0.5,1.0">
<GradientStop Offset="0" Color="Yellow"/>
<GradientStop Offset="0.3" Color="Orange"/>
<GradientStop Offset="0.7" Color="Red"/>
<GradientStop Offset="1" Color="DarkRed"/>
LinearGradientBrush>
Button.Background>
Button>
设置 GradientStop 为属性,并把 GradientStopCollection 元素设置为它的子元素。
<Button>
<Button.Background>
<LinearGradientBrush StartPoint="0.5,0.0" EndPoint="0.5,1.0">
<LinearGradientBrush.GradientStops>
<GradientStopCollection>
<GradientStop Offset="0" Color="Yellow"/>
<GradientStop Offset="0.3" Color="Orange"/>
<GradientStop Offset="0.7" Color="Red"/>
<GradientStop Offset="1" Color="DarkRed"/>
GradientStopCollection>
LinearGradientBrush.GradientStops>
LinearGradientBrush>
Button.Background>
Button>
元素属性包含一组子属性;
使用标记扩展访问资源。
有些情况下,不可能硬编码属性值。例如,可能希望将属性设置为一个已经存在的对象,或者可能希望通过将一个属性绑定到另一个控件来动态地设置属性值。
标记扩展可用于嵌套标签或 XML 特性中(用于 XML 特性的情况更常规)。当用在特性中时,它们总是被花括号{}包围起来。
例如:
标记扩展使用{标记扩展类 参数}语法;
所有标记扩展都由继承自 System.Windows.Marup.MarkupExtension 基类的类实现。MarkupExtension 基类十分简单——它提供了一个简单的ProvideValue()方法来获取所期望的数值。
当 XAML 解析器遇到上述语句时,它将创建 StaticExtension 类的一个实例(传递字符串 SystemColors.ActiveCaptionBrush 作为构造函数的参数),然后调用 ProvideValue()方法获取 SystemColors.ActiveCaption.Brush 静态属性返回的对象。最后使用检索的对象设置 cmdAnswer 按钮的Foreground 属性。
可用cs代码:
cmdAnswer.Foreground = SystemColors.ActiveCaptionBrush;
也可用作嵌套属性:
在后台CS代码:
public enum Operation
{
Add,
Subtract,
Multiply,
Divide
}
[MarkupExtensionReturnType(typeof(string))]
public class CalculatorExtension : MarkupExtension
{
public CalculatorExtension()
{
}
public double X { get; set; }
public double Y { get; set; }
public Operation Operation { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider)
{
IProvideValueTarget provideValue =
serviceProvider.GetService(typeof(IProvideValueTarget))
as IProvideValueTarget;
if(provideValue != null)
{
var host = provideValue.TargetObject as FrameworkElement;
var prop = provideValue.TargetProperty as DependencyProperty;
}
double result = 0;
switch(Operation)
{
case Operation.Add:
result = X + Y;
break;
case Operation.Subtract:
result = X - Y;
break;
case Operation.Multiply:
result = X * Y;
break;
case Operation.Divide:
result = X / Y;
break;
default:
throw new ArgumentException("invalid operation");
}
return result.ToString();
}
}
在XAML:
<TextBlock Text="{local:Calculator Operation=Add,X=3,Y=4}"/>
<TextBlock>
<TextBlock.Text>
<local:CalculatorExtension>
<local:CalculatorExtension.Operation>
<local:Operation>Multiplylocal:Operation>
local:CalculatorExtension.Operation>
<local:CalculatorExtension.X>7local:CalculatorExtension.X>
<local:CalculatorExtension.Y>11local:CalculatorExtension.Y>
local:CalculatorExtension>
TextBlock.Text>
TextBlock>
附加属性可用于多个控件但在另一个类中定义的属性。在 WPF 中,附加属性常用于控件布局。
附加属性始终使用包含两个部分的命名形式:定义类型.属性名。这种命名形式也用于区分开普通属性和附加属性。
创建 WPF 应用程序可使用三种不同的编码方式:
只使用代码。
使用代码和未经编译的标记(XAML)。 处理某些特殊情况时。例如创建高度动态化的用户界面。这种方式在运行时使用 System,Windows.Markup 名称空间中的 XamlReader 类,从 XAML 文件中加载部分用户界面。
使用代码和编译过的标记(BAML)。 对这种方式为每个窗口创建一个 XAML 模板,这个 XAML 模板被编译为 BAML,并嵌入到最终的程序集中。编译过的 BAML 在运行时被提取出来,用于重新生成用户界面。
(1)只使用代码
优点: 可以随意定制应用程序。缺点:编写代码麻烦。
(2)使用代码和未经编译的 XAML
Please click me.
参数xamlFile作为上述的xaml文件,以下为动态加载 XAML 代码。
using System.Windows;
using System.Windows.Controls;
using System.Windows.Markup;
using System.IO;
public class Window1:Window
{
private Button button1;
public Window1()
{
InitializeComponent();
}
public Window1(string xamlFile)
{
//Configure the form.
this.Width = this.Height = 285;
this.Left = this.Top = 100;
this.Title = "Dynamically Loaded XAML";
//Get the XAML content from an external file.
DependencyObject rootElement;
using (FileStream fs = new FilesStream(xamlFile,FileMode.Open))
{
rootElement = (DependencyObject)XamlReader.Load(fs);
}
//Insert the markup into this window.
this.Content = rootElement;
//Find the control with the appropriate name.
button1 = (Button)LogincalTreeHelper.FindLogicalNone(rootElement,"button1");
//Wire up the event handler.
button1.Click += button1_Click;
}
private void button1_Click(object sender,RoutedEventArgs e)
{
button1.Content = "Thank you.";
}
}
操纵元素,如按钮,需要在动态加载的内容中查找相应的控件对象。
第一种方式:LogicalTreeHelper。
button1 = (Button)LogincalTreeHelper.FindLogicalNode(rootElement,"button1");
第二种方式:FrameworkElement.FindName()方法。
FrameworkElement frameworkElement = (FrameworkElement) rootElement;
button1 = (Button)frameworkElement.FindName("button1");
注意: 如果使用这种方法,确保松散的 XAML 文件不会像传统的 XAML 文件那样被编译或嵌入到项目中。
先将 XAML 编译为 BAML,再运行时加载 BAML,比动态加载 XAML 的效率高,当用户界面比较复杂时尤其如此。
(3)使用代码和编译过的 XAML
当编译 WPF 应用程序时, Visual Studio 使用分为两个阶段的编译处理过程。第一阶段将 XAML 文件编译为 BAML 。第二阶段Visual Studio 使用合适的语言编译器来编译代码和生成的部分类文件。