第 3 章:控件和 XAML

 

简介
第 1 章:“长角”应用模型
第 2 章:生成“长角”应用程序

第 3 章:控件和 XAML

布伦特校长
Wise Owl Consulting

2003 年 12 月

目录

XAML 元素
XAML 面板
控制
资源和样式
图形和动画
文档服务
总结

正如你在第 2 章中看到的,Longhorn 平台应用程序通常由 Application 对象和一组以声明性标记语言(称为 XAML)编写的用户界面页面组成。

Application 对象是单一实例,在应用程序的整个生存期内保持不变。 它允许应用程序逻辑处理顶级事件并在页面之间共享代码和状态。 Application 对象还确定应用程序是单窗口应用程序还是导航应用程序。

通常使用名为 Extensible Application Markup Language 的 XML 方言编写每个用户界面页, (XAML) 。 每个页面由 XAML 元素、文本节点以及分层树中组织的其他组件组成。 这些组件的分层关系决定了页面的呈现方式和行为方式。

还可以将 XAML 页视为对象模型的说明。 当运行时创建页面时,它会实例化 XAML 文档中描述的每个元素和节点,并在内存中创建等效的对象模型。 你可以以编程方式操作此对象模型,例如,可以添加和删除元素和节点,使页面呈现和行为不同。

从根本上说,XAML 页描述运行时应创建的类、类实例的属性值和事件处理程序,以及对象模型层次结构,即哪个实例是另一个实例的父级。

所有 XAML 文档都是格式正确的 XML 文档,使用一组定义的元素名称。 因此,有关格式正确的 XML 文档的所有规则都同样适用于 XAML 文档。 例如,文档必须包含单个根元素;所有元素名称都区分大小写;元素定义不能与另一个元素定义重叠,但必须完全包含它,等等。 如果不熟悉 XML 语法,现在是学习 XML 语法的好时机。

XAML 元素

每个 XAML 页面都包含一个或多个控制页面布局和行为的 元素 。 在树中按层次结构排列这些元素。 每个元素只有一个父元素。 元素通常可以有任意数量的子元素。 但是,某些元素类型(例如 Scrollbar)没有子元素;和其他元素类型(例如 Border)可以有一个子元素。

每个元素名称对应于托管类的名称。 将元素添加到 XAML 文档会导致运行时创建相应类的实例。 例如,以下标记表示具有单个子 Table 元素的根 DockPanel 元素。 Table 元素包含三个子 Row 元素。 每个 Row 元素包含三个子元素,其中一些元素具有子文本节点。

<Border xmlns="https://schemas.microsoft.com/2003/xaml" 
        Background="BlanchedAlmond">
  <DockPanel>
    <Table> 
      <Body>
        <Row>
          <Cell><Button/></Cell>
          <Cell><Text>Item</Text></Cell>
          <Cell><Text>Price</Text></Cell>
        </Row>
        <Row>
          <Cell><CheckBox Checked="true"/></Cell>
          <Cell><TextBox Height="50">Nissan 350Z</TextBox></Cell>
          <Cell><TextBox Height="50">29.95</TextBox></Cell>
        </Row>
        <Row>
          <Cell><CheckBox/></Cell>
          <Cell><TextBox Height="50">Porsche Boxster</TextBox></Cell>
          <Cell><TextBox Height="50">9.95</TextBox></Cell>
        </Row>
      </Body>
    </Table>

  </DockPanel>
</Border>

此 XAML 文档创建对象层次结构,如图 3-1 所示,图 3-2 中所示。

图 3-1. XAML 页面对象模型示例

单击此处查看较大的图像

图 3-2。 上一个 XAML 的显示 (单击以获取较大的图像)

仅使用标记即可访问此类对象的大部分功能。 仅使用标记,可以执行以下任一操作:

  • 描述运行时将实例化的分层对象集
  • 将对象属性设置为静态已知值
  • 将对象属性设置为从数据源检索的值
  • 导致更改的属性值存储回数据源
  • 随时间推移反复更改属性的值
  • 将事件处理程序绑定到对象的 事件

但是,尽管你可以仅使用标记创建一些惊人的用户界面,但也可以使用 XAML 对象模型以编程方式访问元素的功能。 对象模型允许你操作页面上元素的各个方面。 它实际上提供了无法通过 XAML 访问的其他功能。

每个 XAML 元素都派生自 System.Windows.UIElementSystem.Windows.ContentElement,因此所有元素都具有许多常见功能。 元素可以分为以下四个基本类别:

  • 控件 派生自 System.Windows.Control 并处理用户交互。
  • 面板 是派生自 System.Windows.Panel 的专用控件,可处理页面布局并充当元素的容器。
  • 文本格式设置元素 派生自 System.Windows.TextElement 并处理文本格式和文档结构。
  • 形状 处理矢量图形形状。

XAML 面板

XAML 页面通常以 panel 元素开头。 面板是页面内容的容器,控制该内容的定位和呈现。 事实上,在使用 XAML 显示任何内容时,总是涉及面板,尽管有时它是隐式的,而不是你显式描述的面板。 面板可以包含其他面板,使你可以将显示图面划分为多个区域,每个区域由其面板控制。

Longhorn 平台中有六个内置 Panel 类:

  • Canvas 使用相对于 Canvas 区域的坐标显式定位每个子元素。
  • DockPanel 将其子级放置在面板的顶部、底部、左侧、右侧或中心。 将多个子级分配到同一区域时, DockPanel 会将它们水平或垂直排列在该区域内。
  • FlowPanel 根据其换行和对齐属性排列其子元素。 当内容超过单行的长度时,面板将相应地换行、换行和对齐内容。
  • TextPanel 以多种文本格式呈现多行文本。 通常仅在需要复杂文本布局时使用它。 在大多数情况下,你将使用轻型 Text 元素来获得基本文本支持。
  • GridPanel 是一个轻量级元素,它将子元素排列在构成网格的行和列中。 它可用于创建简单表,但功能有限。 对于复杂的 表格 布局,可以使用表控件。
  • FixedPanel 将其子元素放置在固定布局页上。 无论设备分辨率或窗口大小如何,固定布局页上的元素始终具有相同的定位和分页。

