最佳化您的 XAML 配置

重要 API

版面配置是為 UI 定義視覺結構的程序。 用來說明 XAML 版面配置的主要機制是透過面板,這類面板是讓您能夠在其中放置與排列 UI 元素的容器物件。 在 CPU 使用量與記憶體負荷方面,版面配置可說是 XAML 應用程式中高度耗費資源的一部分。 您可以採取下列一些簡單步驟來提升 XAML 應用程式的版面配置效能。

減少版面配置結構

簡化 UI 元素樹狀結構的階層結構,就能獲得最大的版面配置效能。 面板存在於視覺化樹狀結構中,但它們是結構化元素,而不是像 ButtonRectangle 的「像素產生的元素」。 藉由減少非像素產生的元素數目來簡化樹狀結構,通常可大幅提升效能。

許多 UI 都是透過巢狀面板來實作,因而產生了既深且複雜的面板與元素樹狀結構。 巢狀面板相當方便,但在許多情況下,您可以利用更複雜的單一面板來達成相同的 UI。 使用單一面板可提供較佳的效能。

減少版面配置結構的時機

使用簡單的方式來減少版面配置結構 (例如,從最上層頁面減少一個巢狀面板) 並不會有顯著的效果。

最大的效能提升是來自減少在 UI (例如 ListViewGridView) 中重複的版面配置結構。 這些 ItemsControl 元素會使用 DataTemplate,它會定義已多次具現化的 UI 元素樹狀子目錄。 當同一個樹狀子目錄在您的應用程式中多次重複出現時,任何對於該樹狀子目錄的效能提升,都會使對整體應用程式效能所產生的影響倍增。

範例

請考慮下列 UI。

Form layout example

下列範例示範 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。 請勿定義 RowDefinitionsColumnDefinitions

範例

<Grid>
    <Ellipse Fill="Red" Width="200" Height="200" />
    <TextBlock Text="Test" 
               HorizontalAlignment="Center" 
               VerticalAlignment="Center" />
</Grid>

Text overlaid on a circle

<Grid Width="200" BorderBrush="Black" BorderThickness="1">
    <TextBlock Text="Test1" HorizontalAlignment="Left" />
    <TextBlock Text="Test2" HorizontalAlignment="Right" />
</Grid>

Two text blocks in a grid

使用面板內建的框線屬性

GridStackPanelRelativePanelContentPresenter 控制項具有內建的框線屬性,可讓您沿著控制項繪製框線,而不需要在 XAML 中新增額外的 Border 元素。 支援內建框線的新屬性包括:BorderBrushBorderThicknessCornerRadiusPadding。 這其中每一個都是 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 類別會公開兩個用於回應版面配置變更的類似事件:LayoutUpdatedSizeChanged。 您可能會在進行版面配置期間調整元素大小時,使用這其中一個事件來接收通知。 這兩個事件的語意不同,而且在它們之間進行選擇時有一些重要的效能考量。

如需良好的效能,SizeChanged 幾乎一向是正確的選擇。 SizeChanged 具備直覺式語意。 它會在進行版面配置期間更新 FrameworkElement 的大小時引發。

LayoutUpdated 也會在配置期間引發,但它具備全域語意—其會在更新任何元素時,於每個元素上引發。 通常只會在事件處理常式中進行本機處理,在此情況下,程式碼執行的頻率會比所需的更頻繁。 只有在您需要知道何時重新放置元素而不會改變其大小時 (這並不常見),才能使用 LayoutUpdated

選擇面板

在選擇個別面板時,通常不會將效能納入考量。 通常是藉由考量哪一個面板可提供最接近您正在實作之 UI 的版面配置行為來選擇。 例如,如果您在 GridStackPanelRelativePanel 之間進行選擇,您所選擇的面板應該要最能對應到您實作的心智模型。

每個 XAML 面板都已針對良好的效能進行最佳化,而所有的面板都可為類似 UI 提供類似的效能。