前言

在 WPF 开发中,我们通常可以通过 Visual Studio 的快捷键(Ctrl+K, Ctrl+D)快速格式化 XAML 代码。然而,在 Avalonia 项目开发中,我发现 VS 并没有提供类似的内置格式化功能,这给开发者带来了不少困扰。

经过探索,我找到了一种通过命令行工具 XamlStyler 来实现 Avalonia XAML 格式化的解决方案。本文将详细介绍配置过程和使用方法,欢迎大家留言分享更好的方式。

问题背景

遇到的问题

  1. VS 无格式化支持:Visual Studio 对 Avalonia XAML 文件没有内置的格式化功能
  2. 代码风格不统一:团队协作时,XAML 文件的缩进、换行、属性排序难以统一
  3. 可读性差:未格式化的 XAML 代码在属性较多时难以阅读和维护

期望的格式化效果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!-- 格式化前 -->
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="AvaloniaApp.MainWindow"
Title="Hello Avalonia"
Width="800" Height="600" Background="White">
<StackPanel>
<TextBlock Text="Welcome"/>
<Button Content="Click Me" Command="{Binding ClickCommand}"/>
</StackPanel>
</Window>

<!-- 格式化后 -->
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="AvaloniaApp.MainWindow"
Title="Hello Avalonia"
Width="800"
Height="600"
Background="White">
<StackPanel>
<TextBlock Text="Welcome" />
<Button Content="Click Me"
Command="{Binding ClickCommand}" />
</StackPanel>
</Window>

解决方案

工具选择

XamlStyler 是一个强大的 XAML 格式化工具,虽然最初为 WPF 设计,但对 Avalonia XAML 同样适用。我们使用其命令行版本 XamlStyler.Console 来实现批量格式化。

安装步骤

1. 安装 .NET 工具

首先确保已安装 .NET SDK,然后执行以下命令安装 XamlStyler:

1
dotnet tool install -g XamlStyler.Console --version 3.2501.8

如果需要更新版本:

1
dotnet tool update -g XamlStyler.Console --version 3.2501.8

2. 验证安装

安装完成后,可以通过以下命令查看帮助:

1
xstyler --help

命令行参数说明

参数 说明
-f, --file XAML文件路径(支持逗号分隔列表)
-d, --directory 要处理的目录
-c, --config JSON配置文件路径
-i, --ignore 忽略XAML文件类型检查,处理所有文件
-r, --recursive 递归处理子目录
-p, --passive 检查格式是否正确,不修改文件(CI/CD用)
--write-to-stdout 输出到stdout而非写入文件
-l, --loglevel 日志级别:None/Minimal/Default/Verbose/Debug
--indent-size 覆盖配置:缩进大小
--indent-tabs 覆盖配置:使用Tab缩进
--attributes-tolerance 覆盖配置:属性换行阈值
--attributes-max 覆盖配置:每行最大属性数
--attributes-reorder 覆盖配置:启用属性重排序
--format-markup-extension 覆盖配置:格式化标记扩展
--remove-empty-ending-tag 覆盖配置:移除空元素结束标签
--space-before-closing-slash 覆盖配置:自闭合标签斜杠前加空格

常用命令示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 基本用法:格式化当前目录及子目录所有XAML文件
xstyler -d . -r -c XamlStyler.json5

# 检查格式但不修改(适合CI/CD流程)
xstyler -d . -r -c XamlStyler.json5 -p

# 静默模式运行,减少输出
xstyler -d . -r -c XamlStyler.json5 -s

# 输出到stdout查看格式化效果(不写入文件)
xstyler -f Views/MainView.axaml --write-to-stdout

# 指定详细日志级别
xstyler -d . -r -c XamlStyler.json5 -l Verbose

# 命令行覆盖配置(无需配置文件)
xstyler -d . -r --indent-size 2 --attributes-tolerance 2

配置文件

