PandHedge

XAML

2025-09-13
PandHedge
C#
 

什么是 XAML?XAML 概述 - UWP applications | Microsoft Learn

可扩展应用程序标记语言(XAML)是一种声明性语言。 具体来讲,XAML 可初始化对象和设置对象的属性,使用一种可显示多个对象间分层关系的语言结构,还使用了一种支持类型扩展的支持类型约定。 可以在声明性 XAML 标记中创建可见的 UI 元素。 然后,可以为每个 XAML 文件关联单独的代码隐藏文件,该文件可以响应事件并操作最初在 XAML 中声明的对象。

XAML 对象元素

对象元素通常声明类型的实例。 若要使用对象元素语法声明对象,请编写如下标记: <objectName> </objectName>,其中 objectName 是要实例化的对象的类型名称。如果一个对象不包含其他对象,可以使用一个自结束标记代替起始/结束标记对来声明对象元素:<Canvas />

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

这指定了两个对象元素:<StackPanel>(包含内容,并在稍后的结束标记),以及<Button .../>(具有多个属性的自闭合形式)。

容器

许多用作 UI 元素的对象(如 Canvas)可以包含其他对象。 这些有时称为容器。 下面的示例演示包含一个元素(一个 Rectangle*)的 *Canvas 容器。

属性语法(属性)

对象的属性通常可表示为对象元素的属性。 属性语法将正在设置的对象属性命名,后跟赋值运算符 (=)。 属性的值始终指定为引号中包含的字符串。

<Button Background="Blue" Foreground="Red" Content="This is a button"/>
  • 通过使用属性语法。
  • 通过使用属性元素语法。
  • 通过使用元素语法,内容(内部文本或子元素)正在设置对象的 XAML 内容属性。
  • 通过使用集合语法(通常是隐式集合语法)。

属性元素语法

对于对象元素的某些属性,不能使用属性语法,因为提供属性值所需的对象或信息不能在属性语法的引号和字符串限制中充分表达。 对于这些情况,可以使用称为属性元素语法的不同语法。属性元素开始标记的语法为 <TypeName.PropertyName>. 通常,该标记的内容是属性值所对应类型的对象元素。 指定内容后,必须使用结束标记关闭属性元素。 结束标记的语法为 </TypeName.PropertyName>.

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

集合语法

XAML 语言包括一些可生成更多可读标记的优化。 一种这样的优化是,如果特定属性采用集合类型,则在标记中声明为该属性的值中的子元素的项将成为集合的一部分。 在这种情况下,子对象元素的集合是设置为集合属性的值。

<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>

XAML 内容属性

XAML 指定一种语言功能,其中一个类可以指定其属性之一作为 XAML 内容 属性。 该对象元素的子元素用于设置该内容属性的值。 换句话说,特有地针对内容属性,在 XAML 标记中设置该属性时可以省略属性元素,以便在标记中更明显地表现父/子隐喻。

作为 XAML 语言的规则,XAML 内容属性的值必须在该对象元素上的任何其他属性元素之前或之后完全提供。

文本内容

少量 XAML 元素可以直接处理文本作为其内容。 若要启用此功能,以下情况之一必须成立:

  • 该类必须声明内容属性,并且内容属性必须是可分配给字符串的类型(类型可以是)。Object 例如,任何ContentControl使用Content作为其内容属性和类型Object,这支持在ContentControl上的以下用法,比如Button<Button>Hello</Button>
  • 该类型必须声明类型转换器,在这种情况下,文本内容用作该类型转换器的初始化文本。 例如, <Brush>Blue</Brush> 将内容值 Blue 转换为画笔。 在实践中,这种情况不太常见。
  • 该类型必须是已知的 XAML 语言基元。

属性语法(事件)

属性语法还可用于事件而不是属性的成员。 在这种情况下,属性的名称是事件的名称。 在 XAML 事件的 WPF 实现中,特性的值是实现该事件的委托的处理程序的名称。 例如,以下标记将事件处理器 Click 分配给在标记中创建的 Button

