XAML 分析概述

XAML 分析是一種能通知你應用程式效能問題的工具。 它會根據一套效能指引和最佳實務來檢查你的應用程式程式碼,並從常見問題的規則集中找出問題。

當工具發現問題時,會指向 Visual Studio 的診斷工具、來源資訊和文件,讓你能調查並修復問題。

解碼影像大小大於渲染尺寸

高解析度擷取的影像可能會導致應用程式解碼時使用更多 CPU,讀取磁碟後也會消耗更多記憶體。 如果你只以較小的尺寸顯示高解析度影像,解碼並沒有什麼好處。 相反地,請使用 DecodePixelWidthDecodePixelHeight 屬性,建立一個符合你在螢幕上繪製尺寸的影像版本。

Impact

將影像顯示到非原生大小,可能會對 CPU 時間(解碼與下載)及記憶體產生負面影響。

原因和解決方案

影像未以非同步方式設定

你的應用程式使用的是 SetSource(),而不是 SetSourceAsync()。 設定串流非同步解碼影像時,務必使用 SetSourceAsync

影像來源設定在元素進入活樹之前

在使用 SetSourceAsyncUriSource 設定內容後,BitmapImage 會連接到即時 XAML 樹。 設定來源前,務必將 BitmapImage 附加到活樹上。 當你在標記中指定圖片元素或筆刷時,附加功能會自動完成。

良好 — 使用 BitmapImage 在標記中指定來源:

<Image x:Name="myImage">
    <Image.Source>
        <BitmapImage UriSource="Assets/cool-image.png"/>
    </Image.Source>
</Image>

很好 ——在設定 UriSource 之前,先把 BitmapImage 連接到樹狀結構:

Image myImage = new Image();
var bitmapImage = new BitmapImage();
myImage.Source = bitmapImage;
bitmapImage.UriSource = new Uri("ms-appx:///Assets/cool-image.png", UriKind.RelativeOrAbsolute);

壞處 — 在連接 BitmapImage 到 樹之前先設定 UriSource:

Image myImage = new Image();
var bitmapImage = new BitmapImage();
bitmapImage.UriSource = new Uri("ms-appx:///Assets/cool-image.png", UriKind.RelativeOrAbsolute);
myImage.Source = bitmapImage;

影像筆刷是非矩形的

當你將影像用於非矩形筆刷時,該影像會使用軟體點陣化路徑,而此路徑不會縮放影像。 此外,系統還會將映像檔的副本儲存在軟體與硬體記憶體中。 當您使用非矩形筆刷時,請先將影像縮放至接近實際算繪時所使用的尺寸。

或者,你可以使用 DecodePixelWidthDecodePixelHeight 屬性設定明確的解碼大小:

<Image>
    <Image.Source>
    <BitmapImage UriSource="ms-appx:///Assets/highresCar.jpg"
                 DecodePixelWidth="300" DecodePixelHeight="200"/>
    </Image.Source>
</Image>

DecodePixelWidthDecodePixelHeight 的單位預設為實體像素。 將 DecodePixelType 設為 邏輯, 讓解碼大小自動考量系統目前的縮放因子,類似其他 XAML 內容。

若事先無法判斷適當的解碼大小,請依賴 XAML 的自動正確大小解碼功能,該功能會盡力將影像解碼至適當大小。

點圖圖示中使用的影像會回復到自然大小

使用 DecodePixelWidthDecodePixelHeight 屬性設定明確的解碼大小。

DecodePixelWidth 或 DecodePixelHeight 大於顯示尺寸

如果你明確設定 DecodePixelWidthDecodePixelHeight 超過圖片螢幕顯示大小,應用程式會佔用額外記憶體(每像素最多 4 位元組),這對大型影像來說成本較高。

圖片已隱藏

影像會因將宿主影像元素、筆刷或父元素上的 Opacity 設為 0,或將 Visibility 設為 Collapsed 而隱藏。 因裁切或透明而無法在螢幕上顯示的影像,可能會回復到自然大小解碼。

圖片使用 NineGrid 屬性

當你使用 NineGrid的影像時,影像會使用軟體光柵化路徑。 先將影像縮放至接近實際渲染時的尺寸。

載荷時的坍縮元件

一個常見的模式是先隱藏 UI 元素,之後再顯示。 大多數情況下,你應該延後使用 x:Load 這些元素,以避免在載入時支付製作成本。

Impact

已摺疊的元素會與其他元素一起載入,並增加載入時間。

Solution

使用 x:Load 屬性 來延遲 UI 載入。 元素會在你需要時載入,減少初期處理。 你也可以用來 x:Bind 控制載入狀態。

ListView 並非虛擬化

UI 虛擬化是你能為收藏效能做最重要改進的部分。 代表物品的使用者介面元素是按需建立的。 對於綁定在 1000 件物品集合的控制項,一次建立所有 UI 會浪費資源,因為你無法同時顯示所有物品。 ListViewGridView 會為你處理 UI 虛擬化——它們會為接近可視區域的項目產生使用者介面,並回收已捲動出可視範圍之項目所占用的記憶體。

Impact

非虛擬化的 ItemsControl 會因載入超過必要數量的子項目,而增加載入時間和資源用量。

Solution

在 上 ItemsControl 設定寬度和高度來定義視窗。 當你把虛擬控制項放在空間無限的面板(例如 ScrollViewerGrid 或 有自動大小列的面板)時,控制鍵會佔用足夠空間放置所有項目,這會破壞虛擬化。

UI 執行緒在載入時被阻塞或閒置

當同步呼叫在其他執行緒上執行的函式,並因此阻塞 UI 執行緒時,就會發生 UI 執行緒阻塞。 透過使用非同步 API,保持 UI 執行緒的響應式。 如需詳細資訊,請參閱 保持 UI 執行緒的回應能力

使用 {x:Bind} 代替 {Binding}

{Binding}{x:Bind} 耗用更多時間和記憶體。 創建 a {Binding} 會引發一連串的分配,而更新綁定目標可能需要反思和限制。 使用 {x:Bind},它會在建置時編譯綁定,以提升效能與編譯時的驗證。

Note

{x:Bind} 不適用於所有情況,例如晚期繫結情境。 詳情請參閱 {x:Bind} 文件

在 ResourceDictionaries 中使用 x:Key 代替 x:Name

當你在資源上使用 x:Name 時,平台會立即實例化它,因為 x:Name 會建立欄位參考。 如果不需要在程式碼後置中參考該資源,請改用 x:Key

使用虛擬化面板來管理收藏

如果你提供自訂項目面板範本(參見 ItemsPanel),請使用虛擬化面板,例如 ItemsWrapGridItemsStackPanel。 使用 VariableSizedWrapGridWrapGridStackPanel 會停用虛擬化。

無障礙性:為 UIA 元素命名

在你的控制項的 XAML 裡設定 AutomationProperties.Name 成適當的本地化字串。 如果某元素不應該出現在 UIA 樹中,則設 AutomationProperties.AccessibilityView = "Raw"

避免為同一父元素下的兩個 UIA 元素賦予相同的 NameControlType。 在經常出現重複名稱的清單中,使用綁定從資料來源設定 AutomationProperties.Name