Overview: 很早就想写些文章来总结一下自己学习一些新知识的经历,一来可以在日后查找,其到一个索引的作用;二来可以和爱好技术的朋友们共同交流,鉴于工作缘故始终无法抽身写。最近源于对WPF和Silverlight的浓厚兴趣和项目中要用的缘故,特此和大家分享一些学习WPF/Silverlight的过程。这个系列会简单回顾一些WPF中基本的概念和入门知识,以此来构成创建WPF程序(甚至于Silverlight程序,其在很大程度上有些依赖于WPF)的基础。本人不曾写过很多blog,再者WPF本身对程序员的一些创新思维挑战对我来说都是很大的难点,不周之处且请各位大侠指点。不胜感谢。

Topic: 本文为此系列的第一张,将大致介绍一下在.NET3.0中正式启用的XAML的相关概念以及其在WPF中的应用。

总所周知,随着.NET Framework 3.0的到来,其包含的几个组件Windows Workflow Fundation, Windows Communication Foundation, Windows Cardspace以及我们要讲的Windows Presentation Foundation逐渐吸引了大家很多的目光,作为.NET Framework的一部分,其提供了很多优越性而使得我们的编程模式以至变成思路都发生了很多的改观。而在其最主要的几个优点上,其引入的"声明式描述"带给我们焕然一新的感觉,而这就是XAML的主要功劳。XAML作为可以在很大程度上替代一些coding语句的声明式XML,.NET Framework提供了对XAML的编译支持,而这无非又让我们看到了和HTML/XML很相似的风格,这种看似很随意,很灵活的风格给我们带来了新的编程体验。WF, WPF都采用XAML来进行部分过程的描述。

1. 什么是XAML?

XAML是一种相对简单、通用的声明式变成语言,它适合于构建和初始化.NET对象。它由一些规则和关键字构成,但是它自己没有任何有意义的元素。在没有WPF/WF框架的基础上讨论XAML就象在没有.NET Framework的基础上讨论C#一样是没有任何意义的,因为它本身并不能提供任何有意义的元素,需要由WPF/WF框架或者说.NET XAML编译器和WPF应用框架来承载、理解XAML的表达意义。由于XAML的通用性,实际上你可以把它应用于任何.NET技术。然而,是否在使用WPF时使用XAML是可选的,每一个XAML能做的事情完全可以由任何一种你喜欢的.NET语言来实现,但是反过来是不行的。在后边的解释中你会看到,实际上针对XAML的解释,XAML编译器是把XAML语句与.NET对象/类型联系在一起的,将XAML纳入和.NET类型相同的模型中控制是他们为什么能够互相兼容的重要原因。

例如如下的XAML片段和.NET C#语句所表达的结果是相同的:

XAML: <Button xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation Content="OK" />

C#:     System.Windows.Controls.Button btnOk = new System.Windows.Controls.Button();

          b.Content = "OK";

2. XAML元素及特性

2.1 XAML命名空间

XAML文件的跟对象元素必须至少有一个XML命名空间,用于验证自己和其子元素。你可以在根元素或子元素上声明额外的XML命名空间来标识你的自定义元素,但每个命名空间下的标识符都必须有一个唯一的前缀。我们把http://schemas.microsoft.com/winfx/2006/xaml/presentation作为默认(主要)的命名空间,我们在使用大多数控件时都使用此主命名空间,因为WPF通过硬编码的方式将此命名空间与.NET的命名空间进行了映射。而对于次命名空间,我们通常需要添加前缀来表示其命名空间在以下范围内被引用,中就像C#中的using System;一样,声明了一个可以引用的命名空间。以下代码片段演示了如何使用主命名空间和次命名空间:

<UserControl x:Class="MediaPlayer.Page"
    xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="450" Height="300">
    <Grid x:Name="LayoutRoot" Background="White">
        <TextBlock FontSize="12" FontWeight="Bold" HorizontalAlignment="Center">Vedio Player</TextBlock>
    </Grid>
</UserControl>

2.2 属性元素(Property Element)

属性元素本质上是为了增加在元素中包含子元素的一种方式。它是XAML提供的用来替代更加详细的语法来设置属性值的方法。例如:

<Button xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" >

    <Button.Content>

        </Rectangle Height="40" Width="40" Fill="Black" />

    </Button.Content>

</Button>

Content属性现在被设置为一个XML元素而不是XML属性,Button.Content中的句点用来区分对象元素与属性元素。属性元素总会以“类型名.属性名”的形式出现并包含在“类型名”对象元素中。说白了,属性元素是为了扩展某类型的属性而存在的。

2.3 类型转换器(Type Converter)

在XAML中实际上我们都是以String的类型来赋予各种属性值的,但在对于.NET这样的有类型库来讲,不是所有的属性值都是为String的,比如对于Button的Background属性,在C#中我们需要这样来写:

