1. XAML 的問題
剛入門 WPF/UWP 之類的 XAML 平台,首先會接觸到 XAML 這一新事物。初學 XAML 時對它的印象可以歸納為一個詞:一坨。
隨着我在 XAML 平台上工作的時間越來越長,我對 XAML 的了解就越來越深入,從語法、約束、擴展性等方方面面,我明白到 XAML 是桌面開發平台的一個最佳解決方案。這時候我已經對 XAML 有了改觀,我重新用一個詞歸納了我對它的印象:一大坨。
沒錯,這時候我已經是一個成熟的 XAML 工人了,經過我熟練的雙手產生了一坨又一坨 XAML,它們成長相遇結合繁衍,變成了一大坨又一大坨 XAML。
明明 XAML 這么一大坨已經夠艱難了,偏偏對於它的格式化微軟爸爸也沒給個好的方案。對我來說,XAML 格式化主要的難題是下面幾個:
- 如果所有屬性都寫在同一行,它太寬了很難看到后面的屬性
- 如果每個屬性單獨一行,它又太長了很難看清楚它的結構
- 屬性之間沒有排序,重要屬性的屬性找起來很困難
- 團隊沒有統一的標准,不小心格式化一下代碼的話全部都會變,CodeReview 煩死個人
如果不想得過且過忍受上述這些問題的話,可以試試用 XAML Styler 這個工具,它正好解決了我最想解決的問題。
2. 安裝 XAML Styler
XAML Styler 是一個 VisualStudio插件(也可用於其它 IDE),這是它在 Github 上的地址:
https://github.com/Xavalon/XamlStyler
在這里你可以找到具體的文檔,而這篇文章我只介紹我關心的其中幾個屬性,不一定滿足到你。
在 VisualStudio 的管理擴展窗口中,輸入 XamlStyle 搜索,點擊“下載”然后關閉 VisualStudio 即可完成安裝。
安裝完成后重啟 Visual Studio,可以在“選項”窗口中看到它的配置:
之后,每次在 XAML 編輯器中執行保存都會自動進行格式化操作。你也可以在 XAML 編輯器的右鍵菜單選擇 Format XAML 或使用快捷鍵進行格式化。
3. 格式化
XAML 的格式主要有兩種方式:所有屬性放一行和每個屬性單獨一行。
如果選擇所有屬性放一行的時候,XAML 結構清晰,結構嚴謹,段落分明,而且文件也很短。
可是萬一很多屬性問題就出來了,一行 XAML 會變得很長。而且看看下面兩個 ContentPresenter,同樣都有 Margin 屬性、HorizontalAlignment 屬性,VerticalAlignment 屬性,RecognizesAccessKey 屬性,SnapsToDevicePixels 順序ing,但你能看到第二個 ContentPresenter 后面偷偷塞了個 Margin 嗎:
<ContentPresenter Margin="{TemplateBinding Padding}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"/>
<ContentPresenter Margin="{TemplateBinding Padding}" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" Margin="40"/>
如果在 VisualStudio 中“文本編輯器->XAML->格式化->間距->特性間距”這個選項中選擇了“將各個屬性分別放置”:
格式化文檔后上面的 XAML 就會變成這樣:
<ContentPresenter Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" />
<ContentPresenter Margin="{TemplateBinding Padding}"
HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
RecognizesAccessKey="True"
SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
Margin="40" />
每個屬性單獨一行不僅不會看漏屬性,而且編輯器本身也不會有橫向和縱向兩種方向的移動,只有從上到下的移動,這就舒服多了。
可是大部分情況下每個屬性分行放置會破壞原本清晰的 XAML 層次結構,例如下面這種本來好好的 XAML:
<Setter Property="FontWeight" Value="Normal" />
<Setter Property="UseSystemFocusVisuals" Value="True" />
<Setter Property="FocusVisualMargin" Value="-3" />
<Setter Property="Height" Value="50" />
<Setter Property="Width" Value="50" />
<Setter Property="Maximum" Value="1" />
變成這樣:
<Setter Property="FontWeight"
Value="Normal" />
<Setter Property="UseSystemFocusVisuals"
Value="True" />
<Setter Property="FocusVisualMargin"
Value="-3" />
<Setter Property="Height"
Value="50" />
<Setter Property="Width"
Value="50" />
<Setter Property="Maximum"
Value="1" />
這種風格優雅得像詩歌
我偶爾稱為豆瓣風
一行變兩行
兩行變四行
本來
一頁看得完
的代碼
變成
兩頁才看得完
也是夠
麻煩的。
XAML Styler 很好地解決了這個問題,它通過 “Attribute tolerance” 屬性控制每一行的容許的最多的屬性數量,如果一個元素的屬性數量少於設定值,那就放在一行,如果超過就所有屬性單獨一行。通常我將這個屬性設置為 2
,再配合 “Keep first attribute on same line = true” 的設置,可以做到下面這種格式化效果:
<SolidColorBrush x:Key="NormalTextColor" Color="#2E2F33" />
<SolidColorBrush x:Key="PrimaryColor" Color="#FFED5B8C" />
<SolidColorBrush x:Key="LineColor" Color="#E1E1E1" />
<SolidColorBrush x:Key="TransparentBackground" Color="Transparent" />
<ControlTemplate x:Key="CompletedTemplate" TargetType="ContentControl">
<Grid x:Name="CompletedElement" Margin="-2">
<control:DropShadowPanel HorizontalContentAlignment="Stretch"
VerticalContentAlignment="Stretch"
BlurRadius="8"
OffsetX="0"
OffsetY="0"
Color="#FFED5B8C">
<Ellipse x:Name="CompletedRectangle" Fill="{StaticResource PrimaryColor}" />
</control:DropShadowPanel>
</Grid>
</ControlTemplate>
這樣就可以兼顧兩種格式化的優點。
4. 排序
如果元素有多個屬性,要找到它的主要屬性(通常是 Name 和 Grid.Row)需要頗費一番功夫。XAML Styler 根據一個可設定的規則自動將元素的各個屬性排序,這個規則如下:
"AttributeOrderingRuleGroups": [
"x:Class",
"xmlns, xmlns:x",
"xmlns:*",
"x:Key, Key, x:Name, Name, x:Uid, Uid, Title",
"Grid.Row, Grid.RowSpan, Grid.Column, Grid.ColumnSpan, Canvas.Left, Canvas.Top, Canvas.Right, Canvas.Bottom",
"Width, Height, MinWidth, MinHeight, MaxWidth, MaxHeight",
"Margin, Padding, HorizontalAlignment, VerticalAlignment, HorizontalContentAlignment, VerticalContentAlignment, Panel.ZIndex",
"*:*, *",
"PageSource, PageIndex, Offset, Color, TargetName, Property, Value, StartPoint, EndPoint",
"mc:Ignorable, d:IsDataSource, d:LayoutOverrides, d:IsStaticText",
"Storyboard.*, From, To, Duration"
],
排序結果大致如下:
<Button x:Name="Show"
Grid.Row="1"
Padding="40,20"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Background="#00aef1"
Content="Show"
Foreground="White"
Style="{StaticResource BubbleButtonStyle}" />
另外,我不喜歡它自動將 VisualStateManager 排序到后面,雖然這個排序合理,但不符合我的習慣,所以要將 “Record visual state manager” 設置為 None。
5. 統一標准
最后,就算自己做好了格式化,團隊中的其它成員使用了不同的格式化標准也會引起很多問題。針對這個問題 Xaml Styler 也提供了解決方案。
在項目的根目錄創建一個名為“Settings.XamlStyler”的文件,內容參考這個網址:https://github.com/Xavalon/XamlStyler/wiki/External-Configurations 中的 Default Configuration。有了這個配置文件,XAML Styler 就會根據它而不是全局配置進行格式化,作為項目的統一格式化標准。