[XAML 平台 - UWP applications Microsoft Learn](https://learn.microsoft.com/zh-cn/windows/uwp/xaml-platform/)

App.xaml 和 App.xaml.cs

  • App.xaml 是声明跨应用使用的资源的文件。
  • App.xaml.cs是 App.xaml 的后台代码文件。 与所有代码隐藏页一样,它包含调用该方法的 InitializeComponent 构造函数。 你不编写 InitializeComponent 方法。 它由 Visual Studio 生成,其主要目的是初始化 XAML 文件中声明的元素。
  • App.xaml.cs是应用的入口点。
  • App.xaml.cs 还包含用于处理应用的激活 和挂起 的方法。

MainPage.xaml

  • MainPage.xaml 是定义应用的 UI 的位置。 可以使用 XAML 标记直接添加元素,也可以使用 Visual Studio 提供的设计工具。
  • MainPage.xaml.cs是 MainPage.xaml 的后台代码页。 这是您添加应用逻辑和事件处理程序的位置。
  • 这两个文件一起定义了一个名为MainPage的新类,该类在命名空间中继承自HelloWorld

Package.appxmanifest

  • 描述应用的清单文件:其名称、说明、磁贴、起始页等。
  • 包括应用包含的依赖项、资源和文件的列表。

一组徽标图像

  • 资产/Square150x150Logo.scale-200.png 和 Wide310x150Logo.scale-200.png 表示你的应用(中等或宽大小)在开始菜单中。
  • 资产/Square44x44Logo.png 表示您的应用,在“开始”菜单、任务栏和任务管理器的应用列表中。
  • 资产/StoreLogo.png 代表你在 Microsoft 应用商店中的应用程序。
  • 资产/SplashScreen.scale-200.png 是应用启动时显示的启动画面。
  • 当系统锁定时,资产/LockScreenLogo.scale-200.png 可用于表示锁屏上的应用。

依赖属性概述

本主题介绍了在编写使用 C++、C# 或 Visual Basic 的 Windows 运行时应用并为 UI 使用 XAML 定义时可用的依赖属性系统。

什么是依赖属性?

依赖属性是一种特定类型的属性。 这种属性的特殊之处在于,其属性值受到 Windows 运行时中专用属性系统的跟踪和影响。

为了支持依赖属性,定义该属性的对象必须是一个 DependencyObject(也就是说,一个在其继承中的某个位置具有 DependencyObject 基类的类)。

依赖属性的用途是提供一种系统方式,用来基于其他输入(在应用运行时其内部出现的其他属性、事件和状态)计算属性的值。 其他输入可能包括:

  • 外部输入,例如用户首选项
  • 即时属性确定机制,例如数据绑定、动画和故事板
  • 多用途模板模式,例如资源和样式
  • 通过与对象树中其他元素的父子关系知道的值

依赖属性代表或支持编程模型的某种特定功能,用于定义 Windows 运行时应用,这种模型使用 XAML 编写 UI,使用 C#、Microsoft Visual Basic 或 Visual C++ 组件扩展 (C++/CX) 编写代码。 这些功能包括:

  • 数据绑定
  • 样式
  • 情节提要动画
  • “PropertyChanged”行;一种依赖属性,实现该依赖属性可提供回调,从而将更改传播给其他依赖属性
  • 使用来自属性元数据的默认值
  • 一般属性系统实用工具,例如 ClearValue 和元数据查找

依赖属性和 Windows 运行时属性

依赖属性提供一种全局内部属性存储来在运行时支持应用内的所有依赖属性,从而扩展基本的 Windows 运行时属性功能。 这种方法可以替代为具有专用字段的属性(在属性定义类中为专用)提供支持的标准模式。

术语 说明  
依赖属性 存在于 DependencyProperty 标识符上的一个属性(如下所示)。 通常该标识符可用作定义 DependencyObject 派生类的一个静态成员。  
依赖属性标识符 用于标识属性的常量值,它通常公开显示且只读。  
属性包装器 Windows 运行时属性的可调用 getset 实现。 或者原始定义的特定于语言的投影。 get 属性包装器实现调用 GetValue,传递相关的依赖属性标识符。  

下面的示例定义一个自定义依赖属性,就像 C# 中的定义一样,然后显示依赖属性标识符与属性包装器之间的关系。

public static readonly DependencyProperty LabelProperty = DependencyProperty.Register(
  "Label",
  typeof(string),
  typeof(ImageWithLabelControl),
  new PropertyMetadata(null)
);


public string Label
{
    get { return (string)GetValue(LabelProperty); }
    set { SetValue(LabelProperty, value); }
}

依赖属性的注册

public static readonly DependencyProperty LabelProperty = DependencyProperty.Register(
  "Label",                 // 属性名称
  typeof(string),          // 属性类型
  typeof(ImageWithLabelControl), // 所属控件类型
  new PropertyMetadata(null)     // 属性元数据
);

参数详解:

  1. “Label”

    • 依赖属性的名称,在 XAML 中使用的标识符(如<控件 Label="值"/>)。
  2. typeof(string)

    • 属性的类型,这里是字符串类型。
  3. typeof(ImageWithLabelControl)

    • 定义该依赖属性的控件类型。这表明Label属性属于ImageWithLabelControl控件。
  4. new PropertyMetadata(null)

    • 属性元数据,包含属性的默认行为:
      • null 是默认值(即未显式设置时,Label值为null)。
      • 还可设置属性变更回调、强制值回调等(此处未设置)。

包装属性(CLR 属性)

public string Label
{
    get { return (string)GetValue(LabelProperty); }
    set { SetValue(LabelProperty, value); }
}

作用:

  • 提供符合 CLR 规范的属性访问方式(对象.Label)。
  • 实际上是调用依赖属性系统的GetValue/SetValue方法。

完整示例(XAML 中使用)

<local:ImageWithLabelControl 
    Label="这是一个标签"
    Source="image.jpg" />

为什么需要依赖属性?

普通 CLR 属性无法支持:

  • XAML 中的属性系统(如绑定、样式)
  • 自动的属性变更通知
  • 复杂的属性值优先级(如动画 > 本地值 > 样式)

依赖属性通过中央存储系统解决了这些问题,是 WPF/UWP 控件开发的核心机制。

  1. 动画值:活动动画、视觉状态动画或具有 HoldEnd 行为的动画。 若要拥有任何实用效果,则适用于属性的动画必须拥有比基础(无动画)值更高的优先级,即使该值进行了本地设置也是如此。
  2. 本地值:本地值可通过属性包装器设置,这也等同于在 XAML 中设置为属性或属性元素,或通过使用特定实例的属性调用 SetValue 方法来设置。 如果使用绑定或静态资源来设置本地值,优先级列表中的每个操作都认为本地值已设置,如果设置了一个新本地值,绑定或资源引用将被清除。
  3. 模板属性:如果在某个模板(来自 ControlTemplateDataTemplate)中创建一个元素,该元素就会拥有这些模板属性。
  4. 样式设置器:来自页面或应用程序资源的样式内某个 Setter 的值。
  5. 默认值:一个依赖属性可在其元数据中包含一个默认值。

模板属性

模板属性作为一个优先级项,不适用于直接在 XAML 页面标记中声明的元素的任何属性。 模板属性概念只适用于 Windows 运行时将 XAML 模板应用于 UI 元素并因此而定义其视觉效果时所创建的对象。

依赖属性名称约定

依赖属性具有命名约定;需要在除一些例外情况外的所有情形中遵循这些约定。 依赖属性本身有一个基本名称(上一个示例中的“Label”),它作为 Register 的第一个参数提供。 该名称必须在每个注册类型中是唯一的,这种唯一性需求也适用于任何继承的成员。 通过基础类型继承的依赖属性已被视为注册类型的一部分;不能再次注册继承属性的名称。

创建标识符属性时,将你注册属性时的属性名称与后缀“Property”结合在一起(例如“LabelProperty”)。 此属性是依赖属性的标识符,并且它用作你在自己的属性包装器中执行的 SetValueGetValue 调用的输入。 它还供属性系统以及其他 XAML 处理器(例如 {x:Bind})使用

实现包装器

属性包装器应该在 get 实现中调用 GetValue,在 set 实现中调用 SetValue

自定义依赖属性的属性元数据

向一个依赖属性分配属性元数据时,针对属性所有者类型的每个实例或其子类,向该属性应用相同的元数据。 在属性元数据中,你可以指定两种行为:

  • 属性系统在所有情况下向属性分配的默认值。
  • 只要检测到属性值更改,就会在属性系统中自动调用静态回调方法。

使用属性元数据调用注册

在调用 DependencyProperty.Register 的先前示例中,我们为 propertyMetadata 参数传递了一个 Null 值。 要使依存关系属性能够提供一个默认值,或使用某个属性已更改的回调,必须定义一个提供其中一项或全部两项功能的 PropertyMetadata 实例。

通常,你将在 DependencyProperty.Register 的参数内提供一个 PropertyMetadata,作为一个内联创建的参数。

public static readonly DependencyProperty LabelProperty = DependencyProperty.Register(
  nameof(Label),
  typeof(String),
  typeof(ImageWithLabelControl),
  new PropertyMetadata(null,new PropertyChangedCallback(OnLabelChanged))
);

CreateDefaultValueCallback

在某些情况下,你将为在多个 UI 线程上使用的对象定义依存关系属性。 如果你要定义由多个应用使用的某个数据对象,或者要定义你在多个应用中使用的某个控件,则可能属于这种情况。 你可以通过提供一个 CreateDefaultValueCallback 实现(而不是一个默认值实例)来启用在不同 UI 线程之间对象的交换,默认值实例被绑定到注册该属性的线程。

附加属性概述

附加属性 是一个 XAML 概念。 附加属性允许在对象上设置其他属性/值对,但属性不是原始对象定义的一部分。 附加属性通常定义为依赖属性的专用形式,该依赖属性在所有者类型的对象模型中没有传统的属性包装器。

XAML 中的附加属性

在 XAML 中,通过使用语法 AttachedPropertyProvider.PropertyName设置附加属性。 下面是如何在 XAML 中设置 Canvas.Left 的示例。

<Canvas>
  <Button Canvas.Left="50">Hello</Button>
</Canvas>

为何使用附加属性?

附加属性是一种突破编码约定的方法,这些约定在运行时可能阻止处于关系中的不同对象彼此信息交流。 当然,可以将属性放在通用基类上,以便每个对象只能获取和设置该属性。 但最终,你可能想要执行此操作的场景数量将膨胀到具有可共享属性的基类。 它甚至可能引入一种情况,在这种情况下,从数百个后代中,只有两个试图使用该属性。 这不是很好的类设计。 为了解决此问题,附加属性概念使对象能够为其自己的类结构未定义的属性赋值。 定义类可以在对象树中创建各种对象后,在运行时从子对象读取值。

自定义附加属性

附加属性引用的特殊语法

附加属性名称中的点是标识模式的关键部分。 当语法或情况将点视为具有其他含义时,有时存在歧义。 例如,在绑定路径中,点被视为对象模型的遍历方式。 在大多数情况下,涉及此类歧义,附加属性有一种特殊的语法,使内部点仍可解析为 所有者属性 附加属性的分隔符。

  • 若要将附加属性指定为动画的目标路径的一部分,请将附加的属性名称括在括号(“()”中,例如“(Canvas.Left)”。 有关详细信息,请参阅 属性路径语法

警告

Windows 运行时 XAML 实现的现有限制是无法对自定义附加属性进行动画处理。

  • 若要将附加属性指定为从资源文件到 x:Uid的资源引用的目标属性,请使用一种特殊语法,即在方括号(“[]”)中插入代码样式的完全限定 声明,以故意打破作用域。 例如,假设存在元素 <TextBlock x:Uid="Title" />,则针对该实例的 Canvas.Top 值的资源文件中的资源键为“Title”。[using:Windows.UI.Xaml.Controls]Canvas.Top”。 有关资源文件和 XAML 的详细信息,请参阅 快速入门:翻译 UI 资源

附加属性的应用场景

当需要有一个可用于定义类之外的其他类的属性设置机制时,建议创建附加属性。 最常见的应用场景是布局和服务支持。 现有布局属性的示例包括 Canvas.ZIndex *Canvas.Top* 在布局应用场景中,作为布局控制元素的子元素存在的元素能够分别向其父级元素表达布局要求,其中每个元素都设置一个被父级定义为附加属性的属性值。 Windows 运行时 API 中的服务支持应用场景是 ScrollViewer 的一组附加属性,例如 ScrollViewer.IsZoomChainingEnabled

注册自定义附加属性

访问器

获取 PropertyName”访问器的签名必须是这个。

public static`*valueType* **获取***PropertyName* `(DependencyObject target)

Microsoft Visual Basic 的签名是这个。

Public Shared Function Get`*PropertyName*`(ByVal target As DependencyObject) As `*valueType*`)

目标对象可以是实现中更具体的类型,但必须从 DependencyObject 派生。 valueType 返回值在实现中也可以指定为更具体的类型。 基本对象类型是可接受的,但附加属性通常要强制执行类型安全。 建议在 getter 和 setter 签名中使用键入。

代码示例

此示例演示依赖属性注册(使用 RegisterAttached 方法),以及自定义附加属性的获取设置访问器。 在此示例中,附加属性名称为 IsMovable。 因此,访问器必须名为 GetIsMovableSetIsMovable。 附加属性的所有者是名为 GameService 没有自己的 UI 的一项服务,其唯一的作用就是在使用 GameService.IsMovable 附加属性时提供附加属性服务。

public class GameService : DependencyObject
{
    public static readonly DependencyProperty IsMovableProperty = 
    DependencyProperty.RegisterAttached(
      "IsMovable",
      typeof(Boolean),
      typeof(GameService),
      new PropertyMetadata(false)
    );
    public static void SetIsMovable(UIElement element, Boolean value)
    {
        element.SetValue(IsMovableProperty, value);
    }
    public static Boolean GetIsMovable(UIElement element)
    {
        return (Boolean)element.GetValue(IsMovableProperty);
    }
}

从 XAML 标记设置自定义附加属性

定义附加属性并将其支持成员包含在自定义类型的一部分后,必须使定义可用于 XAML 用法。 为此,必须映射一个 XAML 命名空间,该命名空间将引用包含相关类的代码命名空间。 如果将附加属性定义为库的一部分,则必须将该库作为应用的应用包的一部分包含。

buyvtycfrt7xdr57

事件和路由事件概述

重要的 API

我们会介绍在使用 C#、Visual Basic 或 Visual C++ 组件扩展 (C++/CX) 作为编程语言并使用 XAML 进行 UI 定义时,针对 Windows 运行时应用的事件的编程概念。 你可以在 XAML 中的 UI 元素声明中为事件分配处理程序,或者在代码中添加处理程序。 Windows 运行时支持路由事件:借助此功能,某些输入事件和数据事件可由引发该事件的对象以外的对象来处理。 在定义控件模板或使用页面或布局容器时,路由事件十分有用。

定义事件处理程序

对于作为 UI 元素并在 XAML 中声明的对象,事件处理程序代码在分部类中定义,其中该分部类可充当 XAML 页面的代码隐藏。 事件处理程序是作为与 XAML 关联的分部类的一部分编写的方法。

“发送方”参数和事件数据

你为事件编写的处理程序可以访问两个值,而这两个值可用作调用处理程序的每个情况的输入。 第一个值是“发送方”,它是对处理程序所附加到的对象的引用。 “发送方”参数键入为基对象类型。 常用技术是将发送方强制转换为更精确的类型。 如果希望检查或更改发送方对象本身的状态,此技术很有用。 基于你自己的应用设计,你通常知道一种可将“发送方”安全强制转换到其中的类型(基于附加处理程序或其他设计指定的位置)。

第二个值是事件数据,它通常在语法定义中显示为 e 参数。 你可以查看为所处理的特定事件分配的委派的 e 参数,然后使用 Visual Studio 中的 IntelliSense 或对象浏览器,以便发现事件数据的哪些属性可用。 或者,你也可以使用 Windows 运行时参考文档。

使用异步模式的事件处理程序

在某些情况下,你将需要使用在事件处理程序中使用异步模式的 API。 例如,可以使用 AppBar *中的*按钮来显示文件选取器并与之交互。 不过,许多文件选取器 API 都是异步的。 必须在异步/可等待的范围内进行调用,而编译器将会强制执行此操作。 因此,你可以执行的操作是将 异步 关键字添加到事件处理程序,以便处理程序现在 为异步void。 现在,允许事件处理程序进行异步/可等待调用。

路由事件

使用 C#、Microsoft Visual Basic 或 C++/CX 的 Windows 运行时支持对大多数 UI 元素上存在的一组事件的路由事件的概念。 这些事件适用于输入和用户交互应用场景,并且它们是在 UIElement 基类上实现的。 下面是路由事件的输入事件列表:

路由事件是一个可能从子对象传递(路由)到对象树中的每个连续父对象的事件。 UI 的 XAML 结构近似于此树,其中该树的根是 XAML 中的根元素。 真正的对象树可能与 XAML 元素嵌套稍有不同,因为对象树不包含 XAML 语言功能,例如属性元素标记。 你可以将路由事件视为从引发事件的任何 XAML 对象元素子元素向包含事件的父对象元素浮升。 事件及其事件数据可以在事件路由中的多个对象上进行处理。 如果没有元素具有处理程序,则路由可能会在到达根元素后才停止。

如果你知道动态 HTML (DHTML) 或 HTML5 等网页技术,那么你可能已经熟悉浮升事件概念。

当路由事件通过其事件路由浮升时,任何附加事件处理程序都会访问事件数据的共享实例。 因此,如果任何事件数据可由处理程序进行写入,则对事件数据所做的任何更改都将传递到下一个处理程序,并且可能不再表示事件的原始事件数据。 当事件具有路由事件行为时,参考文档将包含有关路由行为的注解或其他表示法。

RoutedEventArgs的 OriginalSource 属性

当事件浮升至事件路由时,“发送方”不再是与事件引发对象相同的对象。 相反,“发送方”是正在调用的处理程序附加的对象。

在某些情况下,“发送方”并不令人感兴趣,而是你感兴趣的是信息,例如当指针事件触发时指针可能位于哪个子对象上,或者当用户按下键盘键时,较大 UI 中的对象将焦点放在哪个对象上。 对于这些情况,你可以使用 OriginalSource 属性的值。 在路由的所有点上,OriginalSource 会报告触发事件的起始对象,而不是附加处理程序的对象。 不过,对于 UIElement 输入事件,起始对象通常是页面级 UI 定义 XAML 中不立即可见的对象。 相反,起始源对象可能是控件的模板部件。

Handled 属性

Handled 属性设置为 true 会影响事件系统行为。 当 Handled 为 *true*,大多数事件处理程序的路由将会停止;该事件不会沿路由继续,以通知该特定事件情况的其他附加处理程序。 “Handled” 在事件上下文中的含义,以及你的应用如何进行响应取决于你。 基本上,Handled 是一个简单的协议,使应用代码能够声明某个事件的发生不需要浮升到任何容器,而你的应用逻辑已处理需要执行的操作。 与之相反,你确实必须小心,不要处理可能应浮升的事件,以便内置系统或控制行为可以采取行动。例如,处理选择控件的部件或项内的低级别事件可能会有害。 选择控件可能正在寻找输入事件,以了解所选内容应发生更改。


XAML 中的大小写和空格

通常,XAML 区分大小写。

WPF XAML 处理器和序列化程序将忽略或删除所有不重要的空格,并将规范化任何重要的空白。 这与 XAML 规范的默认空白行为建议一致。 只有在 XAML 内容属性中指定字符串时,此行为才会产生后果。 在最简单的术语中,XAML 会将空格、换行符和制表符转换为一个空格,并在连续字符串的任一端保留一个空格。 本文未介绍 XAML 空白处理的完整说明。 有关详细信息,请参阅 XAML 中的空白处理

标记扩展

标记扩展是 XAML 语言概念。 用于提供属性语法的值时,大括号 ({}) 指示标记扩展用法。 此用法指示 XAML 处理,以不同于一般将属性值作为文本字符串或字符串可转换值处理的方式来处理它。

<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>

初始化文本

对于某些对象,可以使用用作构造的初始化值的内部文本来声明新值。 在 XAML 中,此技术和语法称为 初始化文本。 从概念上讲,初始化文本类似于调用具有参数的构造函数。 初始化文本可用于设置某些结构的初始值。

如果需要具有 x:Key 的结构值,则通常使用对象元素语法和初始化文本,以便它可以存在于 ResourceDictionary。 如果在多个目标属性之间共享该结构值,则可以执行此操作。 对于某些结构,不能使用属性语法设置结构的值:初始化文本是生成有用且可共享的 CornerRadius、Thickness*、**GridLength**Color* 资源的唯一方法。

<UserControl ...>
  <UserControl.Resources>
    <Thickness x:Key="TwentyTenThickness">20,10</Thickness>
    ....
  </UserControl.Resources>
  ...
  <Grid Margin="{StaticResource TwentyTenThickness}">
  ...
  </Grid>
</UserControl ...>

此缩写示例使用初始化文本来指定粗细的值,在本例中指定将 Left *和 Right **设置为** 20 的值*,以及*顶部*底部都设置为 10 的值。 本示例显示创建为键化资源的 Thickness,然后显示对该资源的引用。

XAML 中的接口

在极少数情况下,你将看到一个 XAML 语法,其中属性的类型是接口。 在 XAML 类型系统中,实现该接口的类型在分析时可作为值接受。 必须有一个可用于用作值的此类类型的创建实例。 你将看到一个接口,该接口用作 ButtonBase 的 Command * CommandParameter* 属性的 XAML 语法中的类型。 这些属性支持 Model-View-ViewModel (MVVM) 设计模式,其中 ICommand 接口是视图和模型交互方式的协定。

Windows 运行时引用中的 XAML 占位符约定

如果已检查了有关可以使用 XAML 的 Windows 运行时 API 的参考主题的任何语法部分,则可能已看到语法包含相当多的占位符。 XAML 语法不同于 C#、Microsoft Visual Basic 或 Visual C++ 组件扩展(C++/CX)语法,因为 XAML 语法是用法语法。 它提示你最终在自己的 XAML 文件中的用法,但不对可以使用的值进行过度规范化。 因此,用法通常描述混合文本和占位符的语法类型,并在 XAML 值部分定义一些占位符

类型转换器

根元素和命名空间

XAML 命名空间

在一般编程中,命名空间是一个组织概念,用于确定如何解释编程实体的标识符。 通过使用命名空间,编程框架可以将用户声明的标识符与框架声明的标识符分开,通过命名空间限定来消除标识符的歧义,强制实施范围名称规则等。

XAML 文件必须只有一个根元素,才能同时是格式正确的 XML 文件和有效的 XAML 文件。

以下示例显示了 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>

根元素还包含属性 xmlnsxmlns:x。 这些属性向 XAML 处理器指示哪些 XAML 命名空间包含标记将作为元素引用的后备类型的类型定义。 该 xmlns 属性专门指示默认 XAML 命名空间。 在默认 XAML 命名空间中,可以指定标记中的对象元素,而无需前缀。

x: 前缀

在前面的根元素示例中,前缀 x: 用于映射 XAML 命名空间,该命名空间 http://schemas.microsoft.com/winfx/2006/xaml是支持 XAML 语言构造的专用 XAML 命名空间。 此 x: 前缀用于在整个 SDK 的项目模板、示例和文档中映射此 XAML 命名空间。 XAML 语言的 XAML 命名空间包含多个在 XAML 中经常使用的编程构造。 下面是将使用的最常见 x: 前缀编程构造的列表:

  • x:Key:为ResourceDictionary中的每个资源设置唯一键(或其他框架中的类似字典概念)。 x:Key 可能会构成你在典型的 WPF 应用标记中看到的 x: 使用情况的 90%。
  • x:Class:指定用于 XAML 页后台代码的类的 CLR 命名空间和类名。 必须要有这样的类以支持根据 WPF 编程模型的后置代码,因此几乎总能看到 x: 被映射,即使没有资源,情况也是如此。
  • x:Name:为处理对象元素后在运行时代码中存在的实例指定运行时对象名称。 一般情况下,通常会对 x:Name 使用 WPF 定义的等效属性。 此类属性专门映射到 CLR 支持属性,因此更方便应用编程,你经常使用运行时代码从初始化的 XAML 中查找命名元素。 最常见的此类属性是 FrameworkElement.Name。 当特定类型不支持等效的 WPF 框架级属性时,仍可能使用 Name。 这在某些动画场景中发生。
  • x:Static:启用一种引用,该引用返回一个不是 XAML 兼容属性的静态值。
  • x:Type:基于类型名称构造 Type 引用。 这用于指定采用 Type的属性,例如 Style.TargetType,尽管属性通常具有本机字符串到Type 转换,因此 x:Type 标记扩展用法是可选的。

前缀/XAML 命名空间中 x: 还有其他编程构造,这些构造并不常见。 有关详细信息,请参阅 XAML 命名空间 (x:) 语言功能

自定义前缀和自定义类型

对于你自己的自定义程序集,或者对于 PresentationCorePresentationFrameworkWindowsBase 的 WPF 核心之外的程序集,可以将程序集指定为自定义 xmlns 映射的一部分。 然后,可以在 XAML 中引用该程序集中的类型,只要正确实现该类型以支持尝试的 XAML 用法。

事件和 XAML 代码后置

在项目中,XAML 将编写为 .xaml 文件,CLR 语言(如 Microsoft Visual Basic 或 C#)用于编写代码隐藏文件。 当 XAML 文件作为 WPF 编程和应用程序模型的一部分进行标记编译时,通过在 XAML 根元素中指定一个命名空间和类作为 x:Class 属性,来标识 XAML 文件中代码隐藏的文件的位置。

using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;

namespace ExampleNamespace;

public partial class ExamplePage : Page
{
    public ExamplePage() =>
        InitializeComponent();

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

请注意,代码隐藏文件使用 CLR 命名空间 ExampleNamespace (命名空间在 Visual Basic 中不可见),并声明 ExamplePage 为该命名空间中的分部类。 这与x:ClassExampleNamespace属性值并行。 ExamplePage 已在标记根目录中提供。 WPF 标记编译器将通过从根元素类型派生类,为任何已编译的 XAML 文件创建分部类。 当您提供也定义同一分部类的后台代码时,生成的代码会合并到已编译应用程序的相同命名空间和类中。

路由事件

WPF 的基础特定事件功能是路由事件。 路由事件使元素能够处理由不同元素引发的事件,只要元素通过树关系进行连接。 使用 XAML 属性指定事件处理时,可以在任何元素上侦听和处理路由事件,包括不列出类成员表中特定事件的元素。 这是通过限定具有拥有类名的事件名称属性来实现的。 例如,在正在进行的StackPanelStackPanel / 示例中,父元素Button可以通过在对象元素Click上指定属性Button.Click,将处理程序名称作为该属性值,以便为子元素按钮的StackPanel事件注册处理程序。 有关详细信息,请参阅 路由事件概述

已命名元素

默认情况下,通过处理 XAML 对象元素在对象图中创建的对象实例没有唯一标识符或对象引用。 相比之下,如果在代码中调用构造函数,则几乎总是使用构造函数结果将变量设置为构造的实例,以便稍后可以在代码中引用该实例。 为了提供对通过标记定义创建的对象的标准化访问,XAML 定义 x:Name 属性。 可以在任何对象元素上设置特性的值 x:Name 。 在后台代码中,您选择的标识符相当于引用构造实例的实例变量。 在所有方面,命名元素的功能如同对象实例(名称引用该实例),后台代码可以引用命名元素用于在应用中处理运行时交互。 实例和变量之间的这种连接是由 WPF XAML 标记编译器完成的,更具体地说,涉及的功能和模式,如 InitializeComponent 本文中不会详细讨论这些特性和模式。

以下示例将Name设置在StackPanel元素上。 然后, Button内部的StackPanel处理程序根据StackPanel的设置通过其实例buttonContainer引用Name

<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 元素/对象实例上设置的全局成员。 但是,该元素/类或更大的基础结构必须支持绑定值的支持属性存储。

基类型

安全

WPF 中的代码访问安全性 (CAS)

从代码加载 XAML


教程 创建天气UI界面

定义网格

在 XAML 中, 网格 由一系列行和列组成。 通过在 Grid 中指定元素的行和列,可以在用户界面中放置和设置其他元素的空间。 使用 RowDefinitionColumnDefinition 元素定义行和列。

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition Width="3*"/>
        <ColumnDefinition Width="5*"/>
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
        <RowDefinition Height="2*"/>
        <RowDefinition Height="*"/>
    </Grid.RowDefinitions>
</Grid>

网格 将创建一组两行和两列,用于定义应用界面的布局。 第一列的宽度为“3”,而第二列为“5”,以3:5的比例分配两列之间的水平空间。 同样,这两行的 高度 分别为“2”和“”,因此 网格 为第一行分配的空间是第二行的两倍(其中“”等同于“1”)。 即使调整窗口大小或更改设备,这些比率也会保持。

[采用 XAML 的响应式布局 - Windows apps | Microsoft Learn](https://learn.microsoft.com/zh-cn/windows/apps/design/layout/layouts-with-xaml)

为网格着色

若要为 网格 添加颜色,请添加三个 边框 元素,每个元素具有不同的背景色。 每个元素还通过使用 Grid.RowGrid.Column 参数定位在父 网格 的行和列中。 这些属性的值默认为 0,因此无需将它们分配给第一个 边框。 在行和列定义之后,将以下代码添加到 Grid 元素。

使用 StackPanel 元素组织内容

StackPanel 是用于创建天气应用的第二个 UI 元素。 StackPanel 是许多基本应用布局的基本部分,允许垂直或水平堆叠元素。

在以下代码中,我们将创建两个 StackPanel 元素,并用三个 TextBlock 填充每个元素。 将这些 StackPanel 元素添加到步骤 3 中 边框 元素下方的 网格。 这会导致 TextBlock 元素呈现在前面创建的彩色 网格 之上。

添加图像图标

最后,让我们用表示今天天气的图像填充 网格 中的空部分,即“部分多云”。

<Image Margin="20" Source="Assets/partially-cloudy.png"/>

相关文章


上一篇 UWP

下一篇 正则表达式

Comments

Content