Nota
O acceso a esta páxina require autorización. Pode tentar iniciar sesión ou modificar os directorios.
O acceso a esta páxina require autorización. Pode tentar modificar os directorios.
El análisis del marcado XAML para construir objetos en memoria consume mucho tiempo para una interfaz de usuario compleja. Estas son algunas cosas que puedes hacer para mejorar el tiempo de análisis y carga del marcado XAML y la eficacia de memoria de la aplicación WinUI.
En el inicio de la aplicación, limite el marcado XAML que se carga solo a lo que necesita para la interfaz de usuario inicial. Examine el marcado en la página inicial, incluidos los recursos de página, y confirme que no está cargando elementos adicionales que no son necesarios inmediatamente. Estos elementos pueden provenir de una variedad de orígenes, como diccionarios de recursos, elementos que se contraen inicialmente y elementos dibujados sobre otros elementos.
La optimización de XAML para mejorar la eficiencia requiere tomar decisiones de compromiso; no siempre hay una única solución para cada situación. Aquí, examinamos algunos problemas comunes y proporcionamos directrices que puede utilizar para hacer los compromisos adecuados para su aplicación WinUI.
Minimizar el recuento de elementos
Aunque la plataforma XAML es capaz de mostrar un gran número de elementos, puedes hacer que la aplicación se publique y represente más rápido mediante el menor número de elementos necesarios para lograr los objetos visuales que quieras.
Las opciones que tome en la forma de diseñar los controles de interfaz de usuario afectan al número de elementos de interfaz de usuario que se crean cuando se inicia la aplicación. Para obtener información más detallada sobre cómo optimizar el diseño, consulta Optimizar el diseño XAML.
El recuento de elementos es extremadamente importante en las plantillas de datos porque cada elemento se crea de nuevo para cada elemento de datos. Para obtener información sobre cómo reducir el recuento de elementos en una lista o cuadrícula, consulta Reducción de elementos por elemento en el artículo Optimizar ListView y GridView para WinUI .
Aquí, veremos otras formas de reducir el número de elementos que la aplicación tiene que cargar al iniciarse.
Aplazar la creación de elementos
Si el marcado XAML contiene elementos que no se muestran inmediatamente, puedes aplazar la carga de esos elementos hasta que se muestren. Por ejemplo, puede retrasar la creación de contenido no visible, como una pestaña secundaria en una interfaz de usuario similar a pestañas. O bien, puede mostrar elementos en una vista de cuadrícula de forma predeterminada, pero proporcionar una opción para que el usuario vea los datos en una lista en su lugar. Puede retrasar la carga de la lista hasta que sea necesario.
Use el atributo x:Load en lugar de la propiedad Visibility para controlar cuándo se muestra un elemento. Cuando la visibilidad de un elemento se establece en Collapsed, se excluye del proceso de renderizado, pero aún se siguen incurriendo en los costos de la instancia de objeto en la memoria. Cuando se usa x:Load en su lugar, el marco no crea la instancia del objeto hasta que sea necesario, por lo que los costos de memoria son incluso menores. El inconveniente es que se paga una sobrecarga de memoria pequeña (aproximadamente 600 bytes) cuando no se carga la interfaz de usuario.
Nota:
En windows App SDK, x:Load es el patrón de carga diferida recomendado para el contenido XAML que no es necesario inmediatamente.
En los ejemplos siguientes se muestra la diferencia en el recuento de elementos y el uso de memoria cuando se usan diferentes técnicas para ocultar elementos de la interfaz de usuario. ListView y GridView que contienen elementos idénticos se colocan en la cuadrícula raíz de una página. ListView no está visible, pero se muestra GridView. El CÓDIGO XAML de cada uno de estos ejemplos genera la misma interfaz de usuario en la pantalla. Use herramientas para generar perfiles y rendimiento para comprobar el recuento de elementos y el uso de memoria en la aplicación.
Opción 1: ineficaz
Aquí, se carga el ListView, pero no está visible porque su Width es 0. ListView y cada uno de sus elementos secundarios se crean en el árbol visual y se cargan en la memoria.
<!-- 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>
Árbol visual dinámico con listView cargado. El número total de elementos de la página es 89.
ListView y sus elementos secundarios se cargan en la memoria.
Opción 2: mejor
Aquí, el control ListView Visibility se configura como Collapsed (el XAML restante es idéntico al original). ListView se crea en el árbol visual, pero sus elementos secundarios no. Sin embargo, todavía se cargan en la memoria, por lo que el uso de memoria es idéntico al ejemplo anterior.
<ListView x:Name="List1" Visibility="Collapsed">
Árbol visual en tiempo real con el ListView contraído. El número total de elementos de la página es 46.
ListView y sus elementos secundarios se cargan en la memoria.
Opción 3: más eficaz
Aquí, listView tiene el atributo x:Load establecido en False (el otro XAML es idéntico al original). ListView no se crea en el árbol visual ni se carga en la memoria en el inicio.
<ListView x:Name="List1" Visibility="Collapsed" x:Load="False">
Árbol visual dinámico con listView no cargado. El número total de elementos de la página es 45.
ListView y sus elementos secundarios no se cargan en la memoria.
Nota:
Los recuentos de elementos y el uso de memoria en estos ejemplos son muy pequeños y solo se muestran para demostrar el concepto. En estos ejemplos, la sobrecarga de usar x:Load es mayor que el ahorro de memoria, por lo que la aplicación no se beneficiaría. Debe usar herramientas de generación de perfiles en la aplicación para determinar si la carga diferida ayudará.
Usar propiedades del panel de diseño
Los paneles de diseño tienen una propiedad Background , por lo que no es necesario colocar un rectángulo delante de un panel solo para colorearlo.
Ineficaz
<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<Grid>
<Rectangle Fill="Black"/>
</Grid>
Eficaz
<Grid Background="Black"/>
Los paneles de diseño también tienen propiedades de borde integradas, por lo que no es necesario colocar un elemento Border alrededor de un panel de diseño. Consulta Optimizar el diseño XAML para obtener más información y ejemplos.
Uso de imágenes en lugar de elementos basados en vectores
Si reutiliza el mismo elemento basado en vectores suficientes veces, resulta más eficaz usar un elemento Image en su lugar. Los elementos basados en vectores pueden ser más caros porque la CPU debe crear cada elemento individual por separado. El archivo de imagen solo debe descodificarse una vez.
Optimización de recursos y diccionarios de recursos
Normalmente, usa diccionarios de recursos para almacenar, en un nivel algo global, los recursos a los que quiere hacer referencia en varios lugares de la aplicación. Por ejemplo, estilos, pinceles, plantillas, etc.
En general, ResourceDictionary está optimizado para evitar instanciar recursos hasta que se soliciten. Pero hay situaciones que debe evitar para que los recursos no se instancien innecesariamente.
Recursos con x:Name
Use el atributo x:Key para hacer referencia a los recursos. Cualquier recurso con el atributo x:Name no aprovechará la optimización de la plataforma; en su lugar, se instancia tan pronto como se crea el ResourceDictionary. Esto sucede porque x:Name indica a la plataforma que la aplicación necesita acceso de campo a este recurso, por lo que la plataforma debe crear algo para contener una referencia a él.
ResourceDictionary en un ControlDeUsuario
Un ResourceDictionary definido dentro de un UserControl conlleva una penalización. La plataforma crea una copia de este tipo de ResourceDictionary para cada instancia de un UserControl. Si tiene un UserControl que se usa mucho, mueva ResourceDictionary fuera de UserControl y colóquelo en el nivel de página.
Alcance de recursos y ResourceDictionary
Si una página hace referencia a un control de usuario o a un recurso definido en un archivo diferente, el marco también analiza ese archivo.
En este caso, dado que InitialPage.xaml usa un recurso de ExampleResourceDictionary.xaml, el conjunto de ExampleResourceDictionary.xaml debe analizarse al iniciarse.
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>
Si usas un recurso en muchas páginas en toda la aplicación, almacenarlo en App.xaml es un procedimiento recomendado y evita la duplicación. Pero App.xaml se analiza en el inicio de la aplicación, por lo que cualquier recurso que se use en una sola página, a menos que esa página sea la página inicial, debe colocarse en los recursos locales de la página. En este ejemplo se muestra App.xaml que contiene recursos que solo usan una página que no es la página inicial. Esto aumenta innecesariamente el tiempo de inicio.
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 que este ejemplo sea más eficaz, mueva SecondPageTextBrush a SecondPage.xaml y mueva ThirdPageTextBrush a ThirdPage.xaml.
InitialPageTextBrush puede permanecer en App.xaml porque los recursos de la aplicación deben analizarse en el inicio de la aplicación en cualquier caso.
Consolidar varios pinceles que parecen iguales en un recurso
La plataforma XAML intenta almacenar en caché objetos usados habitualmente para que se puedan reutilizar con la mayor frecuencia posible. Pero XAML no puede saber fácilmente si un pincel declarado en un fragmento de marcado es el mismo que un pincel declarado en otro. En el ejemplo siguiente se usa SolidColorBrush para demostrar, pero el caso es más probable y más importante con GradientBrush. Compruebe también los pinceles que usan colores predefinidos; por ejemplo, "Orange" y "#FFFFA500" son del mismo color.
Ineficaz
<!-- 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 corregir la duplicación, defina el pincel como un recurso. Si los controles de otras páginas usan el mismo pincel, muévalo a App.xaml.
Eficaz
<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 el sobredibujo
El sobredibujo se produce cuando se dibujan más de un objeto en los mismos píxeles de la pantalla. Tenga en cuenta que a veces hay un equilibrio entre esta guía y el deseo de minimizar el recuento de elementos.
Use DebugSettings.IsOverdrawHeatMapEnabled para el diagnóstico visual. Es posible que encuentre objetos dibujados que no sabía que estaban en la escena.
Elementos transparentes o ocultos
Si un elemento no está visible porque es transparente o oculto detrás de otros elementos, y no contribuye al diseño, elimínelo. Si el elemento no está visible en el estado visual inicial, pero está visible en otros estados visuales, use x:Load para controlar su estado o establezca Visibility en Collapsed en el propio elemento y cambie el valor a Visible en los estados adecuados. Habrá excepciones a esta heurística: en general, el valor que tiene una propiedad en la mayoría de los estados visuales se establece mejor localmente en el elemento.
Elementos compuestos
Use un elemento compuesto en lugar de superponer varios elementos para crear un efecto. En este ejemplo, el resultado es una forma de dos tonos en la que la mitad superior es negra desde el fondo de la Cuadrícula y la mitad inferior es gris del Rectángulo blanco semitransparente con mezcla alfa sobre el fondo negro de la Cuadrícula. Aquí se rellenan 150% de los píxeles necesarios para lograr el resultado.
Ineficaz
<!-- 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>
Eficaz
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Rectangle Fill="Black"/>
<Rectangle Grid.Row="1" Fill="#FF7F7F7F"/>
</Grid>
Paneles de diseño
Un panel de disposición puede tener dos propósitos: colorear un área y organizar elementos secundarios. Si un elemento más atrás en orden z ya está coloreando un área, entonces un panel de diseño delante no necesita pintar esa área; en su lugar, puede centrarse en diseñar sus elementos secundarios. Aquí hay un ejemplo.
Ineficaz
<!-- NOTE: EXAMPLE OF INEFFICIENT CODE; DO NOT COPY-PASTE. -->
<GridView Background="Blue">
<GridView.ItemTemplate>
<DataTemplate>
<Grid Background="Blue"/>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
Eficaz
<GridView Background="Blue">
<GridView.ItemTemplate>
<DataTemplate>
<Grid/>
</DataTemplate>
</GridView.ItemTemplate>
</GridView>
Si la cuadrícula tiene que ser comprobable, establezca un valor de fondo en Transparent él.
Bordes
Utilice un elemento Border para dibujar un borde alrededor de un objeto . En este ejemplo, se utiliza una Grid como un borde improvisado alrededor de un TextBox. Pero todos los píxeles de la celda central están sobredibujados.
Ineficaz
<!-- 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>
Eficaz
<Border BorderBrush="Blue" BorderThickness="5" Width="300" Height="45">
<TextBox/>
</Border>
Márgenes
Tenga en cuenta los márgenes. Dos elementos vecinos se superponen, posiblemente accidentalmente, si los márgenes negativos se extienden a los límites de representación de otro elemento y provocan un exceso de dibujo.
Almacenamiento en caché de contenido estático
Otra fuente de sobredibujo es una forma hecha de muchos elementos superpuestos. Si estableces CacheMode en BitmapCache en el UIElement que contiene la forma compuesta, la plataforma representa el elemento como un mapa de bits una vez y usa ese mapa de bits en cada fotograma en lugar de sobredibujar.
Ineficaz
<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>
La imagen anterior es el resultado, pero aquí hay un mapa de las regiones sobredibujadas. El rojo oscuro indica mayores cantidades de sobredibujo.
Eficaz
<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>
Anote el uso de CacheMode. No use esta técnica si alguna de las subformas geométricas se anima porque es probable que la memoria caché del mapa de bits tenga que volver a generarse cada fotograma, lo cual anula el propósito.
Uso de la salida XAML compilada
Windows App SDK compila XAML en una representación binaria como parte de la compilación, lo que evita los costos de análisis de texto en tiempo de ejecución. El formato compilado también optimiza la carga y la creación de árboles para tipos XAML comunes, como estados visuales, diccionarios de recursos y estilos.
Los controles y diccionarios WinUI integrados ya se benefician de esta canalización. En el caso de su propia aplicación WinUI, mantenga habilitados los pasos de compilación XAML normales para que la salida de compilación de marcado generada esté disponible en tiempo de ejecución.
Artículos relacionados
Windows developer