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


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

     

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

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

    IAddChild接口定义了两个方法:AddChildAddTextAddChild用来添加元素,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属性的方式来实现AddChildWindow将检测自身是否已包含子对象,因为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, ListBoxTabControl等类将其Items属性作为内容属性。

             注意像ButtonWindow等从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中两种类型的集合 - ListDictionary支持向其中添加项。

     

    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>

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

    <ListBox>

        <ListBoxItem Content="Item1"/>

        <ListBoxItem Content="Item2"/>

    </ListBox>

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

     

    Dictionary

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

    <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:KeyXAML中指定的值总是被作为字符串处理,其不会尝试使用类型转换器,要想按其它类型处理只能使用标记扩展。

             下面是上面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.BrushSolidColorBrush, GradientBrush和其他一些具体笔刷的基类,故也可以将上面代码用如下来表示来替换:

    <Brush>White</Brush>

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

     

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

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

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

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

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

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

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

     

     

    参考:

    WPF揭秘》

     

     

  • 相关阅读:
    使用对象-关系映射持久化数据
    Spring中使用JDBC
    无法正确解析FreeMarker视图
    使用Java配置SpringMVC
    面向切面的Spring
    高级装配
    爬虫简介与request模块
    列表存储本质以及索引为什么从0开始
    列表和字典操作的时间复杂度
    记录腾讯云中矿机病毒处理过程(重装系统了fu*k)
  • 原文地址:https://www.cnblogs.com/lsxqw2004/p/2613229.html
Copyright © 2020-2023  润新知