通常,这些面板将为大多数开发人员提供足够的功能。 但是,也可以创建自己的面板类,以专用的方式定位和显示内容。

画布

画布 面板在定位和排列屏幕上的元素方面提供了相当大的灵活性。 它允许你指定每个子元素的位置,当元素重叠时,可以通过更改元素在标记中的显示顺序来指定画布绘制重叠元素的顺序。

下面的标记生成三个重叠图形,如图 3-1 所示:一个带有橙色边框的绿色矩形,一个带有蓝色边框的半透明黄色椭圆,以及一些文本在矩形中居中。 (一想到再也不会写WM_PAINT处理程序来画这样的东西,这让我眼里流下了眼泪。。喜悦之泪我赶紧添加!) 框架按呈现的顺序绘制形状,因此文本出现在矩形上方。

<Canvas xmlns="https://schemas.microsoft.com/2003/xaml" >
  <Rectangle 
    Fill="#33CC66"
    Width="2in"       Height="1in"
    Canvas.Top="25"          Canvas.Left="50"
    StrokeThickness="6px" Stroke="Orange" />

  <Ellipse         
    Fill="yellow"
    CenterX="1.5in"    CenterY="1.1in"
    RadiusX=".5in"     RadiusY="1in"
    StrokeThickness="4px"  Stroke="Blue" />

   <Text
    Canvas.Top="50" Canvas.Left="60" Foreground="#000000"
    FontWeight="Bold" FontFamily="Arial"
    FontStyle="Normal" FontSize="25">Hello Shapes!</Text>

</Canvas>

图 3-3。 使用“画布”面板的示例

DockPanel

DockPanel 面板相对于彼此水平或垂直排列子元素。 DockPanel 类检查每个子元素的 Dock 属性,以确定如何沿面板边缘对齐元素。 可以将 Dock 属性设置为以下五个值之一: TopBottomLeftRightFill

例如,面板将第一个子元素与其 Dock 属性等于 Top 与面板的上边缘对齐。 然后,面板将下一个子元素与其 Dock 属性等于上一个元素下方的 Top 对齐。 面板同样将子元素与其 Dock 属性设置为 BottomLeftRight 对齐。 将最后一个子元素的 Dock 属性设置为 Fill 会导致它占用 DockPanel 中的所有剩余空间。 切勿将 Dock=“Fill” 元素与其他元素一起跟随,因为后续元素将不可见。 Dock 属性的默认值为 Left,因此,如果未设置元素的 Dock 属性,它将水平堆叠到左侧。

Dock 属性是一个附加属性-它由 DockPanel 类定义,但你可以在子元素上设置它,如下所示:

<child DockPanel.Dock="Top"/>

或者在代码中:

DockPanel.SetDock(child, Dock.Top)

以下标记使用 DockPanel 和五个 Canvas 面板来创建常见的用户界面。 DockPanel 将前两个画布与 DockPanel 顶部对齐。 它将第三个画布与 DockPanel 的下边缘对齐,第四个画布与左边缘对齐,第五个画布填充剩余空间。 可以将菜单放在顶部面板上,将工具栏放在菜单正下方的面板中。 此决定将左面板保留为树视图,底部面板为状态栏,剩余面板用于详细所选项目视图,如图 3-4 所示。

<Border xmlns="https://schemas.microsoft.com/2003/xaml" 
Background="White">
  <DockPanel>
          <Border Width="500" DockPanel.Dock="Top" 
BorderThickness="2,2,2,2" BorderBrush="Black" Background="#87ceeb" >
              <Text>Dock = "Top"</Text>
          </Border>
          <Border Width="500" DockPanel.Dock="Top" 
BorderThickness="2,2,2,2" BorderBrush="Black" Background="#87ceeb" >
            <Text>Dock = "Top"</Text>
          </Border>
          <Border Width="500" DockPanel.Dock="Bottom" 
BorderThickness="2,2,2,2"
                  BorderBrush="Black" Background="#ffff99" >
            <Text>Dock = "Bottom"</Text>
          </Border>
          <Border Width="200" DockPanel.Dock="Left" 
BorderThickness="2,2,2,2" BorderBrush="Black" Background="#98fb98" >
            <Text>Dock = "Left"</Text>
          </Border>
          <Border Width="300" DockPanel.Dock="Fill" 
BorderThickness="2,2,2,2" BorderBrush="Black" Background="White" >
            <Text>Dock = "Fill"</Text>
          </Border>
  </DockPanel>
</Border>

图 3-4。 使用 DockPanel 面板的示例

FlowPanel

FlowPanel 面板提供了许多自动布局功能,并允许复杂呈现文本和图形。 可以使用面板的 WidthHeight 属性定义面板的大小。 然后,面板以最能使用面板空间的方式显示其子元素,并根据需要包装和对齐元素。 FlowPanel 的默认流方向是从左到右、从上到下。

以下标记示例演示 FlowPanel 如何拆分和包装内容。 FlowPanel 包含四个一英寸的方形画布。 FlowPanel 尝试从左到右和从上到下显示其子元素。

<Border xmlns="https://schemas.microsoft.com/2003/xaml" Background="White">
  <FlowPanel>
      <Border Background="Red" Width="1in" Height="1in"/>
      <Border Background="Green" Width="1in" Height="1in"/>
      <Border Background="Blue" Width="1in" Height="1in"/>
      <Border Background="Yellow" Width="1in" Height="1in"/>
  </FlowPanel>
</Border>

图 3-3 显示了 FlowPanel 可在单个行上容纳所有元素时的输出。 图 3-4 演示了将最后一个元素包装到新行的 FlowPanel 。 图 3-5 显示了 FlowPanel 必须将每个元素放在其自己的行的最坏情况。 图 3-6 显示最后一个元素换行到新行,图 3-7 显示每个元素换行到新行。

