XAML 分析是一种工具,可通知应用中的性能问题。 它会根据一组性能准则和最佳做法检查应用代码,并从常见问题的规则集中识别问题。
当该工具发现问题时,它指向Visual Studio的诊断工具、源信息和文档,以便你可以调查并解决问题。
解码的图像大小大于呈现大小
以高分辨率捕获的图像可能会导致应用在从磁盘加载后解码时使用更多的 CPU,以及更多内存。 如果仅以较小尺寸显示图像,那么解码高分辨率图像就没有任何优势。 而应使用 DecodePixelWidth 和 DecodePixelHeight 属性,创建一个与图像在屏幕上绘制尺寸一致的版本。
影响
以本机大小以外的大小显示图像可能会对 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>
正确 — 在设置 BitmapImage 的 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 设置为 Logical ,使解码大小自动考虑系统的当前缩放系数,类似于其他 XAML 内容。
如果无法提前确定适当的解码大小,请依赖于 XAML 的自动正确大小解码,从而尽力以适当的大小解码图像。
BitmapIcons 内使用的图像回退到自然大小
使用 DecodePixelWidth 和 DecodePixelHeight 属性设置显式解码大小。
DecodePixelWidth 或 DecodePixelHeight 大于显示大小
如果显式设置 DecodePixelWidth 或 DecodePixelHeight 大于图像的屏幕显示大小,应用将使用额外的内存(每个像素最多 4 个字节),这对于大型图像来说会变得昂贵。
图像已隐藏
通过将宿主图像元素、画笔或父元素上的 Opacity 设置为 0,或将 Visibility 设置为 Collapsed,可以隐藏图像。 由于剪辑或透明度而在屏幕上不可见的图像可能会回退到自然大小的解码。
图像使用 NineGrid 属性
将图像与 NineGrid 配合使用时,该图像将使用软件光栅化路径。 将图像预先缩放到接近其渲染时的大小。
加载时折叠的元素
常见的模式是最初隐藏 UI 元素,稍后显示它们。 在大多数情况下,应使用 x:Load 延迟这些元素,以避免在加载时承担创建这些元素的开销。
影响
折叠元素与其他元素一起加载并增加加载时间。
Solution
使用 x:Load 属性 延迟 UI 的加载。 当需要该元素时,该元素将加载,从而减少初始处理。 还可以用于 x:Bind 控制加载状态。
ListView 未虚拟化
UI 虚拟化是可为集合性能做出的最重要改进。 表示项的 UI 元素是按需创建的。 对于绑定到 1000 项集合的控件,一次性创建所有 UI 会浪费资源,因为无法同时显示所有项。
ListView 和 GridView 会为你处理 UI 虚拟化——它们会为靠近视口的项生成 UI,并回收已滚动出视图的项所占用的内存。
影响
非虚拟化 ItemsControl 通过加载比必要更多的子项来增加加载时间和资源使用率。
Solution
在 ItemsControl 上设置宽度和高度,以定义视口。 当你将虚拟化控件放置在具有无限空间(例如 ScrollViewer 或 Grid 具有自动调整大小的行)的面板中时,该控件将占用足够的空间来容纳所有项,这会使虚拟化失败。
加载过程中 UI 线程被阻塞或处于空闲状态
当对执行非线程函数的同步调用阻止 UI 线程时,会发生 UI 线程阻塞。 使用异步 API 使 UI 线程保持响应。 有关详细信息,请参阅 保持 UI 线程响应。
使用 {x:Bind} 而不是 {Binding}
{Binding} 比 {x:Bind} 耗费更多时间和内存。 创建 {Binding} 会导致一系列内存分配,而更新绑定目标则可能涉及反射和装箱。 使用 {x:Bind},用于在生成时编译绑定,以提高性能和编译时验证。
注释
{x:Bind} 并不适用于所有场景,例如后期绑定场景。 有关详细信息,请参阅 {x:Bind} 文档 。
在 ResourceDictionaries 中使用 x:Key 而不是 x:Name
在 x:Name 资源上使用时,平台会立即实例化它,因为 x:Name 会创建字段引用。 如果不需要从代码隐藏文件中引用该资源,请改用 x:Key。
对集合使用虚拟化面板
如果提供自定义项面板模板(请参阅 ItemsPanel),请使用虚拟化面板,例如 ItemsWrapGrid 或 ItemsStackPanel。 使用VariableSizedWrapGrid或WrapGridStackPanel禁用虚拟化。
辅助功能:提供 UIA 元素的名称
在控件的 XAML 中,将 AutomationProperties.Name 设置为适当的本地化字符串。 如果元素不应出现在 UIA 树中,请设置 AutomationProperties.AccessibilityView = "Raw"。
避免为具有相同父元素的两个 UIA 元素提供相同的 Name 和 ControlType。 在经常会出现重名的列表中,使用数据绑定将 AutomationProperties.Name 设置为来自数据源的值。