在项目根目录创建 XamlStyler.json5 配置文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
{
// 缩进与格式化
IndentSize: 4,
IndentWithTabs: false,
AttributeIndentation: 0,
AttributeIndentationStyle: 1,
SpaceBeforeClosingSlash: true,
SpaceBeforeColon: false,
SpaceAfterColon: false,
SpaceBeforeSemicolon: false,
SpaceAfterSemicolon: false,

// 属性换行规则
AttributesTolerance: 3,
MaxAttributesPerLine: 3,
MaxAttributeCharactersPerLine: 80,
KeepFirstAttributeOnSameLine: true,
PutEndingBracketOnNewLine: false,
RemoveEndingTagOfEmptyElement: true,

// 属性分组与排序
EnableAttributeReordering: true,
OrderAttributesByName: false,
SeparateByGroups: true,
FirstLineAttributes: "x:Name,Name,x:Key,Key,Grid.Row,Grid.Column",

// 属性排序规则(WPF/Avalonia推荐顺序)
AttributeOrderingRuleGroups: [
"x:Class",
"x:Name, Name, x:Key, Key, x:Uid, Uid",
"Title",

// 命名空间 (Avalonia/WPF 引用)
"xmlns, xmlns:x",
"xmlns:*",

// 数据上下文与绑定 (Avalonia 强类型绑定很重要)
"x:DataType, DataType, DataContext, d:DataContext",

// 布局位置 (Grid/Canvas/Dock)
"Grid.Row, Grid.RowSpan, Grid.Column, Grid.ColumnSpan",
"Canvas.Left, Canvas.Top, Canvas.Right, Canvas.Bottom",
"DockPanel.Dock",

// 尺寸与外边距
"Width, Height, MinWidth, MinHeight, MaxWidth, MaxHeight",
"Margin, Padding",
"HorizontalAlignment, VerticalAlignment, HorizontalContentAlignment, VerticalContentAlignment",

// 视觉外观 (背景、图标、不透明度)
"Background, Foreground, BorderBrush, BorderThickness",
"Icon, WindowStartupLocation, WindowState",
"Opacity, Visibility",

// 其他所有属性
"*:*, *",

// 交互与事件
"Command, CommandParameter",
"Click, Tapped, KeyDown, *",

// 设计时属性 (Design-Time)
"mc:Ignorable",
"d:DesignWidth, d:DesignHeight",
"d:*",
],

// 特殊元素处理
NewlineExemptionElements: "Setter, Trigger, Condition, GradientStop",
ReorderSetters: 3,
ReorderVSM: 2,
ReorderGridChildren: false,
ReorderCanvasChildren: false,

// 标记扩展格式化
FormatMarkupExtension: true,
NoNewLineMarkupExtensions: "x:Bind, Binding, StaticResource",

// Thickness类型处理
ThicknessSeparator: 2,
ThicknessAttributes: "Margin, Padding, BorderThickness",

// 其他
RemoveDesignTimeReferences: false,
IgnoreDesignTimeReferencePrefix: false,
CommentPadding: 2,
FormatOnSave: true,
}

使用方法

方式一:命令行直接运行

在项目根目录执行:

1
xstyler -d . -r -c XamlStyler.json5

方式二:创建批处理脚本

为了简化操作,可以创建 format-xaml.bat 文件:

1
2
3
4
5
@echo off
echo Starting XAML formatting...
xstyler -d . -r -c XamlStyler.json5
echo Formatting complete!
pause

双击运行即可完成整个项目的 XAML 格式化。

效果展示

格式化前

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MyApp.Views.MainView"
x:DataType="vm:MainViewModel"
Width="800" Height="600" Background="LightGray"
xmlns:vm="clr-namespace:MyApp.ViewModels">
<Grid RowDefinitions="Auto,*,Auto" Margin="10">
<TextBlock Grid.Row="0" Text="Title" FontSize="24"/>
<ListBox Grid.Row="1" ItemsSource="{Binding Items}" Margin="0,10">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Right">
<Button Content="Cancel" Margin="0,0,10,0"/>
<Button Content="OK" Command="{Binding ConfirmCommand}"/>
</StackPanel>
</Grid>
</UserControl>

