Optimiza tu diseño XAML

API importantes

El diseño es el proceso de definir la estructura visual de la interfaz de usuario. El mecanismo principal para describir el diseño en XAML son los paneles, que son objetos contenedores que te permiten colocar y organizar los elementos de interfaz de usuario dentro de ellos. El diseño puede ser una parte costosa de una aplicación XAML; tanto en la sobrecarga de memoria como en el uso de la CPU. A continuación te mostramos algunos sencillos pasos para mejorar el rendimiento de diseño de la aplicación XAML.

Reducir la estructura de diseño

La mayor mejora en el rendimiento de diseño proviene de simplificar la estructura jerárquica del árbol de los elementos de la interfaz de usuario. Los paneles existen en el árbol visual, pero son elementos estructurales, no elementos productores de píxeles como sería una clase Button o Rectangle. El hecho de simplificar el árbol reduciendo el número de elementos no productores de píxeles, proporciona, por lo general, un aumento significativo del rendimiento.

Muchas interfaces de usuario se implementan anidando paneles que dan como resultado árboles de paneles y elementos profundos y complejos. Anidar paneles es práctico pero, en muchos casos, la misma interfaz de usuario se puede lograr con un panel simple más complejo. Usar un panel simple proporciona un mejor rendimiento.

Cuándo reducir la estructura del diseño

Reducir la estructura del diseño de una manera trivial como, por ejemplo, reducir un panel anidado desde la página de nivel superior, no tiene un efecto perceptible.

La mayor mejora que puedes conseguir en el rendimiento, es mediante la reducción de la estructura de diseño que se repite en la interfaz de usuario, como sucede en la clase ListView o GridView. Estos elementos ItemsControl usan una clase DataTemplate, que define un subárbol de elementos de interfaz de usuario del que se crean instancias varias veces. Cuando el mismo subárbol se duplica varias veces en la aplicación, cualquier mejora en el rendimiento de ese subárbol tiene un efecto multiplicativo en el rendimiento general de la aplicación.

Ejemplos

Ten en cuenta la siguiente interfaz de usuario.

Form layout example

Estos ejemplos muestran 3 formas de implementar la misma interfaz de usuario. Cada opción de implementación tiene como resultado píxeles casi idénticos en la pantalla, pero varía considerablemente en los detalles de implementación.

Opción 1: elementos StackPanel anidados

Aunque este es el modelo más sencillo, usa 5 elementos de panel y produce una sobrecarga significativa.

  <StackPanel>
  <TextBlock Text="Options:" />
  <StackPanel Orientation="Horizontal">
      <CheckBox Content="Power User" />
      <CheckBox Content="Admin" Margin="20,0,0,0" />
  </StackPanel>
  <TextBlock Text="Basic information:" />
  <StackPanel Orientation="Horizontal">
      <TextBlock Text="Name:" Width="75" />
      <TextBox Width="200" />
  </StackPanel>
  <StackPanel Orientation="Horizontal">
      <TextBlock Text="Email:" Width="75" />
      <TextBox Width="200" />
  </StackPanel>
  <StackPanel Orientation="Horizontal">
      <TextBlock Text="Password:" Width="75" />
      <TextBox Width="200" />
  </StackPanel>
  <Button Content="Save" />
</StackPanel>

Opción 2: una clase Grid simple

La clase Grid agrega cierta complejidad, pero solo usa un elemento de panel simple.

<Grid>
  <Grid.RowDefinitions>
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
      <RowDefinition Height="Auto" />
  </Grid.RowDefinitions>
  <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto" />
      <ColumnDefinition Width="Auto" />
  </Grid.ColumnDefinitions>
  <TextBlock Text="Options:" Grid.ColumnSpan="2" />
  <CheckBox Content="Power User" Grid.Row="1" Grid.ColumnSpan="2" />
  <CheckBox Content="Admin" Margin="150,0,0,0" Grid.Row="1" Grid.ColumnSpan="2" />
  <TextBlock Text="Basic information:" Grid.Row="2" Grid.ColumnSpan="2" />
  <TextBlock Text="Name:" Width="75" Grid.Row="3" />
  <TextBox Width="200" Grid.Row="3" Grid.Column="1" />
  <TextBlock Text="Email:" Width="75" Grid.Row="4" />
  <TextBox Width="200" Grid.Row="4" Grid.Column="1" />
  <TextBlock Text="Password:" Width="75" Grid.Row="5" />
  <TextBox Width="200" Grid.Row="5" Grid.Column="1" />
  <Button Content="Save" Grid.Row="6" />
</Grid>

Opción 3: una clase RelativePanel simple:

Este panel simple también resulta un poco más complejo que usar paneles anidados, pero puede ser más fácil de comprender y mantener que una clase Grid.

