XAML中的属性和事件

XAML中的属性和事件

迄今为止,你已经讨论了一个没啥意思的例子—有一个空栅格控件的空白页面。在继续学习之前,介绍一个包含更多元素的真实页面是有必要的。图2-1显示了一个自动回答问题的例子。

(图2-1

eight ball页面包括四个元素:一个栅格(在银光中用来安排布局的最常用的工具),两个文本框对象和一个按钮。被用来安排和配置这些元素的标记语言比上一个例子更加引人注目了。下面是一个用省略号的小型代码来说明整体结构:

<UserControl x:Class="EightBall.MainPage"

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

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

Width="400" Height="300">

<Grid x:Name="grid1">

<Grid.Background>

...

</Grid.Background>

<Grid.RowDefinitions>

...

</Grid.RowDefinitions>

<TextBox x:Name="txtQuestion" ... >

</TextBox>

<Button x:Name="cmdAnswer" ... >

</Button>

<TextBox x:Name="txtAnswer" ... >

</TextBox>

</Grid>

</UserControl>

在下面的章节中,你将探索这个文档的内容并且学到XAML的语法。

简单的属性和类型转换

就像你看到的,一个XML元素的特性设置相对应的银光对象的属性。比如,在eight ball这个例子中文本框配置了alignment, marginfont属性:

<TextBox x:Name="txtQuestion"

VerticalAlignment="Stretch" HorizontalAlignment="Stretch"

FontFamily="Verdana" FontSize="24" Foreground="Green" ... >

为了工作起来,System.Windows.Controls.TextBox类必须提供下面的属性:VerticalAlignment, HorizontalAlignment, FontFamily, FontSizeForeground。在下面的章节中你将学到每一个属性的意思:

提示:有一些特殊的字符不能直接写入属性字符串,包括quotation 标记,&符号还有两个<>尖括号。为了用这些值,你必须用相等的XML字符来替换它们。用&quot;替换quotation 标记, &amp; 替换&, &lt;替换< (小于) 字符, &gt; 替换 > (大于) 字符。当然,这些限制是XML的细节,它并不影响你在代码中设置属性。

为了让属性系统工作,XAML解释器需要处理比你想象的更多的工作。XML特性的值通常是一个简单的字符串,但是对象的属性可以是任何.NET类型。在上面的例子中,有两个属性枚举型(VerticalAlignment HorizontalAlignment),一个字符串(FontFamily),一个整形(FontSize),还有一个Brush 对象 (Foreground)

为了跨越字符串型和非字符串型的鸿沟,XAML解释器需要进行转换。这种转换是被类型转换器执行的,它是从完整的.NET框架中借用过来的基础部件。

本质上说,类型转换器在生命周期中扮演一个角色——它提供一套有用的方法,可以吧一个特定的.NET数据类型正向和反向转换到任何.NET的类型,比如在这个例子中的字符串。XAML解释器由两步来发现类型转换器:

1、              它检测属性的声明,搜索类型转换器属性。(如果有,类型转换特性命令相关类处理转换)比如,当你用属性Foreground.NET就检测Foreground属性的声明。

2、              如果在属性声明中没有类型转换器特性,XAML解释器检测相应数据类型的类的声明。比如,Foreground属性用了Brush对象。Brush类(和它的派生类)用到了Brush转换器(typeof(BrushConverter))特性的声明。

3、              如果在属性声明和类的声明中没有相关的类型转换XAML将生产错误。

这个系统是简单但很柔性的。如果在类的层次你设置了类型转换器,就可以在用这个类的时候转换所有的属性。另一方面,如果你想对特殊的属性进行调整类型转换的方式,你可以在属性声明中用类型转换器予以替换。

在代码中使用类型转换器在技术上是可行的,但是语法有些麻烦。通常最好的办法是直接设置属性——这样不仅很快,而且可以避免打字的潜在错误,而这种错误在运行前是难以扑捉的。对这个问题XAML没有反映因为XAML是在编译时才解析和验证的。

注释 XAML, 象所有基于XML的语言都是区分大小写的,这意味着你不能用 <button> 替换<Button>。然而类型转换通常不区分大小写,这意味着Foreground="White"Foreground="white" 有同样的结果。

复杂属性

作为一个便利的类型转换器,他们不用对所有的想法都要操作。比如,一些属性是自带属性设置的全能对象。尽管建立一个属性转换可用的字符串是可能的,但语法可能比较麻烦并且容易出错。

幸运的是XAML提供了另一个操作方法:属性元素语法。用这种语法,你可以用Parent.PropertyName 这种格式来加一个子元素。比如,栅格有一个背景色属性给你提供了一个画刷在元素后面描绘区域。如果你想用复杂的画刷——一个比实体颜色填充更高级的属性——你将需要加一个命名为Grid.Background的子标签,如下所示:

<Grid x:Name="grid1">

<Grid.Background>

...

</Grid.Background>

...

</Grid>

关键的细节是在元素名称中用了句点(.)。这是和其他嵌套类型相区别的属性。

还有一个细节,那就是,一旦你识别了一个你想配置的复杂属性,你如何设置呢?这里有个窍门:在嵌套元素里,你可以加入另一个标签来实例化一个特定的类。在eight ball这个例子中(图2-1)背景是用一个渐变来填充的。为了定义这个你想要的渐变,你需要建立一个LinearGradientBrush对象。

按照XAML规则,你可以建立一个名字为LinearGradientBrush的元素来表示LinearGradientBrush对象:

<Grid x:Name="grid1">

<Grid.Background>

<LinearGradientBrush>

</LinearGradientBrush>

</Grid.Background>

...

</Grid>

LinearGradientBrush是银光命名空间集的一部分,所以你可以继续使用缺省的XML命名空间。

当然,简单的建立一个LinearGradientBrush是不够的。你还需要定义渐变的颜色。你用GradientStop 对象集合来填充LinearGradientBrush.GradientStops属性。但是GradientStops是非常复杂的以至于没办法单独设置属性值,所以你需要依赖属性元素语法:

<Grid x:Name="grid1">

<Grid.Background>

<LinearGradientBrush>

<LinearGradientBrush.GradientStops>

</LinearGradientBrush.GradientStops>

</LinearGradientBrush>

</Grid.Background>

...

</Grid>

最后,你可以用一系列GradientStop对象来填充GradientStops集合。每一个GradientStop对象都有一个偏移值和颜色属性。你可以用普通的属性语法来提供这两个值:

<Grid x:Name="grid1">

<Grid.Background>

<LinearGradientBrush>

<LinearGradientBrush.GradientStops>

<GradientStop Offset="0.00" Color="Yellow" />

<GradientStop Offset="0.50" Color="White" />

<GradientStop Offset="1.00" Color="Purple" />

</LinearGradientBrush.GradientStops>

</LinearGradientBrush>

</Grid.Background>

...

</Grid>

任何一组XAML标签都可以被一组处理相同任务的代码替换。刚才填充渐变色的所用标签和下面的代码等效:

LinearGradientBrush brush = new LinearGradientBrush();

GradientStop gradientStop1 = new GradientStop();

gradientStop1.Offset = 0;

gradientStop1.Color = Colors.Yellow;

brush.GradientStops.Add(gradientStop1);

GradientStop gradientStop2 = new GradientStop();

gradientStop2.Offset = 0.5;

gradientStop2.Color = Colors.White;

brush.GradientStops.Add(gradientStop2);

GradientStop gradientStop3 = new GradientStop();

gradientStop3.Offset = 1;

gradientStop3.Color = Colors.Purple;

brush.GradientStops.Add(gradientStop3);

grid1.Background = brush;

附加属性

     除了普通的属性,XAML还包括了一个附加属性的概念——属性可以申请几个定义在不同类中的元素。在银光中,附加属性常被用作控件的布局上。

下面展示如何工作的。每一个控件都有自己的内在的属性。比如,一个文本框就被诸如FontFamily, Foreground, and Text这些属性来规定字体,文本颜色,文本内容。当你在容器中放置一个控件,它就得到了附加的特性,这些特性依赖容器的类型。比如,你在栅格中放置一个文本框,你就可以选择栅格的单元来放置该控件。

附加属性的格式有两部分:DefiningType.PropertyName 。这两部分命名的语法可以让XAML解释器把普通属性和附加属性区别开。

eight ball这个例子中,附加属性可以让每个元素放置在栅格(不可见的)的分割出的行中:

<TextBox ... Grid.Row="0">

</TextBox>

<Button ... Grid.Row="1">

</Button>

<TextBox ... Grid.Row="2">

</TextBox>

附加属性其实不是真实的属性,他们是对方法调用的翻译。XAML解释器调用静态方法DefiningType.SetPropertyName() 。比如,在上面这个XAML的片段中,定义的类型是Grid类,属性是Row,所以解释器调用Grid.SetRow()

当调用SetPropertyName()时,解释器传两个参数:被修改的对象和被定义的属性。比如:当你在文本框控件上设定Grid.Row属性时,XAML解释器执行这样的代码:Grid.SetRow(txtQuestion, 0)

这种模式(调用定义类型的静态方法)是对所发生的事情的很好的隐藏。不经意间,行数的代码好像已经被存在了栅格对象中。但是,行数是真实存在它所指的对象中的,在本例中就是存在了文本框对象中了。

这种手法之所以能运作,是因为文本框是继承自DependencyObjec基类,这个基类是所有银光元素的基类。DependencyObject被设计存贮一个不受限制的从属属性的集合(附加属性就是一种从属属性)。

事实上,Grid.SetRow()方法是调用DependencyObject.SetValue()方法的一个快捷方式,其实是这样的:txtQuestion.SetValue(Grid.RowProperty, 0);

附加属性是银光的一个核心成分。它使系统变的可扩展。比如定义行属性为一个附加属性,你就可以在任何控件中都可以用到。但从另一个方面说,把它作为基类的一部分,也容易造成混乱。

嵌套元素

XAML文档是用嵌套的元素树来组织的。XAML允许每一个元素来决定如何处理嵌套的元素。这种交互是通过三个评价机制来进行的:

l  如果父结点是一个IList<T>,那么解释器就调用IList<T>.Add()方法

l  如果父结点是一个IDictionary<T>,那么解释器就调用IDictionary<T>.Add(),当用了字典集合,你就必须设定x:Key,为每一项设定一个key名。

l  如果父结点是用ContentPropertyAttribute类来修饰的,那么解释器就用子结点来设置属性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值