图 3-5. FlowPanel 仅在必要时中断行。

图 3-6。 将最后一个元素包装到新行的 FlowPanel 面板

图 3-7。 将每个元素包装到新行的 FlowPanel 面板

TextPanel

TextPanel 面板格式化、调整大小和绘制文本。 此面板类支持多行文本以及多种文本格式。 需要复杂布局支持时,通常会使用 TextPanel 类。 但是,如果只需要简单的文本显示,最好改用 Text 元素。

以下标记示例演示 TextPanel 如何拆分和包装内容。 调整窗口大小时 ,TextPanel 会调整列数和每列的高度。

<Border xmlns="https://schemas.microsoft.com/2003/xaml" Background="White">
  <TextPanel 
    ColumnCount="3" 
    ColumnWidth="200px" 
    ColumnGap="25px"
    ColumnRuleWidth="5px"
    ColumnRuleBrush="blue">

    <Block Background="LightGray">
      <Inline FontFamily="Arial" FontWeight="Bold"
              FontSize="16pt">Transcript of the 
           <Italic>Nicolay Draft</Italic> 
           of the Gettysburg Address.
      </Inline>
    </Block>
    §
  </TextPanel>
</Border>

图 3-8 显示了生成的输出。

图 3-8。 具有多个字体特征、列和格式的 TextPanel

GridPanel

GridPanel 面板显示表格数据。 GridPanel 支持许多可用于自定义表格数据布局的属性。 例如,可以设置 ColumnsRows 属性来控制网格中的列数和行数。 同样, ColumnStylesRowStyles 属性允许设置 GridPanel 分别应用于行和列的属性集合。

GridPanel 按顺序排列其子级,从左上角的单元格开始,向右移动,直到行的末尾。 如果在子级上设置 GridPanel.ColumnSpan 属性,则子级可以采用多个列。 同样, GridPanel.RowSpan 允许子级跨多行。

以下标记显示与 Windows 计算器实用工具非常相似的计算器用户界面。

<Border xmlns="https://schemas.microsoft.com/2003/xaml" Background="#DEE7F7">
<DockPanel Dock="Left">
  <Border  BorderThickness="0,0,0,0">
<!-- Padding="10, 10, 10, 10"  -->
  <GridPanel Columns="7">
    <GridPanel.ColumnStyles>
      <Column Width="16%"/>
      <Column Width="4%"/>
      <Column Width="16%"/>
      <Column Width="16%"/>
      <Column Width="16%"/>
      <Column Width="16%"/>
      <Column Width="16%"/>
    </GridPanel.ColumnStyles>

    <GridPanel.RowStyles>
      <Row Height="25"/>
      <Row Height="10"/>
      <Row Height="35"/>
      <Row Height="7"/>
      <Row Height="35"/>
      <Row Height="35"/>
      <Row Height="35"/>
      <Row Height="35"/>
    </GridPanel.RowStyles>

        <Border GridPanel.ColumnSpan="7" BorderBrush="#DEE7F7" 
                BorderThickness="2,2,2,2" Background="White">
            <Text HorizontalAlignment="right"
                  ID="CalcText">0.</Text>    
        </Border>

        <Text GridPanel.ColumnSpan="7"/>

        <Border BorderThickness="0,0,0,0">
          <GridPanel>
            <Border BorderBrush="#DEE7F7" BorderThickness="2,2,2,2">
               <Text Width="16%"
                  HorizontalAlignment="center"></Text>
            </Border>
          </GridPanel>
        </Border>

        <Text Width="4%"/>
        <DockPanel GridPanel.ColumnSpan="5" Dock="Left">
          <Button Width="33.33%" Foreground="Red">Backspace</Button>
          <Button Width="33.33%" Foreground="Red">CE</Button>
          <Button Width="33.33%" Foreground="Red">C</Button>
        </DockPanel>

        <Text GridPanel.ColumnSpan="7"/>

        <Button Foreground="Red">MC</Button>
        <Text/>
        <Button Foreground="Blue">7</Button>
        <Button Foreground="Blue">8</Button>
        <Button Foreground="Blue">9</Button>
        <Button Foreground="Red">/</Button>
        <Button Foreground="Blue">sqrt</Button>

        <Button Foreground="Red">MR</Button>
        <Text/>
        <Button Foreground="Blue">4</Button>
        <Button Foreground="Blue">5</Button>
        <Button Foreground="Blue">6</Button>
        <Button Foreground="Red">*</Button>
        <Button Foreground="Blue">%</Button>

        <Button Foreground="Red">MS</Button>
        <Text/>
        <Button Foreground="Blue">1</Button>
        <Button Foreground="Blue">2</Button>
        <Button Foreground="Blue">3</Button>
        <Button Foreground="Red">-</Button>
        <Button Foreground="Blue">1/x</Button>

        <Button Foreground="Red">M+</Button>
        <Text/>
        <Button Foreground="Blue">0</Button>
        <Button Foreground="Blue">+/-</Button>
        <Button Foreground="Blue">.</Button>
        <Button Foreground="Red">+</Button>
        <Button Foreground="Red">=</Button>

  </GridPanel>
</Border>
</DockPanel>
</Border>

图 3-9 显示了生成的输出。

图 3-9. GridPanel 作为计算器

FixedPanel

使用 FixedPanel 面板可以指定每个元素的确切位置和大小。 FixedPanel 上的元素将始终在所有设备上以相同的位置和大小显示。 本章后面的“文档布局服务”部分将讨论 FixedPanel 面板。

控制

XAML 具有你期望从 Windows 获得的所有控件-按钮、检查框、单选按钮、列表框、组合框、菜单、滚动条、滑块等。 此示例演示 Longhorn 中提供的一些常见控件。 可以在图 3-10 中看到结果。

<Border
       xmlns="https://schemas.microsoft.com/2003/xaml"
       xmlns:def="Definition"
       Background="BlanchedAlmond" 
   >
