# 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定義更加靈活和動態,同時保持了聲明式編程的簡潔性。