<RelativePanel>
  <TextBlock Text="Options:" x:Name="Options" />
  <CheckBox Content="Power User" x:Name="PowerUser" RelativePanel.Below="Options" />
  <CheckBox Content="Admin" Margin="20,0,0,0" 
            RelativePanel.RightOf="PowerUser" RelativePanel.Below="Options" />
  <TextBlock Text="Basic information:" x:Name="BasicInformation"
           RelativePanel.Below="PowerUser" />
  <TextBlock Text="Name:" RelativePanel.AlignVerticalCenterWith="NameBox" />
  <TextBox Width="200" Margin="75,0,0,0" x:Name="NameBox"               
           RelativePanel.Below="BasicInformation" />
  <TextBlock Text="Email:"  RelativePanel.AlignVerticalCenterWith="EmailBox" />
  <TextBox Width="200" Margin="75,0,0,0" x:Name="EmailBox"
           RelativePanel.Below="NameBox" />
  <TextBlock Text="Password:" RelativePanel.AlignVerticalCenterWith="PasswordBox" />
  <TextBox Width="200" Margin="75,0,0,0" x:Name="PasswordBox"
           RelativePanel.Below="EmailBox" />
  <Button Content="Save" RelativePanel.Below="PasswordBox" />
</RelativePanel>

Tal como se muestra en estos ejemplos, existen muchas formas de lograr la misma interfaz de usuario. Debes elegir teniendo en cuenta todos los inconvenientes, incluido el rendimiento, la legibilidad y el mantenimiento.

Usar cuadrículas de una sola celda para superponer la interfaz de usuario

Un requisito común de la interfaz de usuario es tener un diseño en el que los elementos se superpongan entre sí. Por lo general el espaciado interno, los márgenes, las alineaciones y las transformaciones se usan para ubicar los elementos de esta forma. El control Grid de XAML está optimizado para mejorar el rendimiento de diseño de los elementos que se superponen.

Importante: Para ver la mejora, use una clase Grid de una sola celda. No definas las propiedades RowDefinitions ni ColumnDefinitions.

Ejemplos

<Grid>
    <Ellipse Fill="Red" Width="200" Height="200" />
    <TextBlock Text="Test" 
               HorizontalAlignment="Center" 
               VerticalAlignment="Center" />
</Grid>

Text overlaid on a circle

<Grid Width="200" BorderBrush="Black" BorderThickness="1">
    <TextBlock Text="Test1" HorizontalAlignment="Left" />
    <TextBlock Text="Test2" HorizontalAlignment="Right" />
</Grid>

Two text blocks in a grid

Usar las propiedades de borde integradas de un panel

Los controles Grid, StackPanel, RelativePanel y ContentPresenter tienen propiedades de borde integradas que te permiten dibujar un borde alrededor de ellos sin tener que agregar un elemento Border adicional al XAML. Las nuevas propiedades que admiten el borde integrado son: BorderBrush, BorderThickness, CornerRadius y Padding. Cada una de ellas es una clase DependencyProperty, por lo que puedes usarlas con enlaces y animaciones. Además, están diseñadas para reemplazar completamente otro elemento Border.

Si tu interfaz de usuario tiene elementos Border alrededor de estos paneles, usa el borde integrado en su lugar, ya que guarda un elemento adicional en la estructura de diseño de la aplicación. Tal como se mencionó anteriormente, esto puede suponer un ahorro significativo, especialmente en el caso de interfaces de usuario repetidas.

Ejemplos

<RelativePanel BorderBrush="Red" BorderThickness="2" CornerRadius="10" Padding="12">
    <TextBox x:Name="textBox1" RelativePanel.AlignLeftWithPanel="True"/>
    <Button Content="Submit" RelativePanel.Below="textBox1"/>
</RelativePanel>

Usar eventos SizeChanged para responder a cambios de diseño

La clase FrameworkElement expone dos eventos similares para responder a los cambios de diseño: LayoutUpdated y SizeChanged. Puede que estés usando uno de estos eventos para recibir una notificación cuando cambia el tamaño de un elemento durante el diseño. La semántica de los dos eventos es diferente y hay aspectos importantes de rendimiento para elegir entre ellos.

Para lograr un buen rendimiento, SizeChanged es casi siempre la opción adecuada. SizeChanged tiene una semántica intuitiva. Se genera durante el diseño una vez que se actualiza el tamaño de la clase FrameworkElement.

LayoutUpdated también se genera durante el diseño, pero tiene una semántica global que se genera en todos los elementos cada vez que se actualiza cualquiera de ellos. Es habitual usarla para realizar solo procesamiento local en el controlador de eventos, en cuyo caso el código se ejecuta con más frecuencia de la necesaria. Usa LayoutUpdated solo si necesitas saber cuándo cambia la posición de un elemento sin que haya cambiado el tamaño (que es poco habitual).

Elegir entre los paneles

El rendimiento no suele ser algo a tener en cuenta al elegir entre los paneles individuales. Esa elección se suele realizar teniendo en cuenta qué panel proporciona el comportamiento de diseño más cercano a la interfaz de usuario que estás implementando. Por ejemplo, si estás eligiendo entre Grid, StackPanel y RelativePanel, debes elegir el panel que proporcione la asignación más cercana al modelo de la implementación que tenías en mente.

Cada panel XAML está optimizado para lograr un buen rendimiento y todos los paneles proporcionan un rendimiento similar para interfaces de usuario similares.