System.Windows.Controls.Button b = new System.Windows.Controls.Button();

b.Content = “OK”;

b.Background = System.Windows.Media.Brushes.White;

这段C#代码等同于下边的XAML代码片段:

<Button xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Content="OK">

   <Button.Background>

White

</Button.Background>

</Button>

可以很容易的发现,在C#中Button的Background属性对应的类型为System.Windows.Media.Brushes,而在XAML中由于无法具有强类型表现方式(当然它是通过另一种替代方式来表现的,只是表面上都是String)这里选择了用White这个字符串来表示我们想用White颜色来作为背景颜色。因为这个代码片段是等同的,我们可以自然的将White字符串认为等同于System.Windows.Media.Brushes.White。而这个映射就是通过类型转换器来实现的。这里就通过BrushConverter和ColorConverter一起将White设置为Button的Background(因为Background实际上是Brush类型的)-而这些转换器都是派生自TypeConverter的类。PS:这里另一个参考就是我们在写ASP.NET的自定义控件时里边都会有定义一些Converter,但他们是不一样的。

其实以上的XAML代码是简化的写法,更好的理解它我们可以写为下边这种方式(等价的):

<Button xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" Content="OK">

   <Button.Background>

<SolidColorBrush Color=”White” />

</Button.Background>

</Button>

而对应的C#代码,为了更好的理解类型转换器,我们改写为下边这种方式:

System.Windows.Controls.Button b = new System.Windows.Controls.Button();

b.Content = “OK”;

b.Background = (Brush)System.ComponentModel.TypeDescriptor.GetConverter(typeof(Brush)).ConvertFromInvariantString(“White”);

2.4 扩展标记

扩展标记就像类型转换器一样是用来扩展XAML的表达能力的,他们都可以在运行时计算字符串特性的值并生成合适的基于字符串的对象。但与类型转换器不同的是,标记扩展是通过XAML的显式的、一致的语法调用的,因此这是比较好的扩展XAML的方法。一般来讲我们更多的把扩展标记应用于Resource, Binding等。如下边的例子我们通常会用来标记当前元素/对象是使用的样式(Resource):

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Title="Simple Window">

<Window.Resources>

<SolidColorBrush x:Key="backgroundBrush">Yellow</SolidColorBrush>

<SolidColorBrush x:Key="borderBrush">Red</SolidColorBrush>

</Window.Resources>

<DockPanel>

<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal"

HorizontalAlignment="Center">

<Button Background="{StaticResource backgroundBrush}"

BorderBrush="{StaticResource borderBrush}" Margin="5">

<Image Height="21" Source="zoom.gif"/>

</Button>

</StackPanel>

</DockPanel>

</Window >

2.5 集合项

XAML允许将项添加到支持索引的两种类型的集合中:List和Dictionary.

List: 实现了System.Collections.IList接口的集合,它的Items属性是实现了IList的ItemCollection类型;

Dictionary: 实现了System.Collections.IDictionary接口的集合,能够支持在过程式代码中添加、移除和枚举键/值对。

下边两个例子展示了在XAML中如何应用集合项。注意:由于Content属性实际上相当于在.NET对象中的默认属性(例如在自定义控件中的默认属性),所以可以将内容属性的值作为此对象的直接子元素而不用Content属性元素来标明,以此来简化XAML的复杂度。

<ListBox xmlns=http://schemas.microsoft.com/winfx/2006/xaml/presentation>

<ListBox.Items> <!—直接将子元素作为内容属性—>

<ListBoxItem Content=”Item 1” /><!—等同于<ListBoxItem>Item 1</ListBoxItem>-->

<ListBoxItem Content=”Item 2” />

</ListBox.Items>

</ListBox>

3. 编译XAML

XAML编译 通常包括三项事情:将一个XAML文件转换为一种特殊的二进制格式(BAML:Binary Application Markup Language);将转换好的二进制资源嵌入到正在被创建的程序集中;然后执行链接操作将XAML和过程式代码自动连接起来。

每个XAML都会在编译过程中产生过程式代码,这是在编译过程中动态生成的,但这些过程代码仅仅是“粘合代码”Glue Code,类似于在运行时加载和解析松散XAML文件时所需要的代码(和普通的Win Form自动生成的代码用途类似),你可以在.g.cs(.g.vb)文件中找到这些代码。

同样你可以在XAML中混合使用过程代码(Code inside),当XAML编译后X:code元素中的部分将会放到部分类.g.cs文件中。

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x=http://schemas.microsoft.com/winfx/2006/xaml>

<Button Click=”button_Click”>OK</Button>

<x:Code><![CDATA[

Void button_Click(object sender, RouteEventArgs e)

{

this.Close();

}

]]></x:Code>

</Window>

posted on 2008-06-15 15:18  Allan.  阅读(3020)  评论(5编辑  收藏  举报