使用 XAML 编写 Windows Presentation Foundation (WPF) 应用

本本介绍 XAML 语言的功能,并演示如何使用 XAML 编写 Windows Presentation Foundation (WPF) 应用。

  • 什么是 XAML

XAML 是一种声明性标记语言。 应用于 .NET Core 编程模型时,XAML 简化了为 .NET Core 应用创建 UI 的过程。 你可以在声明性 XAML 标记中创建可见的 UI 元素,然后使用代码隐藏文件(这些文件通过分部类定义与标记相联接)将 UI 定义与运行时逻辑相分离。

XAML 直接以程序集中定义的一组特定后备类型表示对象的实例化。 这与大多数其他标记语言不同,后者通常是与后备类型系统没有此类直接关系的解释语言。 XAML 实现了一个工作流,通过此工作流,各方可以采用不同的工具来处理 UI 和应用的逻辑。

以下示例演示如何创建 UI 中的按钮。 此示例用于初步了解 XAML 如何表示常用 UI 编程形式(它不是一个完整的示例)。

注意区分 xml xaml yaml wxml·
yaml是Docker的配置语言,xml是Android的前端UI语言,wxml是微信小程序前端UI语言。

XAML示例

<StackPanel>
    <Button Content="Click Me"/>
</StackPanel>

XAML实现的WPF界面
下面界面使用了Material Design UI 框架
在这里插入图片描述

1. XAML 语法概述

下面章节介绍 XAML 语法的基本形式,并提供一个简短的标记示例。

1.2 XAML 对象元素

对象元素语法始终以左尖括号 (<) 开头。 后跟要创建实例的类型的名称。 要完成对象元素标记,请以右尖括号 (>) 结尾。 也可以使用不含任何内容的自结束形式,方法是用一个正斜杠后接一个右尖括号 (/>) 来完成标记。

<StackPanel>
    <Button Content="Click Me"/>
</StackPanel>

1.2.1 特性语法(属性)

对象的属性通常可表示为对象元素的特性。符串。

<Button Background="Blue" Foreground="Red" Content="This is a button"/>

1.2.2 属性元素语法

以下示例演示在前面的特性语法示例中设置的相同属性,但这次对 Button 的所有属性使用属性元素语法。

<Button>
    <Button.Background>
        <SolidColorBrush Color="Blue"/>
    </Button.Background>
    <Button.Foreground>
        <SolidColorBrush Color="Red"/>
    </Button.Foreground>
    <Button.Content>
        This is a button
    </Button.Content>
</Button>

1.2.3 集合语法

如果某个特定属性采用集合类型,则在标记中声明为该属性的值内的子元素的项将成为集合的一部分。 在这种情况下,子对象元素的集合是设置为集合属性的值。

下面的示例演示用于设置 GradientStops 属性的值的集合语法。

<LinearGradientBrush>
    <LinearGradientBrush.GradientStops>
        <!-- no explicit new GradientStopCollection, parser knows how to find or create -->
        <GradientStop Offset="0.0" Color="Red" />
        <GradientStop Offset="1.0" Color="Blue" />
    </LinearGradientBrush.GradientStops>
</LinearGradientBrush>

1.3 XAML 内容属性

XAML 指定了一个语言功能,通过该功能,类可以指定它的一个且仅一个属性为 XAML 内容 属性。 该对象(Border)元素的子元素(TextBox )用于设置该内容属性(Width)的值(300)。 换言之,仅对内容属性而言,可以在 XAML 标记中设置该属性时省略属性元素,并在标记中生成更直观的父级/子级形式。

例如,Border 指定 Child 的内容 属性。 以下两个 Border 元素的处理方式相同。 第一个元素利用内容属性语法并省略 Border.Child 属性元素。 第二个元素显式显示 Border.Child。

<Border>
    <TextBox Width="300"/>
</Border>
<!--explicit equivalent-->
<Border>
    <Border.Child>
        <TextBox Width="300"/>
    </Border.Child>
</Border>

作为 XAML 语言的规则,XAML 内容属性的值必须完整在其他属性元素之前或之后指定。 例如,以下标记不会进行编译。

