文章目录
-
- [一、XAML 简介](#一、XAML 简介)
-
- [1.1 什么是 XAML?](#1.1 什么是 XAML?)
- [1.2 XAML 的特点](#1.2 XAML 的特点)
- [1.3 一个最简单的 XAML 示例](#1.3 一个最简单的 XAML 示例)
- [二、XAML 语法基础](#二、XAML 语法基础)
-
- [2.1 对象元素](#2.1 对象元素)
- [2.2 属性设置](#2.2 属性设置)
-
- [2.2.1 特性语法(Attribute Syntax)](#2.2.1 特性语法(Attribute Syntax))
- [2.2.2 属性元素语法(Property Element Syntax)](#2.2.2 属性元素语法(Property Element Syntax))
- [2.2.3 内容属性(Content Property)](#2.2.3 内容属性(Content Property))
- [2.2.4 集合语法(Collection Syntax)](#2.2.4 集合语法(Collection Syntax))
- [2.3 事件处理](#2.3 事件处理)
- [2.4 标记扩展(Markup Extensions)](#2.4 标记扩展(Markup Extensions))
- [2.5 类型转换器(Type Converters)](#2.5 类型转换器(Type Converters))
- [三、XAML 命名空间与类型映射](#三、XAML 命名空间与类型映射)
-
- [3.1 默认 XAML 命名空间](#3.1 默认 XAML 命名空间)
- [3.2 XAML 语言命名空间(x:)](#3.2 XAML 语言命名空间(x:))
- [3.3 自定义前缀与外部程序集](#3.3 自定义前缀与外部程序集)
- [四、XAML 高级概念](#四、XAML 高级概念)
-
- [4.1 附加属性(Attached Properties)](#4.1 附加属性(Attached Properties))
- [4.2 附加事件(Attached Events)](#4.2 附加事件(Attached Events))
- [4.3 命名元素(x:Name)](#4.3 命名元素(x:Name))
- [4.4 基类与 XAML](#4.4 基类与 XAML)
- [五、XAML 与代码隐藏](#五、XAML 与代码隐藏)
-
- [5.1 分部类与 x:Class](#5.1 分部类与 x:Class)
- [5.2 事件处理程序](#5.2 事件处理程序)
- [六、XAML 安全性(简要)](#六、XAML 安全性(简要))
-
- [动态加载 XAML](#动态加载 XAML)
一、XAML 简介
1.1 什么是 XAML?
- XAML (eXtensible Application Markup Language)是一种声明式标记语言,专门用于构建 .NET 应用程序的用户界面(UI)。
- 它基于 XML 语法,但与 XML 的本质区别在于:XAML 直接映射到 .NET 对象。每个 XAML 元素对应一个 .NET 类的实例,属性对应类的属性或事件。
- 通过 XAML,开发者可以将 UI 结构与业务逻辑分离:UI 用 XAML 定义,逻辑用 C#、VB 等代码隐藏文件实现。
1.2 XAML 的特点
- 声明式:描述 UI 的外观和组织结构,而非创建过程。
- 可读性强:结构清晰,易于理解和维护。
- 工具友好:支持可视化设计器(如 Visual Studio、Blend)。
- 与代码无缝集成 :通过
x:Class将 XAML 与后台代码关联。
1.3 一个最简单的 XAML 示例
xml
<Window x:Class="MyApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="我的应用" Height="200" Width="300">
<StackPanel>
<Button Content="单击我" Width="100" Height="30"/>
</StackPanel>
</Window>
这个例子创建了一个窗口,包含一个按钮。Window 和 Button 都是 WPF 中的类。
二、XAML 语法基础
2.1 对象元素
- 对象元素 声明一个类型的实例,使用
<类型名>标签。 - 标签可以成对出现(
<StackPanel>...</StackPanel>),也可以是自结束形式(<Button />)。
xml
<!-- 对象元素示例 -->
<StackPanel>
<Button Content="按钮1" />
<Button Content="按钮2" />
</StackPanel>
当 XAML 被解析时,会调用类型的默认构造函数来创建实例。
2.2 属性设置
2.2.1 特性语法(Attribute Syntax)
- 最简单的属性设置方式,直接写在开始标签内。
- 属性值用双引号括起,通常为字符串,通过类型转换器转换为目标类型。
xml
<Button Background="Blue" Foreground="White" Content="确定" Width="80"/>
2.2.2 属性元素语法(Property Element Syntax)
- 当属性值是一个复杂对象时,无法用简单字符串表示,就需要使用属性元素。
- 格式:
<类型名.属性名>,内部包含用于设置该属性的对象元素。
xml
<Button>
<Button.Background>
<SolidColorBrush Color="Blue"/>
</Button.Background>
<Button.Foreground>
<SolidColorBrush Color="White"/>
</Button.Foreground>
<Button.Content>
确定
</Button.Content>
</Button>
2.2.3 内容属性(Content Property)
- 每个类可以指定一个属性作为内容属性 (使用
[ContentProperty]特性标记)。 - 设置内容属性时,可以直接将子元素写在标签内部,省略属性元素。
例如 Button 的内容属性是 Content,因此下面两种写法等价:
xml
<!-- 隐式使用内容属性 -->
<Button>确定</Button>
<!-- 显式使用内容属性 -->
<Button>
<Button.Content>
确定
</Button.Content>
</Button>
对于 StackPanel,它的内容属性是 Children,所以可以直接添加子元素:
xml
<StackPanel>
<Button>按钮1</Button>
<Button>按钮2</Button>
</StackPanel>
2.2.4 集合语法(Collection Syntax)
- 如果属性是一个集合类型(如
IList或ICollection),可以直接在属性元素内部添加多个子元素,而无需显式写出集合对象。 - 例如
StackPanel.Children是UIElementCollection类型,但我们可以直接添加子元素:
xml
<StackPanel>
<StackPanel.Children>
<Button>按钮1</Button>
<Button>按钮2</Button>
</StackPanel.Children>
</StackPanel>
实际中,由于内容属性常与集合语法结合,我们通常只写:
xml
<StackPanel>
<Button>按钮1</Button>
<Button>按钮2</Button>
</StackPanel>
2.3 事件处理
- 事件也可以通过特性语法关联处理程序,格式为
事件名="处理程序方法名"。 - 处理程序需在代码隐藏文件中实现。
xml
<Button Click="Button_Click" Content="单击我"/>
csharp
// 代码隐藏文件 (MainWindow.xaml.cs)
private void Button_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show("按钮被单击了!");
}
2.4 标记扩展(Markup Extensions)
- 当属性值需要从其他资源、数据绑定或静态属性获取时,使用标记扩展。
- 语法:
{扩展名 参数}。例如{Binding}、{StaticResource}、{x:Static}。
xml
<Button Content="{Binding UserName}" />
<Button Background="{StaticResource MyBrush}" />
<TextBlock Text="{x:Static SystemFonts.IconFontFamily}" />
2.5 类型转换器(Type Converters)
- 很多属性接受复杂类型,但 XAML 允许用字符串表示,通过类型转换器自动转换。
- 例如
Brush类型可以通过颜色名称字符串转换:
xml
<Button Background="Red" />
等价于:
xml
<Button>
<Button.Background>
<SolidColorBrush Color="Red"/>
</Button.Background>
</Button>
再如 Thickness 类型:
xml
<Button Margin="5,10,5,10" />
等价于:
xml
<Button>
<Button.Margin>
<Thickness Left="5" Top="10" Right="5" Bottom="10"/>
</Button.Margin>
</Button>
三、XAML 命名空间与类型映射
3.1 默认 XAML 命名空间
- 每个 XAML 文件的根元素必须声明 XML 命名空间,以将 XAML 元素映射到实际的 .NET 类型。
- WPF 的默认命名空间:
http://schemas.microsoft.com/winfx/2006/xaml/presentation
它映射到 WPF 的核心程序集(PresentationFramework、PresentationCore、WindowsBase)。 - 没有前缀的元素都来自此默认命名空间。
3.2 XAML 语言命名空间(x:)
- 第二个常用的命名空间:
http://schemas.microsoft.com/winfx/2006/xaml
通常使用前缀x:。 - 它提供 XAML 语言级别的指令,如:
x:Class:指定代码隐藏的分部类名。x:Name:为元素命名,以便在后台代码中引用。x:Key:为资源字典中的资源定义唯一键。x:Static:引用静态成员。x:Type:引用Type对象。
示例:
xml
<Window x:Class="MyApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="示例" Height="200" Width="300">
<Grid>
<Button x:Name="myButton" Content="单击" Click="Button_Click"/>
</Grid>
</Window>
3.3 自定义前缀与外部程序集
- 如需使用自定义程序集中的类型,可在根元素中定义新的 xmlns 前缀,映射到 CLR 命名空间和程序集。
- 语法:
xmlns:前缀="clr-namespace:命名空间;assembly=程序集名称"
xml
<Window x:Class="MyApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:MyApp.CustomControls"
Title="自定义控件示例" Height="200" Width="300">
<StackPanel>
<local:MyNumericUpDown Value="10"/>
</StackPanel>
</Window>
四、XAML 高级概念
4.1 附加属性(Attached Properties)
- 允许一个元素设置不属于自己定义的属性,通常用于布局或交互场景。
- 语法:
所有者类型.属性名="值" - 例如,
DockPanel.Dock是一个附加属性,由DockPanel类定义,但可以设置在Button上。
xml
<DockPanel>
<Button DockPanel.Dock="Left" Content="左侧按钮"/>
<Button DockPanel.Dock="Right" Content="右侧按钮"/>
<Button Content="中间按钮"/>
</DockPanel>
工作原理 :DockPanel 在布局时会检查每个子元素是否有 DockPanel.Dock 附加属性值,然后决定排列方式。
4.2 附加事件(Attached Events)
- 类似附加属性,允许子元素为父元素定义的事件添加处理程序。
- 语法:
所有者类型.事件名="处理程序方法名" - 例如,在
Grid上处理其内部所有按钮的Click事件:
xml
<Grid Button.Click="Grid_Click">
<Button Content="按钮1"/>
<Button Content="按钮2"/>
</Grid>
csharp
private void Grid_Click(object sender, RoutedEventArgs e)
{
// 注意:sender 是 Grid,但 e.OriginalSource 是实际被单击的按钮
MessageBox.Show($"单击了 {(e.OriginalSource as Button).Content}");
}
4.3 命名元素(x:Name)
- 使用
x:Name为元素分配一个唯一标识符,以便在后台代码中直接访问该对象。 - 许多 WPF 元素也提供了同名的
Name属性(如FrameworkElement.Name),两者等价。
xml
<TextBox x:Name="txtInput" Width="200"/>
<Button Click="Button_Click" Content="显示文本"/>
csharp
private void Button_Click(object sender, RoutedEventArgs e)
{
MessageBox.Show(txtInput.Text); // 直接访问 txtInput
}
4.4 基类与 XAML
- 虽然 XAML 直接实例化具体类型,但所有 WPF 控件都继承自
FrameworkElement(或FrameworkContentElement),从而获得大量通用属性(如Width、Height、Margin、Name等)。 - 理解继承关系有助于掌握哪些属性可以在哪些元素上使用。
五、XAML 与代码隐藏
5.1 分部类与 x:Class
- XAML 文件通过
x:Class指令指定代码隐藏的类名。 - 编译器会将 XAML 生成的代码和开发者编写的代码合并为一个分部类。
xml
<Window x:Class="MyApp.MainWindow" ...>
...
</Window>
在后台代码文件中:
csharp
namespace MyApp
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent(); // 由 XAML 编译器生成的方法,用于加载 UI
}
}
}
5.2 事件处理程序
- 在 XAML 中声明事件处理程序时,方法必须存在于分部类的另一部分中。
- 处理程序必须匹配委托签名(通常为
object sender, RoutedEventArgs e)。
xml
<Button Click="OnButtonClick" Content="确定"/>
csharp
private void OnButtonClick(object sender, RoutedEventArgs e)
{
// 处理逻辑
}
六、XAML 安全性(简要)
- 在部分信任环境(如 XBAP、Internet 区域)中运行的 XAML 受到 .NET 安全策略限制,不能执行某些危险操作(如文件 I/O、网络访问等)。
- 完全信任的应用程序可以加载和执行任何 XAML。
动态加载 XAML
- 使用
XamlReader.Load方法可以从字符串或文件动态加载 XAML 并创建对象树。 - 通常用于插件、主题切换等场景。
csharp
string xaml = "<Button xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation' Content='动态按钮'/>";
var button = (Button)XamlReader.Parse(xaml);
myStackPanel.Children.Add(button);