<DockPanel>
  <Menu DockPanel.Dock="Top">
    <MenuItem Header="File">
      <MenuItem Header="New" />
      <MenuItem Header="Open" />
    </MenuItem>

    <MenuItem Header="Edit">
      <MenuItem Header="Cut"/>
      <MenuItem Header="Copy"/>
      <MenuItem Header="Paste"/>
    </MenuItem>
  </Menu>

<FlowPanel>

<Button> Button </Button>
<Border Width="15"/>

<CheckBox Checked="true"> CheckBox </CheckBox>
<Border Width="15"/>

<RadioButtonList>
  <RadioButton> RadioButton 1 </RadioButton>
  <RadioButton Checked="true"> RadioButton 2 </RadioButton>
  <RadioButton> RadioButton 3 </RadioButton>
</RadioButtonList>
<Border Width="15"/>

<ListBox>
   <ListItem> ListItem 1 </ListItem> 
   <ListItem> ListItem 2 </ListItem> 
   <ListItem> ListItem 3 </ListItem> 
</ListBox>
<Border Width="15"/>

<ComboBox>
   <ListItem> ListItem 1 </ListItem> 
   <ListItem> ListItem 2 </ListItem> 
   <ListItem> ListItem 3 </ListItem> 
</ComboBox>
<Border Width="15"/>

    <DockPanel>
      <VerticalSlider DockPanel.Dock="Top"  Height="200"
             Minimum="0" Maximum="255" Value="75"
             SmallChange="1" LargeChange="16"/> 
      <Text DockPanel.Dock="Bottom">Slider</Text>
    </DockPanel>
<Border Width="15"/>

    <DockPanel>
      <VerticalScrollBar DockPanel.Dock="Top" 
             Minimum="0" Maximum="255" Value="125" Height="200"
             SmallChange="1" LargeChange="16"/> 
      <Text DockPanel.Dock="bottom">ScrollBar</Text>
    </DockPanel>
<Border Width="15"/>
   
<TextBox> TextBox </TextBox> 

</FlowPanel>
</DockPanel>
</Border>

图 3-10. XAML 控件的示例

XAML 还允许合并元素和控件以创建丰富的效果。 我们称之为元素 控制组合的组合,它是 Longhorn 最强大的方面之一。 例如,若要创建包含图像的按钮,请将 Image 元素放在 Button 中:

<Button> 
  <Image Source="tulip.jpg"/>
</Button>

若要在 按钮中同时显示图像和文本(如图 3-11 所示),我们使用老朋友 DockPanel

<Button> 
  <DockPanel>
     <Image Source="tulip.jpg"/>
     <Text DockPanel.Dock="fill" VerticalAlignment="center"> Button 
         <Italic>with Image!</Italic>
     </Text>
  </DockPanel>
</Button>

图 3-11. 包含图像和文本的按钮

你可以将几乎所有内容放入任何内容,包括按钮CheckBox 的一个奇怪示例:

<Button> 
  <CheckBox Checked="true"> CheckBox </CheckBox>
</Button>

合成功能足够强大,许多 Longhorn 控件实际上是使用合成定义的。 例如, ScrollBar 实际上是两个按钮和一个滑块,外加一些事件处理程序逻辑,用于将它们挂钩在一起。

XAML 还包括一些控件“基元”,这些“基元”主要用于控件组合,以生成更大的效果。 例如, ScrollViewer 采用一个子 (通常) 面板并为其添加滚动条。 本示例在 ScrollViewer 中放置一个非常大的 CheckBox 元素列表,而 Longhorn 之前需要单独的控件,例如Windows 窗体的 CheckedListBox

<Border BorderThickness="1" BorderBrush="black">
  <ScrollViewer Height="100" Width="200">
    <GridPanel Columns="1">
      <CheckBox Checked="true"> CheckBox 1</CheckBox>
      <CheckBox Checked="true"> CheckBox 2</CheckBox>
      <CheckBox Checked="true"> CheckBox 3</CheckBox>
      <CheckBox Checked="true"> CheckBox </CheckBox>
      <CheckBox Checked="true"> CheckBox </CheckBox>
      <CheckBox Checked="true"> CheckBox </CheckBox>
      <CheckBox Checked="true"> CheckBox </CheckBox>
      <CheckBox Checked="true"> CheckBox </CheckBox>
    </GridPanel>
  </ScrollViewer>
</Border>

资源和样式

XAML 提供了非常丰富的功能,用于通过称为样式的实体自定义应用程序的外观。 但是,在进入本主题之前,我们需要了解 资源。 此上下文中使用的术语 “资源 ”仅指重用常见定义的对象和值的方式。 我们来看一个示例:

<Border
       xmlns="https://schemas.microsoft.com/2003/xaml"
       xmlns:def="Definition"
       Background="BlanchedAlmond" 
   >
  <FlowPanel>
    <FlowPanel.Resources>
      <SolidColorBrush def:Name="MyColor" Color="Gold"/>
    </FlowPanel.Resources>
    <Button Background="{MyColor}"/>
    <Ellipse Fill="{MyColor}"/>
  </FlowPanel>
</Border>

此代码定义名为 MyColor 的新资源,其类型为 SolidColorBrush ,值为 Gold。 此资源是 FlowPanelResources 集合的一部分。 每个元素都有一个 Resources 集合。 可以在所需的任何元素上定义资源,但通常只将它们放在根元素上,在本例中为 FlowPanel

定义资源后,可以通过将资源名称放在大括号中来引用属性值中的资源,如下所示:

<Button Background="{MyColor}"/>

当 XAML 处理器在此示例中看到 {MyColor} 时,它将首先检查按钮的 Resources 集合。 由于 Button 没有 MyColor 的定义 (其 Resources 集合) 为空,因此它将检查 Button 的父级 FlowPanel

一种特别有用的资源是 StyleStyle 既是类的名称,也是所有元素都具有的属性的名称。 Style 定义要在元素上设置的属性,该元素随后使用该指定样式。 此示例定义名为 MyStyle 的样式,并将该样式应用于按钮:

<Border
       xmlns="https://schemas.microsoft.com/2003/xaml"
       xmlns:def="Definition"
       Background="BlanchedAlmond" 
   >
  <FlowPanel>
    <FlowPanel.Resources>

      <Style def:Name="MyStyle">               
           <Button Background="Red" FontSize="24"/>
      </Style>   

    </FlowPanel.Resources>
    <Button>Normal</Button>

    <Button Style="{MyStyle}">Styled</Button>

  </FlowPanel>
</Border>

还可以定义不带名称的 Style 资源,该资源成为未指定显式 Style 属性的元素的默认 样式 。 本示例将默认样式添加到前面的示例:

<Border
       xmlns="https://schemas.microsoft.com/2003/xaml"
       xmlns:def="Definition"
       Background="BlanchedAlmond" 
   >
  <FlowPanel>
    <FlowPanel.Resources>

      <Style>               
           <Button Background="Green" FontSize="15"/>
      </Style>   

      <Style def:Name="MyStyle">               
           <Button Background="Red" FontSize="24"/>
      </Style>   
    </FlowPanel.Resources>
    <Button>Normal</Button>
    <Button Style="{MyStyle}">Styled</Button>
  </FlowPanel>
</Border>

可以通过设置 Style 类的 BasedOn 属性来执行某种 样式 继承。 引用新的 Style 类将设置旧 Style 执行的所有属性,以及指定的其他属性。 以下示例定义了两种样式:第一种设置 Background 属性,第二种基于第一种设置 FontSize 属性。

<Border
       xmlns="https://schemas.microsoft.com/2003/xaml"
       xmlns:def="Definition"
       Background="BlanchedAlmond" 
   >
  <FlowPanel>
    <FlowPanel.Resources>
      <Style def:Name="Style1">
           <Button Background="Red"/>
      </Style>   

      <Style def:Name="Style2" BasedOn="{Style1}">
           <Button FontSize="24"/>
      </Style>   
    </FlowPanel.Resources>
    <Button Style="{Style1}">Style 1</Button>
    <Button Style="{Style2}">Style 2</Button>
  </FlowPanel>
</Border>

样式甚至可以使用称为属性触发器的功能有条件地设置属性Style 具有名为 VisualTriggers 的属性,该属性是 PropertyTriggers 的集合。 每个 PropertyTrigger 使用 PropertyValue 属性指定一个条件,并包含 Set 语句的集合。 当样式元素的 属性与该值匹配时, 将应用 Set 语句,当条件不再为 true 时,这些值将取消应用,就像一开始从未设置过一样。 本示例使用属性触发器在鼠标悬停按钮上时使按钮为绿色,否则为红色:

<Border
       xmlns="https://schemas.microsoft.com/2003/xaml"
           xmlns:def="Definition"
       Background="BlanchedAlmond" 
   >
  <FlowPanel>
    <FlowPanel.Resources>
      <Style def:Name="Style1">
           <Button Background="Red"/>

           <Style.VisualTriggers>
             <PropertyTrigger Property="IsMouseOver" Value="true">
               <Set PropertyPath="Background" Value="Green"/>
             </PropertyTrigger>
            </Style.VisualTriggers>
      </Style>   
    </FlowPanel.Resources>
    <Button Style="{Style1}">Style 1</Button>
  </FlowPanel>
</Border>

许多 XAML 控件使用控件组合。 它们组合了许多较小的控件,以创建更大、更复杂的控件。 样式甚至允许你更改此内容! 通过指定不同的组合,可以完全重新定义控件的外观,同时仍保留其行为。

声明 style 元素及其属性后, <Style.VisualTree> 标记在 Style 内指定要组合哪些元素来创建更大的控件。 可以像往常一样设置子元素的属性,并赋予这些子元素自己的子元素。 还可以使用属性别名来设置属性值。 例如, Name1=“*Alias (Target=Name2) ” 会将子级的 Name1 属性设置为较大控件的 Name2 属性。 以下示例为 Button 创建一个样式,用于更改组合以实现圆形外观,如图 3-12 所示。 按钮的 “背景 ”和“ 内容” 属性在可视化树中的相应点进行别名。

<Border Background="white"
    xmlns="https://schemas.microsoft.com/2003/xaml"
    xmlns:def="Definition">
  <FlowPanel>
    <FlowPanel.Resources>
      <Style def:Name="RoundButton">
    
         <Button FontSize="20"/>
     
         <Style.VisualTree>
             <Canvas>
                 <Rectangle ID="MainRect" 
                     RadiusX="10" RadiusY="10" 
                     Fill="*Alias(Target=Background)" 
                     Width="100%" Height="100%" />

                 <FlowPanel Width="100%" Height="100%" >
                     <ContentPresenter  
                          ContentControl.Content="*Alias(Target = Content)" 
                          Margin="15,3,15,5"/>
                 </FlowPanel>
             </Canvas>
         </Style.VisualTree>
      </Style>
    </FlowPanel.Resources>
 
    <Button Style="{RoundButton}">
        standard RoundButton
    </Button>

    <Button Background="red" Style="{RoundButton}">
        red RoundButton
    </Button>

    <Button>
        standard button
    </Button>

    <Button Background="red">
        red standard button
    </Button>
  </FlowPanel>
</Border>

图 3-12. FlowPanel 上的几个 RoundButton 和标准按钮

图形和动画

XAML 为绘制形状、转换对象状态以及对对象的几乎任何属性进行动画处理提供了广泛的支持。 使用 Shape 元素进行绘制, 使用转换 元素来更改属性或对象, 使用 Animation 元素可以随时间推移更改对象的属性。

形状

XAML 提供了一组 用于绘图的形状 元素,其中包括 椭圆线条矩形路径多边形折线形状具有“填充”(背景色)和“笔划”(边框颜色)。 填充笔划 默认为透明,因此请确保至少设置其中一个! StrokeWidth 属性控制轮廓的粗细。