<Button>I am a
  <Button.Background>Blue</Button.Background>
  blue button</Button>

下面则会编译

<Button>
<Button.Background>Blue</Button.Background>
I am a blue button
</Button>

1.3.1 内容属性和集合语法组合

考虑以下示例。

<StackPanel>
  <Button>First Button</Button>
  <Button>Second Button</Button>
</StackPanel>

此处,每个 Button 都是 StackPanel 的子元素。 这是一个简单直观的标记,此标记由于两个不同的原因省略了两个标记。

省略了 StackPanel.Children 属性元素:StackPanel 派生自 Panel。 Panel 将 Panel.Children 定义为其 XAML 内容属性。

省略了 UIElementCollection 对象元素: Panel.Children 属性采用类型 UIElementCollection,该类型实现 IList。 根据处理 IList 等集合的 XAML 规则,集合的元素标记可以省略。 (在本例中,UIElementCollection 实际上无法实例化,因为它不公开无参数构造函数,这便是 UIElementCollection 对象元素显示为注释掉的原因)。

<StackPanel>
  <StackPanel.Children>
      <Button>First Button</Button>
      <Button>Second Button</Button>
  </StackPanel.Children>
</StackPanel>

1.4 特性语法

1.4.1 事件(点击,长按,选中,获取焦点等)

<Button Click="Button_Click" >Click Me!</Button>

除此特性语法示例外,还有更多关于 WPF 中的事件和 XAML 的内容。 例如,可了解此处引用的 ClickHandler 表示什么,以及它是如何定义的。 这将在本文后面的事件和 XAML 代码隐藏一节中介绍。

1.4.2 XAML 中的大小写和空白

  • 一般而言,XAML 区分大小写。 出于解析后备类型的目的,WPF XAML 按照 CLR 区分大小写的相同规则区分大小写。
  • XAML 语言关键字和基元也区分大小写。 值并不总是区分大小写。

值是否区分大小写将取决于与采用该值的属性关联的类型转换器行为,或取决于属性值类型。 例如,采用 Boolean 类型的属性可以采用 true 或 True 作为等效值,但只是因为将字符串转换为 Boolean 的本机 WPF XAML 分析程序类型转换已经允许将这些值作为等效值。

1.4.3 标记扩展

标记扩展是一个 XAML 语言概念。 用于提供特性语法的值时,大括号({ 和 })表示标记扩展用法。WPF 应用编程中最常用的标记扩展是 Binding(用于数据绑定表达式)以及资源引用 StaticResource 和 DynamicResource。

例如,以下标记使用特性语法设置 Style 属性的值。

<Window x:Class="index.Window1"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window1" Height="100" Width="300">
    <Window.Resources>
        <SolidColorBrush x:Key="MyBrush" Color="Gold"/>
        <Style TargetType="Border" x:Key="PageBackground">
            <Setter Property="BorderBrush" Value="Blue"/>
            <Setter Property="BorderThickness" Value="5" />
        </Style>
    </Window.Resources>
    <Border Style="{ 
        StaticResource PageBackground}">
        <StackPanel>
            <TextBlock Text="Hello" />
        </StackPanel>
    </Border>
</Window>

有关特定在 WPF 中实现的所有 XAML 标记扩展的参考列表,请参阅 WPF XAML 扩展。 有关由 System.Xaml 定义并且可更广泛用于 .NET Core XAML 实现的标记扩展的参考列表,请参阅 XAML 命名空间 (x:)语言功能。 有关标记扩展概念的详细信息,请参阅标记扩展和 WPF XAML。

1.5 类型转换器

Thickness 结构是一个类型示例,该类型拥有可使用 XAML 的类型转换。 Thickness 指示嵌套矩形中的度量,可用作属性(如 Margin)的值。 通过对 Thickness 放置类型转换器,所有使用 Thickness 的属性都可以更容易地在 XAML 中指定,因为它们可指定为特性。 以下示例使用类型转换和特性语法来为 Margin 提供值:

<Button Margin="10,20,10,30" Content="Click me"/>

下面的示例中,Margin 改为通过包含 Thickness 对象元素的属性元素语法进行设置。


<Button Content="Click me">
    <Button.Margin>
        <Thickness Left="10" Top="20" Right="10" Bottom="30"/>
    </Button.Margin>
</Button>

1.6 根元素和命名空间

一个 XAML 文件只能有一个根元素,以便同时作为格式正确的 XML 文件和有效的 XAML 文件。 对于典型 WPF 方案,可使用在 WPF 应用模型中具有突出意义的根元素(例如,页面的 Window 或 Page、外部字典的 ResourceDictionary 或应用定义的 Application)。

以下示例演示 WPF 页的典型 XAML 文件的根元素,此根元素为 Page。

<Page x:Class="index.Page1"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      Title="Page1">

</Page>

根元素还包含特性 xmlns 和 xmlns:x。 这些特性向 XAML 处理器指示哪些 XAML 命名空间包含标记将其作为元素引用的后备类型的类型定义。 xmlns 特性明确指示默认的 XAML 命名空间。

只有在每个 XAML 文件的根元素上,xmlns 特性才是绝对必需的。 要引用来自 WPF 程序集的 XAML 元素,只需将默认 XAML 命名空间声明为默认 xmlns。

1.6.1x: 前缀

在之前的根元素示例中,前缀 x: 用于映射 XAML 命名空间 http://schemas.microsoft.com/winfx/2006/xaml

x:Key:为 ResourceDictionary(或其他框架中的类似字典概念)中的每个资源设置唯一的键。 在典型的 WPF 应用标记中的所有 x: 用法中,x:Key 可能占到 90%。

<Page x:Class="index.Page1"
</Page>

x:Class:向为 XAML 页提供代码隐藏的类指定 CLR 命名空间和类名。 必须具有这样一个类才能支持每个 WPF 编程模型的代码隐藏,因此即使没有资源,也几乎总是能看到映射的 x:。

x:Name:处理对象元素后,为运行时代码中存在的实例指定运行时对象名称。使用运行时代码从初始化的 XAML 中查找命名元素。 最常见的此类属性是 FrameworkElement.Name。

x:Static:启用一个返回静态值的引用,该静态值不是与 XAML 兼容的属性。

x:Type:根据类型名称构造 Type 引用。 用于指定采用 Type(例如 Style.TargetType)的特性,但属性经常具有本机的字符串到 Type 的转换功能,因此使用 x:Type 标记扩展用法是可选的。

1.6.2 自定义前缀和自定义类型

对于自身的自定义程序集或 PresentationCore 、PresentationFramework 和 WindowsBase 的 WPF 核心以外的程序集,可以将该程序集指定为自定义 xmlns 映射的一部分。 只要该类型能够正确地实现以支持正在尝试的 XAML 用法,就可以在 XAML 中引用该程序集中的类型。

下面是一个说明自定义前缀如何在 XAML 标记中工作的基本示例。

前缀 custom 在根元素标记中定义,并映射为打包在应用中并随应用一起提供的特定程序集。 此程序集包含 NumericUpDown 类型,实现该类型的目的是在支持常规 XAML 用法之外,还可以使用允许在 WPF XAML 内容模型的此特定点执行插入的类继承。 通过使用该前缀,此 NumericUpDown 控件的一个实例声明为对象元素,以便 XAML 分析程序可找到包含该类型的 XAML 命名空间,从而找到包含该类型定义的后备程序集的位置。

<Page
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:custom="clr-namespace:NumericUpDownCustomControl;assembly=CustomLibrary"
    >
  <StackPanel Name="LayoutRoot">
    <custom:NumericUpDown Name="numericCtrl1" Width="100" Height="60"/>
...
  </StackPanel>
</Page>

Assembly:配件或程序集, 以示和组件(Component)加以区别

2 事件、XAML标记、代码隐藏

2.1XAML标记、代码隐藏

