Nota
O acesso a esta página requer autorização. Pode tentar iniciar sessão ou alterar os diretórios.
O acesso a esta página requer autorização. Pode tentar alterar os diretórios.
Analisar a marcação XAML para construir objetos em memória é demorado para uma interface complexa. Aqui estão algumas coisas que pode fazer para melhorar o tempo de análise e carregamento da sua marcação XAML e a eficiência de memória da sua aplicação WinUI.
No arranque da aplicação, limite a marcação XAML carregada apenas ao que precisa para a sua interface inicial. Verifique a marcação na sua página inicial, incluindo os recursos da página, e confirme que não está a carregar elementos extra que não são necessários imediatamente. Estes elementos podem provir de várias fontes, como dicionários de recursos, elementos inicialmente recolhidos e elementos sobrepostos a outros elementos.
Otimizar o seu XAML para eficiência exige fazer concessões; nem sempre há uma solução única para todas as situações. Aqui, analisamos alguns problemas comuns e fornecemos orientações que pode usar para fazer as trocas certas para a sua aplicação WinUI.
Minimizar o número de elementos
Embora a plataforma XAML seja capaz de exibir um grande número de elementos, pode fazer com que a sua aplicação seja disposta e renderizada mais rapidamente usando o menor número possível de elementos necessários para alcançar os visuais desejados.
As escolhas que faz na forma como dispõe os controlos da interface afetam o número de elementos criados quando a aplicação começa. Para informações mais detalhadas sobre otimização do layout, consulte Otimizar o seu layout XAML.
A contagem de elementos é extremamente importante nos templates de dados porque cada elemento é criado novamente para cada elemento de dados. Para informações sobre como reduzir o número de elementos numa lista ou grelha, veja Redução de elementos por item no artigo Otimizar ListView e desempenho do GridView para o WinUI .
Aqui, analisamos outras formas de reduzir o número de elementos que a sua aplicação tem de carregar no arranque.
Adiar a criação de itens
Se a sua marcação XAML contiver elementos que não mostra de imediato, pode adiar o carregamento desses elementos até serem mostrados. Por exemplo, pode atrasar a criação de conteúdo não visível, como um separador secundário numa interface do tipo separador. Ou, pode mostrar os itens numa vista em grelha como padrão, mas oferecer uma opção para o utilizador visualizar os dados numa lista em vez disso. Podes adiar o carregamento da lista até ser necessário.
Use o atributo x:Load em vez da propriedade Visibilidade para controlar quando um elemento é mostrado. Quando a visibilidade de um elemento está definida como Colapsado, é ignorado durante o ciclo de renderização, mas ainda assim paga-se os custos de instância do objeto em memória. Quando usas x:Load em vez disso, a framework não cria a instância do objeto até ser necessária, pelo que os custos de memória são ainda mais baixos. A desvantagem é que se paga uma pequena sobrecarga de memória (aproximadamente 600 bytes) quando a interface não está carregada.
Observação
No Windows App SDK, x:Load é o padrão recomendado de carregamento diferido para conteúdo XAML que não é necessário imediatamente.
Os exemplos seguintes mostram a diferença no número de elementos e no uso de memória quando diferentes técnicas são usadas para ocultar elementos da interface. Uma Vista de Lista e uma Vista de Grade contendo itens idênticos são colocadas na Grelha raiz da página. O ListView não é visível, mas o GridView é mostrado. O XAML em cada um destes exemplos produz a mesma interface no ecrã. Usa ferramentas de perfilagem e desempenho para verificar o número de elementos e o uso de memória na tua aplicação.
Opção 1 - Ineficiente
Aqui, o ListView é carregado, mas não é visível porque o seu Width é 0. O ListView e cada um dos seus elementos filhos são criados na árvore visual e carregados na memória.
<!-- 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>
Árvore visual ao vivo com o ListView carregado. A contagem total de elementos para a página é 89.
O ListView e os seus filhos são carregados na memória.
Opção 2 - Melhor
Aqui, o ListView's Visibility está definido para Collapsed (o outro XAML é idêntico ao original). O ListView é criado na árvore visual, mas os seus elementos subordinados não são. No entanto, continuam a ser carregados na memória, pelo que o uso da memória é idêntico ao exemplo anterior.
<ListView x:Name="List1" Visibility="Collapsed">
Árvore visual ao vivo com o ListView colapsado. O total de elementos da página é 46.
O ListView e os seus filhos são carregados na memória.
Opção 3 - Mais eficiente
Aqui, o ListView tem o atributo x:Load definido para False (o outro XAML é idêntico ao original). O ListView não é criado na árvore visual nem carregado na memória no arranque.
<ListView x:Name="List1" Visibility="Collapsed" x:Load="False">
Árvore visual ao vivo com o ListView não carregado. O número total de elementos para a página é de 45.
O ListView e os seus componentes não são carregados na memória.
Observação
A contagem de elementos e o uso de memória nestes exemplos são muito pequenos e são mostrados apenas para demonstrar o conceito. Nestes exemplos, a sobrecarga de usar x:Load é maior do que a poupança de memória, pelo que a aplicação não beneficiaria. Deves usar ferramentas de perfil na tua aplicação para perceber se o carregamento diferido ajuda.
Usar propriedades do painel de layout
Os painéis de layout têm uma propriedade Background, por isso não há necessidade de colocar um Retângulo à frente do painel apenas para colorir o painel.
Ineficiente
<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Grid>
<Rectangle Fill="Black"/>
</Grid>
Eficiente
<Grid Background="Black"/>
Os painéis de layout também têm propriedades de borda incorporadas, por isso não precisas de colocar um elemento de borda à volta de um painel de layout. Consulte Otimizar o seu layout XAML para mais informações e exemplos.
Use imagens em vez de elementos baseados em vetores
Se reutilizares o mesmo elemento baseado em vetores vezes suficientes, torna-se mais eficiente usar um elemento Imagem . Os elementos baseados em vetores podem ser mais caros porque a CPU tem de criar cada elemento individualmente. O ficheiro de imagem só precisa de ser decodificado uma vez.
Otimizar recursos e dicionários de recursos
Normalmente usas dicionários de recursos para armazenar, a um nível algo global, recursos que queres consultar em vários locais da tua aplicação. Por exemplo, estilos, pincéis, moldes, e assim por diante.
Em geral, o ResourceDictionary está otimizado para evitar instanciar recursos até que sejam solicitados. Mas há situações que deves evitar para que os recursos não sejam instanciados desnecessariamente.
Recursos com x:Nome
Utilize o atributo x:Key para referenciar os seus recursos. Qualquer recurso com o atributo x:Name não beneficiará da otimização da plataforma; em vez disso, é instanciado assim que o ResourceDictionary é criado. Isto acontece porque o x:Name indica à plataforma que a sua aplicação precisa de acesso em campo a esse recurso, por isso a plataforma precisa de criar algo para manter uma referência a ele.
Dicionário de Recursos num Controle de Utilizador
Um Dicionário de Recursos definido dentro de um Controlo de Utilizador acarreta uma penalização. A plataforma cria uma cópia desse ResourceDictionary para cada instância do UserControl. Se tiveres um UserControl que é muito usado, então move o ResourceDictionary para fora do UserControl e coloca-o ao nível da página.
Recursos e Âmbito do Dicionário de Recursos
Se uma página faz referência a um controlo de utilizador ou a um recurso definido num ficheiro diferente, então o framework analisa esse ficheiro também.
Aqui, como o InitialPage.xaml usa um recurso do ExampleResourceDictionary.xaml, todo o ExampleResourceDictionary.xaml deve ser analisado no arranque.
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>
DicionárioDeRecursosExemplo.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>
Se usares um recurso em muitas páginas da tua aplicação, então guardá-lo no App.xaml é uma boa prática e evita duplicações. Mas o App.xaml é analisado no arranque da aplicação, por isso qualquer recurso que seja usado apenas numa página, a menos que essa página seja a página inicial, deve ser colocado nos recursos locais da página. Este exemplo mostra o App.xaml contendo recursos que são usados apenas por uma página que não é a página inicial. Isto aumenta desnecessariamente o tempo de arranque.
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>
Para tornar este exemplo mais eficiente, passe SecondPageTextBrush para SecondPage.xaml e depois ThirdPageTextBrush para ThirdPage.xaml.
InitialPageTextBrush pode permanecer em App.xaml porque os recursos da aplicação têm de ser analisados no arranque da aplicação em qualquer caso.
Consolide vários pincéis que tenham o mesmo aspeto num único recurso
A plataforma XAML tenta armazenar em cache objetos frequentemente usados para que possam ser reutilizados o mais frequentemente possível. Mas o XAML não consegue perceber facilmente se um pincel declarado numa parte de marcação é igual a um pincel declarado noutra. O exemplo aqui usa o SolidColorBrush para demonstrar, mas o caso é mais provável e mais importante com o GradientBrush. Verifique também pincéis que usem cores pré-definidas; por exemplo, "Orange" e "#FFFFA500" são da mesma cor.
Ineficiente
<!-- 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>
Para corrigir a duplicação, defina o pincel como um recurso. Se os controlos noutras páginas usarem o mesmo pincel, move-o para App.xaml.
Eficiente
<Page ... >
<Page.Resources>
<SolidColorBrush x:Key="BrandBrush" Color="#FFFFA500"/>
</Page.Resources>
<StackPanel>
<TextBlock Foreground="{StaticResource BrandBrush}" />
<Button Content="Submit" Foreground="{StaticResource BrandBrush}" />
</StackPanel>
</Page>
Minimizar o desenho excessivo
O sobredesenho ocorre quando mais do que um objeto é desenhado nos mesmos píxeis do ecrã. Note-se que por vezes existe um conflito entre esta orientação e o desejo de minimizar o número de elementos.
Utilize DebugSettings.IsOverdrawHeatMapEnabled como uma ferramenta de diagnóstico visual. Podes encontrar objetos a serem desenhados que não sabias que estavam na cena.
Elementos transparentes ou ocultos
Se um elemento não for visível porque é transparente ou está escondido atrás de outros elementos, e não estiver a contribuir para o layout, então apague-o. Se o elemento não for visível no estado visual inicial, mas for visível noutros estados visuais, então use x:Load para controlar o seu estado ou defina a Visibilidade para Colapsado no próprio elemento e altere o valor para Visível nos estados apropriados. Haverá exceções a esta heurística: em geral, o valor que uma propriedade tem na maioria dos estados visuais é melhor definido localmente no elemento.
Elementos compostos
Use um elemento composto em vez de sobrepor vários elementos para criar um efeito. Neste exemplo, o resultado é uma forma de duas tonalidades onde a metade superior é preta devido ao fundo da Grelha e a metade inferior é cinzenta derivada do Retângulo branco semi-transparente fundido em alfa sobre o fundo preto da Grelha. Aqui, 150% dos píxeis necessários para alcançar o resultado estão a ser preenchidos.
Ineficiente
<!-- 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>
Eficiente
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Rectangle Fill="Black"/>
<Rectangle Grid.Row="1" Fill="#FF7F7F7F"/>
</Grid>
Painéis de layout
Um painel de disposição pode ter dois propósitos: colorir uma área e organizar elementos filhos. Se um elemento mais atrás na ordem z já estiver a colorir uma área, então um painel de layout à frente não precisa de pintar essa área; em vez disso, pode focar-se em apresentar os seus filhos. Eis um exemplo.
Ineficiente
<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<GridView Background="Blue">
<GridView.ItemTemplate>
<DataTemplate>
<Grid Background="Blue"/>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
Eficiente
<GridView Background="Blue">
<GridView.ItemTemplate>
<DataTemplate>
<Grid/>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
Se a Grelha tiver de ser detectável ao toque, então defina o valor de fundo como Transparent.
Fronteiras
Usa um elemento Borda para desenhar uma borda à volta de um objeto. Neste exemplo, uma Grelha é usada como uma borda improvisada em torno de uma Caixa de Texto. Mas todos os píxeis na célula central estão sobredesenhados.
Ineficiente
<!-- 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>
Eficiente
<Border BorderBrush="Blue" BorderThickness="5" Width="300" Height="45">
<TextBox/>
</Border>
Margens
Esteja atento às margens. Dois elementos vizinhos irão sobrepor-se, possivelmente acidentalmente, se as margens negativas se estenderem dentro dos limites de renderização de um elemento adjacente e causarem sobreposição indesejada.
Cache de conteúdo estático
Outra fonte de overdrawing é uma forma composta por muitos elementos sobrepostos. Se definires o Modo de Cache para BitmapCache no UIElement que contém a forma composta, então a plataforma renderiza o elemento para um bitmap uma vez e usa esse bitmap em cada frame em vez de fazer um sobreesboço.
Ineficiente
<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>
A imagem acima é o resultado, mas aqui está um mapa das regiões sobredesenhadas. Vermelho mais escuro indica uma maior sobrecarga de renderização.
Eficiente
<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>
Note o uso do Modo Cache. Não uses esta técnica se alguma das sub-formas animar, porque o cache bitmap provavelmente terá de ser regenerado a cada frame, anulando o propósito.
Usar saída XAML compilada
O Windows App SDK compila XAML numa representação binária como parte da compilação, o que evita custos de análise de texto em tempo de execução. O formato compilado também otimiza a criação de cargas e árvores para tipos XAML comuns, como estados visuais, dicionários de recursos e estilos.
Os controlos e dicionários incorporados do WinUI já beneficiam deste pipeline. Para a sua própria aplicação WinUI, mantenha ativados os passos normais de compilação XAML para que o resultado da compilação do markup gerado esteja disponível em tempo de execução.
Artigos relacionados
Windows developer