最佳化您的 XAML 配置
重要 API
版面配置是為 UI 定義視覺結構的程序。 用來說明 XAML 版面配置的主要機制是透過面板,這類面板是讓您能夠在其中放置與排列 UI 元素的容器物件。 在 CPU 使用量與記憶體負荷方面,版面配置可說是 XAML 應用程式中高度耗費資源的一部分。 您可以採取下列一些簡單步驟來提升 XAML 應用程式的版面配置效能。
減少版面配置結構
簡化 UI 元素樹狀結構的階層結構,就能獲得最大的版面配置效能。 面板存在於視覺化樹狀結構中,但它們是結構化元素,而不是像 Button 或 Rectangle 的「像素產生的元素」。 藉由減少非像素產生的元素數目來簡化樹狀結構,通常可大幅提升效能。
許多 UI 都是透過巢狀面板來實作,因而產生了既深且複雜的面板與元素樹狀結構。 巢狀面板相當方便,但在許多情況下,您可以利用更複雜的單一面板來達成相同的 UI。 使用單一面板可提供較佳的效能。
減少版面配置結構的時機
使用簡單的方式來減少版面配置結構 (例如,從最上層頁面減少一個巢狀面板) 並不會有顯著的效果。
最大的效能提升是來自減少在 UI (例如 ListView 或 GridView) 中重複的版面配置結構。 這些 ItemsControl 元素會使用 DataTemplate,它會定義已多次具現化的 UI 元素樹狀子目錄。 當同一個樹狀子目錄在您的應用程式中多次重複出現時,任何對於該樹狀子目錄的效能提升,都會使對整體應用程式效能所產生的影響倍增。
範例
請考慮下列 UI。
下列範例示範 3 種實作相同 UI 的方式。 每個實作選項都會在畫面上產生幾乎完全相同的像素,但在實作細節上卻大不相同。
選項 1:巢狀 StackPanel 元素
雖然這是最簡單的模型,但它使用了 5 個面板元素,並產生了顯著的負荷。
<StackPanel>
<TextBlock Text="Options:" />
<StackPanel Orientation="Horizontal">
<CheckBox Content="Power User" />
<CheckBox Content="Admin" Margin="20,0,0,0" />
</StackPanel>
<TextBlock Text="Basic information:" />
<StackPanel Orientation="Horizontal">
<TextBlock Text="Name:" Width="75" />
<TextBox Width="200" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Email:" Width="75" />
<TextBox Width="200" />
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="Password:" Width="75" />
<TextBox Width="200" />
</StackPanel>
<Button Content="Save" />
</StackPanel>
選項 2:單一 Grid
Grid 增加了一些複雜度,但只使用單一面板元素。
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock Text="Options:" Grid.ColumnSpan="2" />
<CheckBox Content="Power User" Grid.Row="1" Grid.ColumnSpan="2" />
<CheckBox Content="Admin" Margin="150,0,0,0" Grid.Row="1" Grid.ColumnSpan="2" />
<TextBlock Text="Basic information:" Grid.Row="2" Grid.ColumnSpan="2" />
<TextBlock Text="Name:" Width="75" Grid.Row="3" />
<TextBox Width="200" Grid.Row="3" Grid.Column="1" />
<TextBlock Text="Email:" Width="75" Grid.Row="4" />
<TextBox Width="200" Grid.Row="4" Grid.Column="1" />
<TextBlock Text="Password:" Width="75" Grid.Row="5" />
<TextBox Width="200" Grid.Row="5" Grid.Column="1" />
<Button Content="Save" Grid.Row="6" />
</Grid>
選項 3:單一 RelativePanel:
比起使用巢狀面板,此單一面板同樣較為複雜,但可能會比 Grid 更容易了解和維護。
<RelativePanel>
<TextBlock Text="Options:" x:Name="Options" />
<CheckBox Content="Power User" x:Name="PowerUser" RelativePanel.Below="Options" />
<CheckBox Content="Admin" Margin="20,0,0,0"
RelativePanel.RightOf="PowerUser" RelativePanel.Below="Options" />
<TextBlock Text="Basic information:" x:Name="BasicInformation"
RelativePanel.Below="PowerUser" />
<TextBlock Text="Name:" RelativePanel.AlignVerticalCenterWith="NameBox" />
<TextBox Width="200" Margin="75,0,0,0" x:Name="NameBox"
RelativePanel.Below="BasicInformation" />
<TextBlock Text="Email:" RelativePanel.AlignVerticalCenterWith="EmailBox" />
<TextBox Width="200" Margin="75,0,0,0" x:Name="EmailBox"
RelativePanel.Below="NameBox" />
<TextBlock Text="Password:" RelativePanel.AlignVerticalCenterWith="PasswordBox" />
<TextBox Width="200" Margin="75,0,0,0" x:Name="PasswordBox"
RelativePanel.Below="EmailBox" />
<Button Content="Save" RelativePanel.Below="PasswordBox" />
</RelativePanel>
如下列範例所示,有許多方式可用來達成相同的 UI。 您應該謹慎地進行全面考量 (包括效能、可讀性和可維護性),然後做出選擇。
針對重疊的 UI 使用單一儲存格格線
常見的 UI 要求是讓元素彼此重疊的版面配置。 通常會以這種方式利用邊框間距、邊界、對齊和轉換來放置元素。 XAML Grid控制項已最佳化,可改善重疊元素的版面配置效能。
重要若要查看改進功能,請使用單一儲存格 Grid。 請勿定義 RowDefinitions 或 ColumnDefinitions。
範例
<Grid>
<Ellipse Fill="Red" Width="200" Height="200" />
<TextBlock Text="Test"
HorizontalAlignment="Center"
VerticalAlignment="Center" />
</Grid>
<Grid Width="200" BorderBrush="Black" BorderThickness="1">
<TextBlock Text="Test1" HorizontalAlignment="Left" />
<TextBlock Text="Test2" HorizontalAlignment="Right" />
</Grid>
使用面板內建的框線屬性
Grid、StackPanel、RelativePanel、ContentPresenter 控制項具有內建的框線屬性,可讓您沿著控制項繪製框線,而不需要在 XAML 中新增額外的 Border 元素。 支援內建框線的新屬性包括:BorderBrush、BorderThickness、CornerRadius和 Padding。 這其中每一個都是 DependencyProperty,因此您可以將它們與繫結和動畫搭配使用。 它們是設計用來完全取代個別的 Border 元素。
如果您的 UI 在這些面板周圍具有 Border 元素,請改用內建的框線,以便在應用程式的版面配置結構中省下額外的元素。 如先前所述,這樣能夠大幅節省,特別是在重複 UI 的情況下。
範例
<RelativePanel BorderBrush="Red" BorderThickness="2" CornerRadius="10" Padding="12">
<TextBox x:Name="textBox1" RelativePanel.AlignLeftWithPanel="True"/>
<Button Content="Submit" RelativePanel.Below="textBox1"/>
</RelativePanel>
使用 SizeChanged 事件來回應版面配置變更
FrameworkElement 類別會公開兩個用於回應版面配置變更的類似事件:LayoutUpdated 和 SizeChanged。 您可能會在進行版面配置期間調整元素大小時,使用這其中一個事件來接收通知。 這兩個事件的語意不同,而且在它們之間進行選擇時有一些重要的效能考量。
如需良好的效能,SizeChanged 幾乎一向是正確的選擇。 SizeChanged 具備直覺式語意。 它會在進行版面配置期間更新 FrameworkElement 的大小時引發。
LayoutUpdated 也會在配置期間引發,但它具備全域語意—其會在更新任何元素時,於每個元素上引發。 通常只會在事件處理常式中進行本機處理,在此情況下,程式碼執行的頻率會比所需的更頻繁。 只有在您需要知道何時重新放置元素而不會改變其大小時 (這並不常見),才能使用 LayoutUpdated。
選擇面板
在選擇個別面板時,通常不會將效能納入考量。 通常是藉由考量哪一個面板可提供最接近您正在實作之 UI 的版面配置行為來選擇。 例如,如果您在 Grid、StackPanel 和 RelativePanel 之間進行選擇,您所選擇的面板應該要最能對應到您實作的心智模型。
每個 XAML 面板都已針對良好的效能進行最佳化,而所有的面板都可為類似 UI 提供類似的效能。