Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Анализ разметки XAML для создания объектов в памяти занимает много времени для сложного пользовательского интерфейса. Ниже приведены некоторые действия, которые можно сделать, чтобы улучшить синтаксический анализ и время загрузки разметки XAML и эффективность памяти приложения WinUI.
При запуске приложения ограничьте разметку XAML, загруженную только тем, что вам нужно для начального пользовательского интерфейса. Проверьте разметку на начальной странице, включая ресурсы страницы, и убедитесь, что вы не загружаете дополнительные элементы, которые не нужны сразу. Эти элементы могут поступать из различных источников, таких как словари ресурсов, элементы, которые изначально свернуты, и элементы, отображаемые поверх других элементов.
Оптимизация XAML для повышения эффективности требует компромиссов; Для каждой ситуации не всегда существует единое решение. Здесь мы рассмотрим некоторые распространенные проблемы и укажите рекомендации, которые можно использовать, чтобы сделать правильные компромиссы для вашего приложения WinUI.
Свести к минимуму число элементов
Несмотря на то, что платформа XAML способна отображать большое количество элементов, вы можете ускорить расположение и отрисовку приложения, используя минимальное количество элементов, необходимых для достижения желаемой визуализации.
Решения, которые вы принимаете относительно того, как выкладывать элементы пользовательского интерфейса, влияют на количество создаваемых элементов при запуске приложения. Дополнительные сведения об оптимизации макета см. в статье "Оптимизация макета XAML".
Число элементов крайне важно в шаблонах данных, так как каждый элемент создается повторно для каждого элемента данных. Для получения информации о сокращении количества элементов в списке или сетке см. "Сокращение элементов на элемент" в статье "Оптимизация производительности ListView и GridView для WinUI".
Здесь мы рассмотрим некоторые другие способы уменьшения количества элементов, которые нужно загрузить при запуске.
Отложить создание элемента
Если разметка XAML содержит элементы, которые не отображаются сразу, можно отложить загрузку этих элементов, пока они не будут показаны. Например, можно отложить создание невидимого содержимого, такого как второстепенная вкладка в интерфейсе, напоминающем вкладки. Кроме того, можно отобразить элементы в представлении сетки по умолчанию, но предоставить пользователю возможность просматривать данные в списке. Вы можете отложить загрузку списка до тех пор, пока это не потребуется.
Используйте атрибут x:Load вместо свойства Видимости , чтобы управлять отображением элемента. Если видимость элемента установлена как Collapsed, он пропускается в процессе рендеринга, но вы по-прежнему несете расходы на экземпляр объекта в памяти. Если вместо этого используется x:Load, платформа не создает экземпляр объекта до тех пор, пока не потребуется, поэтому затраты на память еще ниже. Недостаток заключается в том, что вы оплачиваете небольшую нагрузку на память (приблизительно 600 байт), когда пользовательский интерфейс не загружается.
Замечание
В пакете SDK для приложений Windows рекомендуется использовать x:Load как шаблон отложенной загрузки для содержимого XAML, которое не требует немедленной загрузки.
В следующих примерах показано различие в количестве элементов и памяти, используемых при использовании различных методов для скрытия элементов пользовательского интерфейса. ListView и GridView, содержащие идентичные элементы, помещаются в корневую сетку страницы. ListView не отображается, но отображается GridView. XAML в каждом из этих примеров создает один и тот же пользовательский интерфейс на экране. Используйте средства профилирования и производительности для проверки количества элементов и использования памяти в приложении.
Вариант 1 — неэффективный
Здесь объект ListView загружается, но не отображается, так как он Width есть 0. ListView и каждый из его дочерних элементов создаются в визуальном дереве и загружаются в память.
<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<ListView x:Name="List1" Width="0">
<ListViewItem>Item 1</ListViewItem>
<ListViewItem>Item 2</ListViewItem>
<ListViewItem>Item 3</ListViewItem>
<ListViewItem>Item 4</ListViewItem>
<ListViewItem>Item 5</ListViewItem>
<ListViewItem>Item 6</ListViewItem>
<ListViewItem>Item 7</ListViewItem>
<ListViewItem>Item 8</ListViewItem>
<ListViewItem>Item 9</ListViewItem>
<ListViewItem>Item 10</ListViewItem>
</ListView>
<GridView x:Name="Grid1">
<GridViewItem>Item 1</GridViewItem>
<GridViewItem>Item 2</GridViewItem>
<GridViewItem>Item 3</GridViewItem>
<GridViewItem>Item 4</GridViewItem>
<GridViewItem>Item 5</GridViewItem>
<GridViewItem>Item 6</GridViewItem>
<GridViewItem>Item 7</GridViewItem>
<GridViewItem>Item 8</GridViewItem>
<GridViewItem>Item 9</GridViewItem>
<GridViewItem>Item 10</GridViewItem>
</GridView>
</Grid>
Динамическое визуальное дерево с загруженным ListView. Общее число элементов для страницы равно 89.
ListView и его дочерние элементы загружаются в память.
Вариант 2 . Лучше
Здесь у ListView задано Visibility значение Collapsed (остальной XAML идентичен оригиналу). ListView создается в визуальном дереве, но его дочерние элементы не создаются. Однако они по-прежнему загружаются в память, поэтому использование памяти идентично предыдущему примеру.
<ListView x:Name="List1" Visibility="Collapsed">
Динамическое визуальное дерево с свернутыми ListView. Общее число элементов для страницы равно 46.
ListView и его дочерние элементы загружаются в память.
Вариант 3. Наиболее эффективный
Здесь ListView имеет атрибут x:Load, равный False (другой XAML идентичен исходному коду). ListView не создается в визуальном дереве или загружается в память при запуске.
<ListView x:Name="List1" Visibility="Collapsed" x:Load="False">
Динамическое визуальное дерево без загрузки ListView. Общее количество элементов для страницы равно 45.
ListView и его дочерние элементы не загружаются в память.
Замечание
Количество элементов и использование памяти в этих примерах очень малы и показаны только для демонстрации концепции. В этих примерах затраты на использование x:Load больше экономии памяти, поэтому приложение не будет пользоваться преимуществами. Вы должны использовать средства профилирования в приложении, чтобы определить, поможет ли отложенная загрузка.
Использование свойств панели макета
Панели макета имеют свойство Background, поэтому нет необходимости помещать Прямоугольник перед панелью только для того, чтобы задать цвет.
Неэффективно
<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Grid>
<Rectangle Fill="Black"/>
</Grid>
Эффективным
<Grid Background="Black"/>
Панели макета также имеют встроенные свойства границы, поэтому не нужно помещать элемент Border на панель макета. Дополнительные сведения и примеры см. в статье "Оптимизация макета XAML ".
Использование изображений вместо векторных элементов
Если повторно использовать один и тот же векторный элемент достаточное количество раз, будет более эффективно использовать элемент Image. Элементы на основе векторов могут быть дороже, так как ЦП должен создавать каждый отдельный элемент отдельно. Файл изображения должен быть декодирован только один раз.
Оптимизация ресурсов и словарей ресурсов
Как правило, словари ресурсов используются для хранения нескольких глобальных ресурсов, на которые вы хотите ссылаться в нескольких местах в приложении. Например, стили, кисти, шаблоны и т. д.
Как правило, ResourceDictionary оптимизирован, чтобы избежать создания экземпляров ресурсов, пока они не будут запрашиваться. Но существуют ситуации, которые следует избежать, чтобы ресурсы не создавались без необходимости.
Ресурсы с x:Name
Используйте атрибут x:Key для ссылки на ресурсы. Любой ресурс с атрибутом x:Name не выиграет от оптимизации платформы; вместо этого он создается как только создаётся ResourceDictionary. Это происходит потому, что x:Name сообщает платформе, что приложению требуется доступ к этому ресурсу, поэтому платформа должна создать что-то для хранения ссылки на нее.
«ResourceDictionary» в «UserControl»
ResourceDictionary, определенный внутри UserControl , несет штраф. Платформа создает копию такого ResourceDictionary для каждого экземпляра UserControl. Если у вас есть UserControl, который используется много, переместите ResourceDictionary из UserControl и поместите его на уровень страницы.
Область ресурса и ResourceDictionary
Если страница ссылается на пользовательский элемент управления или ресурс, определенный в другом файле, платформа также анализирует этот файл.
Здесь, так как InitialPage.xaml использует один ресурс из ExampleResourceDictionary.xaml, весь пример ExampleResourceDictionary.xaml должен быть проанализирован при запуске.
InitialPage.xaml
<Page x:Class="ExampleNamespace.InitialPage" ...>
<Page.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="ExampleResourceDictionary.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Page.Resources>
<Grid>
<TextBox Foreground="{StaticResource TextBrush}"/>
</Grid>
</Page>
ExampleResourceDictionary.xaml
<ResourceDictionary>
<SolidColorBrush x:Key="TextBrush" Color="#FF3F42CC"/>
<!--This ResourceDictionary contains many other resources that
are used in the app, but are not needed during startup.-->
</ResourceDictionary>
Если вы используете ресурс на многих страницах во всём приложении, то хранение его в App.xaml является хорошей практикой и помогает избежать дублирования. Но App.xaml анализируется при запуске приложения, поэтому любой ресурс, используемый только на одной странице, если эта страница не является начальной, следует поместить в локальные ресурсы страницы. В этом примере показано приложение.xaml , содержащее ресурсы, которые используются только одной страницей, которая не является начальной. Это не обязательно увеличивает время запуска.
App.xaml
<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Application ...>
<Application.Resources>
<SolidColorBrush x:Key="DefaultAppTextBrush" Color="#FF3F42CC"/>
<SolidColorBrush x:Key="InitialPageTextBrush" Color="#FF3F42CC"/>
<SolidColorBrush x:Key="SecondPageTextBrush" Color="#FF3F42CC"/>
<SolidColorBrush x:Key="ThirdPageTextBrush" Color="#FF3F42CC"/>
</Application.Resources>
</Application>
InitialPage.xaml
<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Page x:Class="ExampleNamespace.InitialPage" ...>
<StackPanel>
<TextBox Foreground="{StaticResource InitialPageTextBrush}"/>
</StackPanel>
</Page>
SecondPage.xaml
<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Page x:Class="ExampleNamespace.SecondPage" ...>
<StackPanel>
<Button Content="Submit" Foreground="{StaticResource SecondPageTextBrush}"/>
</StackPanel>
</Page>
Чтобы сделать этот пример более эффективным, перейдите SecondPageTextBrush в SecondPage.xaml и перейдите ThirdPageTextBrush в ThirdPage.xaml.
InitialPageTextBrush может оставаться в App.xaml , так как ресурсы приложения должны анализироваться при запуске приложения в любом случае.
Объедините несколько кистей, которые выглядят одинаково, в один ресурс.
Платформа XAML пытается кэшировать часто используемые объекты, чтобы их можно было повторно использовать как можно чаще. Но XAML не может легко определить, совпадает ли кисть, объявленная в одной части разметки, с кистью, объявленной в другой. В этом примере мы демонстрируем использование SolidColorBrush, однако ситуация чаще возникает и более существенна, когда используется GradientBrush. Также проверьте наличие кистей, использующих предопределенные цвета; например, "Orange" и "#FFFFA500" один и тот же цвет.
Неэффективно
<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Page ... >
<StackPanel>
<TextBlock>
<TextBlock.Foreground>
<SolidColorBrush Color="#FFFFA500"/>
</TextBlock.Foreground>
</TextBlock>
<Button Content="Submit">
<Button.Foreground>
<SolidColorBrush Color="#FFFFA500"/>
</Button.Foreground>
</Button>
</StackPanel>
</Page>
Чтобы исправить дублирование, определите кисть как ресурс. Если элементы управления на других страницах используют ту же кисть, переместите его в App.xaml.
Эффективным
<Page ... >
<Page.Resources>
<SolidColorBrush x:Key="BrandBrush" Color="#FFFFA500"/>
</Page.Resources>
<StackPanel>
<TextBlock Foreground="{StaticResource BrandBrush}" />
<Button Content="Submit" Foreground="{StaticResource BrandBrush}" />
</StackPanel>
</Page>
Свести к минимуму переполнение
Перезарисовка происходит, когда несколько объектов рисуется в одном и том же пикселях экрана. Обратите внимание, что иногда существует компромисс между этим руководством и желанием свести к минимуму количество элементов.
Используйте DebugSettings.IsOverdrawHeatMapEnabled в качестве визуальной диагностики. Вы можете обнаружить объекты, которые неожиданно оказались в сцене и вы не знали об их присутствии.
Прозрачные или скрытые элементы
Если элемент не отображается, так как он прозрачный или скрытый за другими элементами, и он не вносит вклад в макет, а затем удалите его. Если элемент не отображается в исходном визуальном состоянии, но отображается в других визуальных состояниях, используйте x:Load для управления состоянием элемента или задайте значение Видимости для свернутого элемента и измените значение "Видимый " в соответствующих состояниях. Существуют исключения для этой эвристики: как правило, значение свойства в большинстве визуальных состояний лучше всего задать локально на элементе.
Составные элементы
Используйте составной элемент вместо слоя нескольких элементов для создания эффекта. В этом примере результатом является двухтонированная фигура, в которой верхняя половина черная благодаря фону Grid, а нижняя половина серая за счет полупрозрачного белого Rectangle, альфа-смешанного поверх черного фона Grid. Здесь заполняется 150% пикселей, необходимых для достижения результата.
Неэффективно
<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Grid Background="Black">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Rectangle Grid.Row="1" Fill="White" Opacity=".5"/>
</Grid>
Эффективным
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Rectangle Fill="Black"/>
<Rectangle Grid.Row="1" Fill="#FF7F7F7F"/>
</Grid>
Панели макета
Панель макета может иметь два назначения: цвет области и размещение дочерних элементов. Если элемент дальше в z-порядке уже закрашивает область, то панель макета перед ним не должна рисовать эту область; вместо этого она может сосредоточиться на размещении своих дочерних элементов. Приведем пример.
Неэффективно
<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<GridView Background="Blue">
<GridView.ItemTemplate>
<DataTemplate>
<Grid Background="Blue"/>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
Эффективным
<GridView Background="Blue">
<GridView.ItemTemplate>
<DataTemplate>
<Grid/>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
Если сетка должна быть проверена, задайте для нее фоновое значение Transparent .
Границы
Используйте элемент Border для рисования границы вокруг объекта. В этом примере сетка используется в качестве удельной границы вокруг текстового поля. Но все пиксели в центральной ячейке перерисовываются.
Неэффективно
<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Grid Background="Blue" Width="300" Height="45">
<Grid.RowDefinitions>
<RowDefinition Height="5"/>
<RowDefinition/>
<RowDefinition Height="5"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="5"/>
<ColumnDefinition/>
<ColumnDefinition Width="5"/>
</Grid.ColumnDefinitions>
<TextBox Grid.Row="1" Grid.Column="1"></TextBox>
</Grid>
Эффективным
<Border BorderBrush="Blue" BorderThickness="5" Width="300" Height="45">
<TextBox/>
</Border>
Отступы
Помните о полях. Два соседних элемента могут перекрываться, возможно, случайно, если отрицательные отступы выходят за пределы границ отрисовки другого элемента и вызывают перерисовку.
Кэширование статического содержимого
Другой источник перезарисовки — это форма, созданная из множества перекрывающихся элементов. Если для параметра CacheMode установлено значение BitmapCache на элементе UIElement, который содержит составной объект, платформа отрисовывает этот элемент в виде растрового изображения один раз и затем использует это изображение для каждого кадра вместо повторной отрисовки.
Неэффективно
<Canvas Background="White">
<Ellipse Height="40" Width="40" Fill="Blue"/>
<Ellipse Canvas.Left="21" Height="40" Width="40" Fill="Blue"/>
<Ellipse Canvas.Top="13" Canvas.Left="10" Height="40" Width="40" Fill="Blue"/>
</Canvas>
Приведенное выше изображение является результатом, но вот карта переопределенных регионов. Темный красный цвет указывает на более высокие объемы передравов.
Эффективным
<Canvas Background="White" CacheMode="BitmapCache">
<Ellipse Height="40" Width="40" Fill="Blue"/>
<Ellipse Canvas.Left="21" Height="40" Width="40" Fill="Blue"/>
<Ellipse Canvas.Top="13" Canvas.Left="10" Height="40" Width="40" Fill="Blue"/>
</Canvas>
Обратите внимание на использование CacheMode. Не используйте этот метод, если какие-либо из под-фигур анимируются, так как битмап-кэш, скорее всего, потребуется перегенерировать каждый кадр, что сведет на нет всю задумку.
Использование скомпилированных выходных данных XAML
Пакет SDK для приложений Windows компилирует XAML в двоичное представление в рамках сборки, что позволяет избежать затрат на анализ текста во время выполнения. Скомпилированный формат также оптимизирует загрузку и создание дерева для распространенных типов XAML, таких как визуальные состояния, словари ресурсов и стили.
Встроенные элементы управления и словари WinUI уже пользуются этим конвейером. Для собственного приложения WinUI сохраните обычные шаги компиляции XAML, чтобы созданные выходные данные компиляции разметки были доступны во время выполнения.
Связанные статьи
Windows developer