大多数 WPF 应用既包括 XAML 标记,也包括代码隐藏。

  • XAML 标记编写为 .xaml 文件
  • CLR 语言(如 Microsoft Visual Basic 或 C#)用于编写代码隐藏文件。

在 WPF 编程和应用程序模型中对 XAML 文件进行标记编译时,XAML 文件的 XAML 代码隐藏文件的位置是通过如下方式来标识的:

以 XAML 根元素的 x:Class 特性形式指定一个命名空间和类。

通过目前已介绍的示例,你已了解了几个按钮,但这其中没有一个按钮具有任何与其关联的逻辑行为。 为对象元素添加行为的主要应用程序是使用元素类的现有事件,并为在运行时引发该事件时调用的特定处理程序。

  • 在XAML 标记中指定事件名称以及处理程序名称,
  • 在代码隐藏中定义实现处理程序的代码。

XAML 标记

<Window x:Class="index.Window2"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="Window2" Height="450" Width="800">
    <StackPanel>
        <Button Click="Button_Click">Click me</Button>
    </StackPanel>
</Window>

代码隐藏

private void Button_Click(object sender, RoutedEventArgs e)
{
    
    
    var buttonControl = (Button)e.Source;
    buttonControl.Foreground = Brushes.Red;
}

请注意,代码隐藏文件使用 CLR 命名空间 ExampleNamespace 并将 ExamplePage 声明为该命名空间内的一个分部类。 这相当于 ExampleNamespace. 的 x:Class 特性值, 在标记根中提供的 ExamplePage。 WPF 标记编译器将通过从根元素类型派生一个类,为编译的任何 XAML 文件创建一个分部类。 在提供定义同一分部类的代码隐藏时,将在与编译的应用相同的命名空间和类中合并生成的代码。

如果不需要创建单独的代码隐藏文件,还可以将代码内联到 XAML 文件中。 但是,内联代码是一种通用性较低的方法,具有很多的限制。 有关详细信息,请参阅 WPF 中的代码隐藏和 XAML。

2.2 路由事件

路由事件是一个特殊的事件功能,该功能是 WPF 的基础。 路由事件使一个元素可以处理另一个元素引发的事件

命名元素

  • 以下内容由英语翻译成中文,理解难度较大,请硬着头皮慢慢读

默认情况下, XAML 对象元素没有唯一标识符或对象引用。 相反,在代码中调用view构造函数生成UI的对象因素,则使用构造函数设置变量,以便以后在代码中引用该实例。

  • 代码隐藏如何找到 XAML 对象元素?

XAML 定义了 x:Name 特性。 可以在任何对象元素上设置 x:Name 特性的值。

在代码隐藏中,所选标识符等效于引用所构造的实例的实例变量。 在所有方面,命名元素以类似于对象实例的方式工作(名称引用实例),并且代码隐藏可以通过引用命名元素来处理应用内的运行时交互。
实例和变量之间的这种连接是由 WPF XAML 标记编译器实现的,并且更具体地涉及到功能和模式,例如本文中未详细讨论的 InitializeComponent。

WPF 框架级 XAML 元素继承 Name 属性,该属性等效于 XAML 定义的 x:Name 特性。 其他某些类也为 x:Name(通常也定义为 Name 属性)提供属性级等效项。 一般而言,如果在所选元素/类型的成员表中找不到 Name 属性,则可以改用 x:Name。 x:Name 值将通过特定子系统或通过 FindName 等实用工具方法,为可在运行时使用的 XAML 元素提供标识符。

学过Android 的同学是否记得 findViewById()
学过小程序的同学是否记得findCompent()
其实和 FindName 方法是一样的

下面的示例对 StackPanel 元素设置 Name。 然后,该 StackPanel 中 Button 上的一个处理程序通过其实例引用 buttonContainer(由 Name 设置)来引用 StackPanel。

<StackPanel Name="buttonContainer">
    <Button Click="RemoveThis_Click">Click to remove this button</Button>
</StackPanel>
private void RemoveThis_Click(object sender, RoutedEventArgs e)
{
    
    
    var element = (FrameworkElement)e.Source;
    
    if (buttonContainer.Children.Contains(element))
        buttonContainer.Children.Remove(element);
}

XAML 名称在可预测的某个范围内强制名称唯一。 页面的主标记表示一个唯一的 XAML 名称范围,而该 XAML 名称范围的边界是该页面的根元素。

2.3 附加属性和附加事件

XAML 指定了一个语言功能,该功能允许对任何元素指定某些属性或事件,即使要设置属性或事件的元素的类型定义中不存在该属性或事件。 该功能的属性版本称为附加属性,事件版本称为附加事件。

从概念上讲,可以将附加属性和附加事件视为可以在任何 XAML 元素/对象实例上设置的全局成员。 但是,元素/类或更大的基础结构必须支持附加值的后备属性存储。

通常通过特性语法来使用 XAML 中的附加属性。 在特性语法中,可以采用 ownerType.propertyName 的形式指定附加属性。

表面上,这与属性元素用法类似,但在这种情况下,所指定的 ownerType 始终是一种与从中要设置附加属性的对象元素不同的类型。 ownerType 这种类型提供 XAML 处理器为获取或设置附加属性值所需要的访问器方法。

附加属性的最常见方案是使子元素向其父元素报告属性值。

下面的示例演示 DockPanel.Dock 附加属性。 DockPanel 类为 DockPanel.Dock 定义访问器,并拥有附加属性。 DockPanel 类还包括一个逻辑,该逻辑迭代其子元素并具体检查每个元素是否具有 DockPanel.Dock 设置值。 如果找到一个值,将在布局过程中使用该值定位子元素。 使用 DockPanel.Dock 附加属性和此定位功能实际上是 DockPanel 类激动人心的一面。

<DockPanel>
    <Button DockPanel.Dock="Left" Width="100" Height="20">I am on the left</Button>
    <Button DockPanel.Dock="Right" Width="100" Height="20">I am on the right</Button>
</DockPanel>

在 WPF 中,大部分或所有附加属性还作为依赖属性实现。

附加事件使用类似的 ownerType.eventName 特性语法形式。 和非附加事件一样,XAML 中附加事件的特性值指定对元素处理事件时调用的处理程序方法的名称。 在 WPF XAML 中使用附加事件并不常见。

3.基类型

基础 WPF XAML 及其 XAML 命名空间是类型的一个集合,这些类型对应于 CLR 对象和 XAML 的标记元素。 但是,并不是所有的类都能映射到元素。 抽象类(如 ButtonBase)和某些非抽象基类在 CLR 对象模型中用于继承。 基类(包括抽象类)对于 XAML 开发仍然很重要,因为每个具体的 XAML 元素都从其层次结构中的某个基类继承成员。 通常,这些成员包括可以设置为元素特性的属性或者可以处理的事件。 FrameworkElement 是 WPF 在 WPF 框架级的具体 UI 基类。 设计 UI 时,将使用各种形状、面板、装饰器或控件类,它们全部派生自 FrameworkElement。 一个相关的基类 FrameworkContentElement 使用可在 FrameworkElement 中特意镜像 API 的 API,支持适合流布局表示形式的面向文档的元素。 元素级的特性和 CLR 对象模型的组合提供一组通用的属性,这些属性可以在大多数具体的 XAML 元素上设置,而不管具体的 XAML 元素及其基础类型。

4.安全性

XAML 是一种直接表示对象实例化和执行的标记语言。 因此,在 XAML 中创建的元素能够像你的应用代码那样与系统资源进行交互(如网络访问、文件系统 IO)。 XAML 还与承载应用具有相同的系统资源访问权限。

WPF 中的代码访问安全性 (CAS)
与 .NET Framework 不同,适用于 .NET 的 WPF 不支持 CAS。

从代码加载 XAML
XAML 可用于定义整个 UI,但有时也适合在 XAML 中定义一部分 UI。 此功能可用于:

启用部分自定义。
本地存储 UI 信息。
对业务对象进行建模。


  • 更新日期:2022年12月1日14:33:08

猜你喜欢

转载自blog.csdn.net/gao511147456/article/details/128130050
今日推荐