XAML 分析是一種能通知你應用程式效能問題的工具。 它會根據一套效能指引和最佳實務來檢查你的應用程式程式碼,並從常見問題的規則集中找出問題。
當工具發現問題時,會指向 Visual Studio 的診斷工具、來源資訊和文件,讓你能調查並修復問題。
解碼影像大小大於渲染尺寸
高解析度擷取的影像可能會導致應用程式解碼時使用更多 CPU,讀取磁碟後也會消耗更多記憶體。 如果你只以較小的尺寸顯示高解析度影像,解碼並沒有什麼好處。 相反地,請使用 DecodePixelWidth 和 DecodePixelHeight 屬性,建立一個符合你在螢幕上繪製尺寸的影像版本。
Impact
將影像顯示到非原生大小,可能會對 CPU 時間(解碼與下載)及記憶體產生負面影響。
原因和解決方案
影像未以非同步方式設定
你的應用程式使用的是 SetSource(),而不是 SetSourceAsync()。 設定串流非同步解碼影像時,務必使用 SetSourceAsync 。
影像來源設定在元素進入活樹之前
在使用 SetSourceAsync 或 UriSource 設定內容後,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;
影像筆刷是非矩形的
當你將影像用於非矩形筆刷時,該影像會使用軟體點陣化路徑,而此路徑不會縮放影像。 此外,系統還會將映像檔的副本儲存在軟體與硬體記憶體中。 當您使用非矩形筆刷時,請先將影像縮放至接近實際算繪時所使用的尺寸。
或者,你可以使用 DecodePixelWidth 和 DecodePixelHeight 屬性設定明確的解碼大小:
<Image>
<Image.Source>
<BitmapImage UriSource="ms-appx:///Assets/highresCar.jpg"
DecodePixelWidth="300" DecodePixelHeight="200"/>
</Image.Source>
</Image>
DecodePixelWidth 和 DecodePixelHeight 的單位預設為實體像素。 將 DecodePixelType 設為 邏輯, 讓解碼大小自動考量系統目前的縮放因子,類似其他 XAML 內容。
若事先無法判斷適當的解碼大小,請依賴 XAML 的自動正確大小解碼功能,該功能會盡力將影像解碼至適當大小。
點圖圖示中使用的影像會回復到自然大小
使用 DecodePixelWidth 和 DecodePixelHeight 屬性設定明確的解碼大小。
DecodePixelWidth 或 DecodePixelHeight 大於顯示尺寸
如果你明確設定 DecodePixelWidth 或 DecodePixelHeight 超過圖片螢幕顯示大小,應用程式會佔用額外記憶體(每像素最多 4 位元組),這對大型影像來說成本較高。
圖片已隱藏
影像會因將宿主影像元素、筆刷或父元素上的 Opacity 設為 0,或將 Visibility 設為 Collapsed 而隱藏。 因裁切或透明而無法在螢幕上顯示的影像,可能會回復到自然大小解碼。
圖片使用 NineGrid 屬性
當你使用 NineGrid的影像時,影像會使用軟體光柵化路徑。 先將影像縮放至接近實際渲染時的尺寸。
載荷時的坍縮元件
一個常見的模式是先隱藏 UI 元素,之後再顯示。 大多數情況下,你應該延後使用 x:Load 這些元素,以避免在載入時支付製作成本。
Impact
已摺疊的元素會與其他元素一起載入,並增加載入時間。
Solution
使用 x:Load 屬性 來延遲 UI 載入。 元素會在你需要時載入,減少初期處理。 你也可以用來 x:Bind 控制載入狀態。
ListView 並非虛擬化
UI 虛擬化是你能為收藏效能做最重要改進的部分。 代表物品的使用者介面元素是按需建立的。 對於綁定在 1000 件物品集合的控制項,一次建立所有 UI 會浪費資源,因為你無法同時顯示所有物品。
ListView 和 GridView 會為你處理 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),請使用虛擬化面板,例如 ItemsWrapGrid 或 ItemsStackPanel。 使用 VariableSizedWrapGrid、WrapGrid 或 StackPanel 會停用虛擬化。
無障礙性:為 UIA 元素命名
在你的控制項的 XAML 裡設定 AutomationProperties.Name 成適當的本地化字串。 如果某元素不應該出現在 UIA 樹中,則設 AutomationProperties.AccessibilityView = "Raw"。
避免為同一父元素下的兩個 UIA 元素賦予相同的 Name 和 ControlType。 在經常出現重複名稱的清單中,使用綁定從資料來源設定 AutomationProperties.Name 。