您的位置:首页 > 其它

WPF,Silverlight与XAML读书笔记第四 - XAML元素子元素/内容及其模式

2009-08-28 15:45 441 查看

说明:本系列基本上是《WPF揭秘》的读书笔记。在结构安排与文章内容上参照《WPF揭秘》的编排,对内容进行了总结并加入一些个人理解。



XAML中子内容的处理由其父对象来决定,这就需要父元素类型实现System.Windows.Serialization命名空间的IAddChild接口或是实现标准的集合接口 – Icollection。如果试图添加子元素到一个没有实现这两种接口的元素类型,编译器将会报错。

XAML中元素处理其子元素/内容基本上就是以这两种通用的模式来完成,实现这个处理功能不是由XAML来完成的,而是由于父元素实现了.NET类库中两个接口才得以实现。

IAddChild接口定义了两个方法:AddChild和AddText,AddChild用来添加元素,AddText用来添加普通文本。编译器为每段子内容调用这两个方法。

示例代码:

XAML:

<Window x:Class="WpfApplication1.Window1"

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

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

Title="Window1" Height="300" Width="300">

<Grid>

</Grid>

</Window>

C#:(演示)

Window1 myWindow = new Window1();

myWindow.AddChild(new Grid());

Window元素(继承自ContentControl类的类型都可采用这种方式)也可以通过设置Content属性的方式来实现AddChild(Window将检测自身是否已包含子对象,因为Window元素的Content属性只支持单一对象,试图添加更多的对象将抛出异常)。这是下文将要重点介绍的子元素3大模式之一的 – 内容属性。

示例代码:(与前文代码等效,加入了粗体部分来演示内容属性的本质,语法上来看和属性元素一样)

<Window x:Class="WpfApplication1.Window1"

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

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

Title="Window1" Height="300" Width="300">

<Window.Content>

<Grid>

</Grid>

</Window.Content>

</Window>

C#:

Window1 myWindow = new Window1();

myWindow.Content = new Grid();

通过这种设置属性的方式也可以实现将Grid对象添加为Window的子元素。

XAML中,一个对象元素可以有3种类型的子元素:

1. 内容属性值

2. 集合项

3. 能够通过类型转换到它的父元素的值。

下文将分别详细介绍。

1. 内容属性

WPF中很多类指定了一个属性,通过设置该属性的值可以给此类的元素来设置内容,这个特性称为内容属性。在某些情况下内容属性是比属性元素更简单的表示方法。将属性作为内容属性的方法是使用System.Windows.Markup.ContentPropertyAttribute标记这个属性。

对比下面两组例子:

第一:普通方式(以Xml Attribute方式)设置属性的方式改写为内容属性的表示方式:

XML Attribute:

<Button Content="OK"/>

内容属性方式:

<Button>OK</Button>

第二:属性元素方式设置属性的方式改写为内容属性的表示方式

属性元素:

<Button>

<Button.Content>

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

</Button.Content>

</Button>

重写为内容属性形式:

<Button>

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

</Button>

另外,并不一定只有Content属性被设计为内容属性,像ComboBox, ListBox和TabControl等类将其Items属性作为内容属性。

注意像Button、Window等从ContentControl继承的元素都只能包含一个单独的子元素,根本原因在于它们的子元素被作为内容属性(Content属性),而只能给Content属性指定一个值。当添加的元素超过一个会抛出异常。

另外,WPF中继承自Panel基类的布局容器将Children属性作为内容属性,所以有如下写法。(Children属性不像Content属性那样被限制只能指定一个值)

属性元素写法:

<StackPanel>

<StackPanel.Children>

<Button>Foo</Button>

<Button>Bar</Button>

</StackPanel.Children>

</StackPanel>

内容属性写法:

<StackPanel>

<Button>Foo</Button>

<Button>Bar</Button>

</StackPanel>

2. 集合项

有一些类使用相同的方式表现子项的集合。如:XAML中两种类型的集合 - List与Dictionary支持向其中添加项。

List

Itmes集合

List指实现了System.Collection.IList接口的集合,如System.Collections.ArrayList类及WPF中定义的ListBox控件,RadioButtonList控件,ComboBox控件及TabControl控件。当添加一个子项到这些元素时,IAddChild接口就会将子项添加到Items集合中。

一般来说,设置它们的Items属性可以使用属性元素。如下示例:

向ListBox中添加两项:

<ListBox>