形状不能有子元素。 通常将形状放置在 画布内,因此标记中的第一个形状将是绘制的第一个形状。 此示例演示了一些基本形状,也可在图 3-13 中看到:

<Border
       xmlns="https://schemas.microsoft.com/2003/xaml"
       xmlns:def="Definition"
       Background="BlanchedAlmond" 
   >
  <Canvas Height="400" Width="400">
        <Ellipse CenterX="70" CenterY="75" 
         RadiusX="30" RadiusY="50" 
         Fill="yellow" Stroke="red" StrokeThickness="15"/>

        <Rectangle RectangleLeft="150" RectangleTop="20" 
         RectangleHeight="100" RectangleWidth="40"
         Fill="lightBlue" Stroke="green"/>

        <Line X1="20" Y1="220" X2="150" Y2="240"
         Stroke="black" StrokeThickness="5"/>

        <Polygon Points="220,140 270,240 170,240"
         StrokeLineJoin="Round"
         Stroke="black" StrokeThickness="20"/>
  </Canvas>
</Border>

图 3-13。 画布上的各种形状

到目前为止,我们仅将纯色与 “笔划 ”和“ 填充” 属性一起使用。 但在 XAML 中,几乎可以在任何位置使用可以指定 画笔的颜色。 SolidColorBrush 是我们迄今一直使用的画笔,但 XAML 还支持 ImageBrushLinearGradientBrushRadialGradientBrushImageBrush 具有 ImageSource 属性,该属性指定图像文件的名称。 SolidColorBrush 具有 Color 属性。 LinearGradientBrushRadialGradientBrush 包含 一个 GradientStopCollection,它允许非常复杂的渐变。 此示例将四个画笔定义为资源,并将它们用作椭圆的 笔划填充 。 可以在图 3-14 中看到它们的外观。

<Border
       xmlns="https://schemas.microsoft.com/2003/xaml"
       xmlns:def="Definition"
       Background="BlanchedAlmond" 
   >
  <Border.Resources>
        <LinearGradientBrush def:Name="lineargradient" StartPoint="0,0" 
                             EndPoint="1,1"  >
          <LinearGradientBrush.GradientStops>
            <GradientStopCollection>
              <GradientStop Color="Blue" Offset="0"/>
              <GradientStop Color="white" Offset="1"/>
            </GradientStopCollection>
          </LinearGradientBrush.GradientStops>
        </LinearGradientBrush>

        <RadialGradientBrush def:Name="radialgradient" Focus="0.3,0.3">
          <RadialGradientBrush.GradientStops>
            <GradientStopCollection>
              <GradientStop Color="red" Offset="0"/>
              <GradientStop Color="yellow" Offset="1"/>
            </GradientStopCollection>
          </RadialGradientBrush.GradientStops>
        </RadialGradientBrush>

        <ImageBrush def:Name="image" ImageSource="Tulip.jpg" TileMode="Tile"/>

        <SolidColorBrush def:Name="solid" Color="gray"/>
  </Border.Resources>

  <Canvas Height="400" Width="400">
        <Ellipse CenterX="100" CenterY="75" 
         RadiusX="90" RadiusY="50" 
         Fill="{lineargradient}" Stroke="{image}" StrokeThickness="15"/>

        <Ellipse CenterX="300" CenterY="170" 
         RadiusX="50" RadiusY="150" 
         Fill="{radialgradient}" Stroke="{solid}" StrokeThickness="15"/>
  </Canvas>
</Border>

图 3-14。 工作中的渐变

转换

XAML 支持多种转换RotateTransformAngle 属性的量旋转。 TranslateTransform 根据 XY 属性移动内容。 ScaleTransform 将根据 ScaleXScaleY 属性收缩或拉伸。 SkewTransform 使用 AngleXAngleYCenter 属性来倾斜内容。 MatrixTransform 支持任意仿射转换。 最后, TransformCollection 本身就是一种 转换 ,允许将多个转换组合在一起。

某些类(如 Brush)具有 Transform 属性。 对于其他情况,可以使用 TransformDecorator 元素,该元素具有 Transform 属性。 TransformDecorator 将转换其子元素。 (像 边框一样,它只能有一个 child。) TransformDecorator 可以包含任何类型的子级,包括形状、面板和控件。 此示例使用 TransformDecorators。 第一个包含 椭圆形,并将它旋转 45 度。 第二个 TransformDecorator 包含 ListBox ,并旋转和缩放 ListBox。 可以在图 3-15 中查看形状的外观。

<Border
       xmlns="https://schemas.microsoft.com/2003/xaml"
       xmlns:def="Definition"
       Background="BlanchedAlmond" 
   >
  <Canvas Height="400" Width="400">
     <TransformDecorator Transform="rotate 45">
       <Ellipse CenterX="100" CenterY="75" 
         RadiusX="90" RadiusY="50" 
         Fill="white" Stroke="black" StrokeThickness="15"/>
     </TransformDecorator>

     <TransformDecorator Canvas.Top="200" Canvas.Left="100">
       <TransformDecorator.Transform>
         <TransformCollection>
           <RotateTransform Angle="135"/>
           <ScaleTransform ScaleX="2" ScaleY="4"/>
         </TransformCollection>
        </TransformDecorator.Transform>

        <ListBox >
          <ListItem> ListItem 1 </ListItem> 
          <ListItem> ListItem 2 </ListItem> 
          <ListItem> ListItem 3 </ListItem> 
        </ListBox>
     </TransformDecorator>
  </Canvas>
</Border>

图 3-15。 倾斜的 ListBox 和椭圆形

动画

XAML 还支持动画。 几乎可以对每个属性进行动画处理。 某些属性具有相应的“Animations”属性,例如 RotateTransform.AngleRotateTransform.AngleAnimations。 在其他情况下,可以使用复合属性语法将 动画集合 分配给属性。 例如,请参阅以下代码:

  <Button>
       <Button.Width>
         … put animation collection here …
       </Button.Width>
  </Button>