格式化后

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="MyApp.Views.MainView"
x:DataType="vm:MainViewModel"
xmlns:vm="clr-namespace:MyApp.ViewModels"
Width="800"
Height="600"
Background="LightGray">
<Grid Margin="10"
RowDefinitions="Auto,*,Auto">
<TextBlock Grid.Row="0"
FontSize="24"
Text="Title" />
<ListBox Grid.Row="1"
Margin="0,10"
ItemsSource="{Binding Items}">
<ListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel Grid.Row="2"
HorizontalAlignment="Right"
Orientation="Horizontal">
<Button Content="Cancel"
Margin="0,0,10,0" />
<Button Command="{Binding ConfirmCommand}"
Content="OK" />
</StackPanel>
</Grid>
</UserControl>

配置详解

缩进设置

配置项 说明 推荐值
IndentSize 缩进大小 4
IndentWithTabs 使用 Tab 缩进 false
AttributeIndentationStyle 属性缩进样式(1=空格) 1

属性换行规则

配置项 说明 推荐值
AttributesTolerance 超过此数量则换行 3
MaxAttributesPerLine 每行最大属性数 3
KeepFirstAttributeOnSameLine 首个属性保持同行 true
RemoveEndingTagOfEmptyElement 空元素自动闭合 true

属性排序策略

配置中的 AttributeOrderingRuleGroups 是核心配置项,它定义了属性的推荐排序顺序:

  1. x:Class - 类定义
  2. 命名空间 - xmlns 引用
  3. x:Name/x:Key - 命名元素
  4. 数据绑定 - DataContext, x:DataType
  5. 布局位置 - Grid.Row, Grid.Column
  6. 尺寸外边距 - Width, Height, Margin
  7. 外观属性 - Background, Foreground
  8. 其他属性 - 通用属性
  9. 交互事件 - Command, Click
  10. 设计时属性 - d:* (放最后)

标记扩展处理

1
2
"FormatMarkupExtension": true,
"NoNewLineMarkupExtensions": "x:Bind, Binding, StaticResource"

常用绑定扩展保持在一行,避免过度换行影响可读性。

集成到构建流程

作为构建前脚本

在项目的 .csproj 文件中添加:

1
2
3
<Target Name="FormatXaml" BeforeTargets="Build">
<Exec Command="xstyler -d $(ProjectDir) -r -c XamlStyler.json5" />
</Target>

CI/CD 集成

在 GitHub Actions 中添加格式化检查:

1
2
3
4
5
- name: Format XAML
run: |
dotnet tool install -g XamlStyler.Console --version 3.2501.8
xstyler -d . -r -c XamlStyler.json5
git diff --exit-code

注意事项

1. 文件编码

确保 XAML 文件使用 UTF-8 编码,避免格式化后出现乱码。

2. 备份文件

首次批量格式化前,建议备份或使用版本控制,便于对比差异。

3. 属性排序影响

开启属性排序后,文件的 Git diff 可能会显示较大变化,建议在项目初期就采用此配置。

4. 与设计器兼容

部分设计时属性(如 d:DesignWidth)格式化后可能影响设计器显示,建议在设计完成后手动调整或使用 d:* 排除规则。

5. Avalonia 特有属性

Avalonia 的一些特有属性(如 classes, styles)可能需要根据实际情况调整配置。

总结

通过 XamlStyler 工具,我们成功为 Avalonia 项目实现了 XAML 格式化能力,主要收获包括:

  1. 统一代码风格:团队成员的 XAML 文件格式保持一致
  2. 提升可读性:智能的属性排序使代码更易阅读
  3. 提高效率:批量格式化功能节省大量手动调整时间
  4. 构建集成:可集成到 CI/CD 流程中确保代码质量

虽然这是目前发现的解决方案,但使用上仍有一些不便捷之处。如果你有更好的 Avalonia XAML 格式化方案,欢迎留言分享!


提示:如果格式化后出现预期外的效果,可以调整 AttributeOrderingRuleGroups 中的规则顺序,或适当放宽 AttributesTolerance 的阈值。