<ListBox.Items>

<ListBoxItem Content="Item1"/>

<ListBoxItem Content="Item2"/>

</ListBox.Items>

</ListBox>

因为Items是ListBox的内容属性,所以可以将XAML简化为内容属性的写法:

<ListBox>

<ListBoxItem Content="Item1"/>

<ListBoxItem Content="Item2"/>

</ListBox>

这时ListBox的Items属性首先会自动被初始化为一个空的集合对象,这样代码可以正常工作。

Dictionary

Dictionary指的是实现了System.Collections.IDictionary接口的集合。下面以WPF中一个常用的集合类型System.Windows.ResourceDictionary为例,来演示使用XAML向Dictionary中添加键值对的方式,以下代码直接给出内容属性的写法(粗体部分):

<Grid>

<Grid.Resources>

<ResourceDictionary>

<Color x:Key="1" A="255" R="255" G="255" B="255"/>

<Color x:Key="2" A="0" R="0" G="0" B="0"/>

</ResourceDictionary>

</Grid.Resources>

</Grid>

此XAML使用了经过特殊处理的XAML Key关键字(定义于次级命名空间xmlns:x中),从而可以为每个Color值添加一个键。带有x:Key的XAML中指定的值总是被作为字符串处理,其不会尝试使用类型转换器,要想按其它类型处理只能使用标记扩展。

下面是上面XAML等价的C#代码:

System.Windows.ResourceDictionary d = new ResourceDictionary();

System.Windows.Media.Color color1 = new Color();

System.Windows.Media.Color color2 = new Color();

color1.A = 255; color1.R = 255; color1.G = 255; color1.B = 255;

color2.A = 0; color2.R = 0; color2.G = 0; color2.B = 0;

d.Add("1", color1);

d.Add("2", color2);

Collections属性

当一个属性是集合类型(实现ICollection接口)时,通常不需要为集合自身再提供一个元素,而是让属性名来控制集合,通常用于只有一个属性的元素。(如下示例中,加粗代码表示的是不用再提供的元素)

注:例子使用属性元素,这样使示例代码的来龙去脉更清晰。(属性元素加虚下划线,去掉即是子内容的表示方式)

<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">

<LinearGradientBrush.GradientStops>

<GradientStopCollection>

<GradientStop Color="Black" Offset="0" />

<GradientStop Color="Red" Offset="1" />

</GradientStopCollection>

</LinearGradientBrush.GradientStops>

</LinearGradientBrush>

所以最简单的子内容表示方法:

<LinearGradientBrush StartPoint="0,0" EndPoint="0,1">

<GradientStop Color="Black" Offset="0" />

<GradientStop Color="Red" Offset="1" />

</LinearGradientBrush>

这个例子中GradientStops是集合类型的属性,所以我们不需要在提供GradientStopCollection这一级元素,而是通过集合的子元素GradientStop直接控制集合。

3. 支持类型转化的普通文本

首先看一下下面这个XAML中声明的SolidColorBrush:

<SolidColorBrush>White</SolidColorBrush>

这段代码看起来很像上文讲到的内容属性,但是其等价代码

<SolidColorBrush Color="White"/>

中的Color属性并没有被指定为内容属性。这个代码之所以能工作是因为类型转换器会将字符串"White"转换为SolidColorBrush对象。另外,由于System.Windows.Media.Brush是SolidColorBrush, GradientBrush和其他一些具体笔刷的基类,故也可以将上面代码用如下来表示来替换:

<Brush>White</Brush>

Brush类型转化器将其理解为SolidColorBrush。

下表总结了XAML中元素的子元素的处理规则:

当转换子元素时,任何一个有效的XAML分析器必须遵循下面的规则:

(1) 如果该类型实现了ILits接口,就为每个子元素调用IList.Add。

(2) 否则,如果该类型实现了IDictionary,就为每个子元素调用IDictionary.Add,在该值的键和元素中使用x:Key特性值。

(3) 否则,如果父元素支持内容属性,而且子元素的类型与该内容属性是兼容的,就把子元素作为内容属性的值。

(4) 否则,如果子对象是普通文本,且有类型转换器将子对象转换为父元素的类型,则把子元素作为类型转换器的输入,将输出作为父元素的实例(没有在父元素上设置属性,子元素只相当于初始化父元素的一个参数)。

(5) 其他情况下,则抛出一个异常。

参考:

《WPF揭秘》
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