每种类型的属性都有一个单独的动画集合。 Button.Width 的类型为 Length,因此使用 LengthAnimationCollection。 同样, 动画对象 本身特定于要进行动画处理的属性的类型 - LengthAnimationCollection 包含一组 LengthAnimation 对象。

动画对象具有 FromTo 属性,其类型与要进行动画处理的属性的类型匹配。 动画对象还具有 BeginDurationEnd 属性,这些属性以秒为单位度量并控制动画的计时。 Begin 还支持“ 即时”值,“ 持续时间 ”支持 “无限期”。 可以使用 RepeatCountRepeatDuration 属性自动重复动画。 Fill 属性指定动画结束后属性发生的情况。 Fill=“Hold” 是最重要的值之一;它将 属性保留为动画 End 值。

动画类不是默认 XAML 命名空间的一部分,因此需要使用 <?映射>xmlns 构造以加载 MSAvalon.Windows.Media.Animation 命名空间。 由于此命名空间包含来自多个 DLL 的类,因此需要单独的 <?每个 DLL 的映射>xmlns

下一个示例对两个属性 (RotateTransform.Angle 属性和 Button.Width 属性)进行动画处理,它们使用两个动画命名空间中的类。 图 3-16 显示了按钮在不同时间。

<?Mapping XmlNamespace="animC" ClrNamespace="MSAvalon.Windows.Media.Animation" 
                               Assembly="PresentationCore" ?>
<?Mapping XmlNamespace="animF" ClrNamespace="MSAvalon.Windows.Media.Animation" 
                               Assembly="PresentationFramework" ?>
<Border
       xmlns="https://schemas.microsoft.com/2003/xaml"
       xmlns:animC="animC"
       xmlns:animF="animF"
       xmlns:def="Definition"
       Background="BlanchedAlmond" 
   >
  <Canvas Height="400" Width="400">
     <TransformDecorator Canvas.Top="200" Canvas.Left="100">
       <TransformDecorator.Transform>
         <TransformCollection>
           <RotateTransform Angle="135">
              <RotateTransform.AngleAnimations>
                 <animC:DoubleAnimationCollection>
                    <animC:DoubleAnimation From="0" To="360" Duration="4" 
                       AutoReverse="True" 
                       RepeatDuration="Indefinite"/>
                 </animC:DoubleAnimationCollection>

              </RotateTransform.AngleAnimations>

           </RotateTransform>
         </TransformCollection>
        </TransformDecorator.Transform>

        <ListBox >
          <ListItem> ListItem 1 </ListItem> 
          <ListItem> ListItem 2 </ListItem> 
          <ListItem> ListItem 3 </ListItem> 
        </ListBox>
     </TransformDecorator>

     <Button Width="40" Canvas.Top="10" Canvas.Left="10">
       <Button.Width>
         <animF:LengthAnimationCollection>
           <animF:LengthAnimation From="40" To="300" 
             AutoReverse="true" Begin="1" Duration="1.2"
             RepeatDuration="Indefinite"/>
         </animF:LengthAnimationCollection>
       </Button.Width>
       Button
     </Button>

  </Canvas>
</Border>

图 3-16。 不同时间动画按钮的视图

文档服务

Longhorn 平台提供广泛的服务,支持更好的联机文档查看体验。 有两种main服务:一个用于查看、分页和浏览文档内容的控件,以及旨在增强阅读体验的布局服务。

PageViewer 控件

如果要向用户显示文档以供联机查看,请使用 PageViewer 控件。 PageViewer 控件提供分页和页面导航功能。 控件自动将文档内容的格式设置为单独的页面。 用户可以使用页面查看器提供的控件直接导航到不同的页面。

分页和导航

传统上,在线内容(如网页)是连续的。 用户界面提供了滚动条,用于查看无法容纳在可见区域的内容。 实际上,你会将视图窗口“滚动”到文档中想要查看的位置。

使用分页,可以将文档的内容拆分为一个或多个单独的页面,类似于书籍。 Longhorn 平台通过包含多个控件来支持分页内容,这些控件可帮助你显示和导航显示为离散页面的内容。 此外,Longhorn 还提供分页应用程序编程接口 (API) 来扩展这些功能,并为自定义分页应用程序提供丰富的分页支持。

PageViewer 控件实际上是使用前面所述的控件组合技术从较小的控件生成的复杂控件。 PageViewer 控件使用 PageSource 和 PageElement 控件提供其分页功能。 PageSource 控件可跨页面拆分内容并设置其格式。 PageElement 控件呈现单个页面。 PageViewer 控件还使用 PageBar 控件来浏览页面。

使用 PageViewer 控件非常简单。 若要显示已知文档,可以使用它,如以下代码所示。 当然,可以挂接事件处理程序并更改源文档,使页面查看器显示不同的文档。

<Border
       xmlns="https://schemas.microsoft.com/2003/xaml"
       xmlns:def="Definition"
       Background="BlanchedAlmond" 
   >
  <PageViewer Source="AuthorsandPublishers.xaml" />
</Border>

图 3-17 显示浏览器窗口中托管的页面查看器,显示其文档。 请注意文档顶部的页面导航控件。

图 3-17。 PageViewer 控件

文档布局服务

Longhorn 还提供旨在改善阅读体验的文档布局服务。 Longhorn 包含对两种新类型文档的支持:

  • 自适应流文档
  • 固定布局文档

这些新的文档格式使开发人员能够为其应用程序的用户提供更好的文档阅读体验。

自适应流文档使用声明文档应为 Adaptive 的专用标记元素。 Longhorn 自动优化自适应文档,以充分利用可用的屏幕空间,并根据用户系统的功能或限制提供最佳阅读体验。

例如,用户可能具有最近引入的 16 乘 9 纵横比宽屏显示器之一。 很难阅读跨长水平线的文本行。 根据窗口的宽度,自适应文档可能会将文本分为两列、三列或更多列,从而减少用户扫描文本行的工作量。

