# XAML 标记扩展详解
标记扩展(Markup Extensions)是XAML中一种特殊的语法结构,允许在XAML属性中嵌入动态值或引用,而不是简单的静态值。它们使用花括号`{}`作为标识,是XAML强大功能的核心组成部分。
## 基本语法结构
所有标记扩展都遵循以下基本格式:
```xml
{扩展类 参数1=值1 参数2=值2 ...}
```
或
```xml
{扩展类 值} <!-- 当只有一个主要参数时 -->
```
## 核心标记扩展类型
### 1. 资源引用扩展
#### `StaticResource`
- **作用**:引用已定义的资源(编译时确定)
- **示例**:
```xml
<Window.Resources>
<SolidColorBrush x:Key="MyBrush" Color="Red"/>
</Window.Resources>
<Button Background="{StaticResource MyBrush}"/>
```
- **特点**:
- 资源必须在引用之前定义
- 性能优于DynamicResource
#### `DynamicResource`
- **作用**:动态引用资源(运行时可以更改)
- **示例**:
```xml
<Button Background="{DynamicResource MyBrush}"/>
```
- **特点**:
- 允许运行时更改资源
- 适用于主题切换等场景
- 性能开销比StaticResource大
#### `ThemeResource` (UWP/WinUI特有)
- **作用**:根据当前主题自动选择资源
- **示例**:
```xml
<Button Background="{ThemeResource SystemControlBackgroundAccentBrush}"/>
```
### 2. 数据绑定扩展
#### `Binding`
- **作用**:建立数据绑定关系
- **基本语法**:
```xml
{Binding Path=PropertyName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}
```
- **完整示例**:
```xml
<TextBox Text="{Binding Path=UserName,
Mode=TwoWay,
UpdateSourceTrigger=PropertyChanged,
ValidatesOnDataErrors=True,
Converter={StaticResource MyConverter}}"/>
```
- **常用属性**:
- `Path`:绑定路径(可省略"Path="直接写属性名)
- `Mode`:`OneWay`, `TwoWay`, `OneTime`, `OneWayToSource`
- `UpdateSourceTrigger`:`PropertyChanged`, `LostFocus`, `Explicit`
- `Converter`:值转换器
- `ElementName`:绑定到其他元素
- `RelativeSource`:相对绑定源
#### `TemplateBinding`
- **作用**:在控件模板中绑定到模板化父级的属性
- **示例**:
```xml
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}">
<ContentPresenter/>
</Border>
</ControlTemplate>
```
### 3. 相对源扩展
#### `RelativeSource`
- **作用**:指定相对于当前元素的绑定源
- **模式**:
- `Self`:绑定到元素自身
```xml
<Button Content="{Binding RelativeSource={RelativeSource Self}, Path=Width}"/>
```
- `FindAncestor`:查找祖先元素
```xml
<TextBlock Text="{Binding RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Window}},
Path=Title}"/>
```
- `TemplatedParent`:绑定到应用模板的父元素
```xml
<TextBlock Text="{Binding RelativeSource={RelativeSource TemplatedParent},
Path=Content}"/>
```
- `PreviousData`:绑定到数据列表中的前一项
### 4. XAML语言级扩展
#### `x:Static`
- **作用**:引用静态属性、字段或枚举值
- **示例**:
```xml
<!-- 引用静态属性 -->
<Button Content="{x:Static local:MyClass.StaticProperty}"/>
<!-- 引用枚举值 -->
<Button Visibility="{x:Static Visibility.Collapsed}"/>
```
#### `x:Type`
- **作用**:获取类型的Type对象
- **示例**:
```xml
<Style TargetType="{x:Type Button}">
<Setter Property="FontSize" Value="14"/>
</Style>
```
#### `x:Array`
- **作用**:定义数组
- **示例**:
```xml
<x:Array Type="{x:Type sys:String}" xmlns:sys="clr-namespace:System;assembly=mscorlib">
<sys:String>Item 1</sys:String>
<sys:String>Item 2</sys:String>
</x:Array>
```
#### `x:Null`
- **作用**:显式设置为null
- **示例**:
```xml
<Button Background="{x:Null}"/>
```
### 5. 自定义标记扩展
您可以创建自己的标记扩展:
```csharp
public class RainbowExtension : MarkupExtension
{
public override object ProvideValue(IServiceProvider serviceProvider)
{
var brush = new LinearGradientBrush();
// 添加彩虹色渐变
return brush;
}
}
```
使用自定义扩展:
```xml
<Button Background="{local:Rainbow}"/>
```
## 标记扩展嵌套
标记扩展可以嵌套使用:
```xml
<Button Content="{Binding Source={StaticResource MyData},
Path=UserName,
Converter={StaticResource MyConverter}}"/>
```
## 转义花括号
如果需要显示字面量的花括号,可以使用双花括号转义:
```xml
<TextBlock Text="{}{This will display { and }}"/>
<!-- 或 -->
<TextBlock Text="{}{}{This will display { and }}"/>
```
## 标记扩展的工作原理
1. **解析阶段**:XAML解析器遇到`{...}`时,识别为标记扩展
2. **创建实例**:实例化对应的标记扩展类
3. **参数处理**:解析命名参数或位置参数
4. **值提供**:调用`ProvideValue`方法获取实际值
5. **应用值**:将返回值赋给目标属性
## 性能考虑
1. `StaticResource`比`DynamicResource`性能更好
2. 复杂的绑定表达式会增加解析开销
3. 避免在资源键中使用标记扩展(不允许)
4. 考虑在大量重复使用的场景中缓存标记扩展的结果
## 实际应用示例
### 动态主题切换
```xml
<Button Background="{DynamicResource ThemeBrush}"/>
```
### 多级数据绑定
```xml
<TextBlock Text="{Binding Path=Customer.Address.City}"/>
```
### 控件模板中的灵活绑定
```xml
<ControlTemplate TargetType="Button">
<Border Background="{TemplateBinding Background}">
<ContentPresenter Content="{TemplateBinding Content}"/>
</Border>
</ControlTemplate>
```
### 跨元素绑定
```xml
<Slider x:Name="fontSizeSlider" Minimum="8" Maximum="72"/>
<TextBlock FontSize="{Binding ElementName=fontSizeSlider, Path=Value}"/>
```
标记扩展极大地增强了XAML的表达能力,使得UI定义更加灵活和动态,同时保持了声明式编程的简洁性。