在另一个示例中,文档可能包含在图像周围流动的图像和文本。 在非固定文档中收缩文档窗口的大小时,图像将保持固定大小,并且文本越来越少。 自适应文档在确定窗口中显示的文本不足时,可能会收缩图像。 这样,读者仍能大致了解图像所描绘的内容,但可以继续阅读图像上下文中的文本。 在较小的窗口中,与能够阅读更多文本相比,查看图像的每个像素对读者来说可能不那么重要和有用。 另一种方法(如将图像和周围的文本分离到单独的页面上)会破坏文档作者的意图,即在上下文中一起呈现文本和图像。

无论查看器的屏幕大小、窗口大小或输出设备如何,固定版式文档每次都会显示相同。 使用专用标记元素或使用 Microsoft Windows 矢量图形 (WVG) 打印机驱动程序打印文档来创建固定布局文档。

自适应布局文档

在自适应布局文档中,可以在根级标记中提供关键首选项。 然后,Longhorn 可以以充分利用窗口区域并增强其可读性的方式呈现文档。 Longhorn 自动确定页面的最佳宽度和列数、所有文本元素的理想大小、所有图形的最佳大小和位置,以及边距和装订线的宽度,以提供内容的最佳整体呈现效果。

生成自适应布局文档

若要创建自适应布局文档,请使用如下所示的声明性标记:

<Border
       xmlns="https://schemas.microsoft.com/2003/xaml"
       xmlns:def="Definition"
       Background="BlanchedAlmond" 
   >
  <AdaptiveMetricsContext ColumnPreference="Medium"
                          FontFamily="Arial">
    <TextPanel Background="white"> 
      <Section>
        <Heading OutlineLevel="1">Adaptive Layout Example</Heading>
        <Paragraph>
This example shows the advanced capabilities of Adaptive Flow 
Layout. Lorem ipsum dolor sit amet, consectetuer adipiscing 
elit, sed diam nonummy nibh euismod tin cidunt ut laoreet dolore 
magna aliquam erat volutpat. Ut wisi enim ad minim veni am, quis 
nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip 
ex ea commodo consequat. Duis autem vel eum iriure.</Paragraph>
        <Paragraph>
        <Image TextPanel.FlowBehavior="Figure" Source="picture1.jpg"
               TextPanel.Emphasis="Medium" />
Notice how images and text are flowed intelligently to enhance the 
reading experi ence. Lorem ipsum dolor sit amet, consectetuer 
adipiscing elit, sed diam nonummy  nibh euismod tincidunt ut laoreet 
dolore magna aliquam erat volutpat. Ut wisi e nim ad minim veniam, 
quis nostrud  exerci tation ullamcorper suscipit lobortis ni sl ut 
aliquip ex ea commodo consequat. Duis autem vel eum iriure.
</Paragraph>
        <Paragraph>Adaptive layout is an exciting new feature of Longhorn.
        <Image TextPanel.FlowBehavior="Figure" Source="picture2.jpg"
               TextPanel.Emphasis="Low" />
Lorem ipsum dolor sit amet, consectetuer 
adipiscing elit, sed diam nonummy  nibh euismod tincidunt ut laoreet 
dolore magna aliquam erat volutpat. Ut wisi e nim ad minim veniam, 
quis nostrud  exerci tation ullamcorper suscipit lobortis ni sl ut 
aliquip ex ea commodo consequat. Duis autem vel eum iriure.
</Paragraph>
      </Section>
    </TextPanel>
  </AdaptiveMetricsContext>
</Border>

Fixed-Layout文档

使用固定布局文档以完全相同的布局和格式呈现文档内容,与所使用的应用程序软件、硬件和操作系统无关。 此外,固定布局文档在所有输出设备上以相同的方式呈现。 固定布局文档是一组共同描述一个或多个页面外观的对象。

生成Fixed-Layout文档

可以使用两种不同的技术来生成固定布局文档:

  • 使用 Longhorn 打印机驱动程序将不带标记的文档打印到文件
  • 使用 XAML 编写固定布局文档

使用大多数 Microsoft Win32 应用程序 ((例如,Microsoft Office) 使用 Longhorn 打印机驱动程序)打印文档时,打印机驱动程序将创建一个 XAML 文件,其中包含标记,用于对打印文档中的每个字符、图像或矢量图形进行分页和定位。

可以选择将文档直接输出为标记文件,也可以选择将标记文件包含在容器内。 选择容器输出时,还可以对文档应用数字权限和文档保护。

或者,可以使用编辑器创建 XAML。 下面是固定布局文档的框架示例:

<FixedPanel xmlns="https://schemas.microsoft.com/2003/xaml/" >
  <FixedPage Width="8.50in" Height="11.00in"> <!-- PAGE 1 -->
    <Text FontFamily="Arial" FontSize="8.4" FixedPage.Left="1.250in"
          FixedPage.Top="0.530in" FontWeight="Bold">1.</Text>
    <Text FontFamily="Arial" FixedPage.Left="1.350in" FixedPage.Top="0.500in"
          FontWeight="Bold" FontSize="12">Fixed Document</Text>
  </FixedPage>
  <FixedPage>
    <Text>This is page 2</Text>
  </FixedPage>
  <FixedPage>
    <Text>This is page 3</Text>
  </FixedPage>
</FixedPanel>

总结

使用面板可以将显示图面划分为具有不同布局特征的区域。 有各种各样的控件可用于填充显示器的面板。 通过形状转换动画 ,可以生成动态图形输出。 使用控件组合,可以组合这些功能来生成所需的几乎任何用户界面。 可以生成自适应文档,以智能方式设置内容布局,使读者易于阅读。 或者,可以精确定位页面上的每个元素,并显式控制分页,以生成与输出设备完全一样显示的文档。 此外,还可以使用 XAML 以声明方式完成所有操作。 这肯定会击败编写WM_PAINT消息处理程序!

继续学习第 4 章:存储