Problema de rendimiento en Silverlight

Es común que en nuestras vistas de una aplicación Silverlight queramos añadir un borde con un bonito DropShadowEffect que agrupe todo nuestro control o un conjunto de controles. Un posible XAML de ejemplo podría ser el siguiente (aunque de bonito no tenga mucho):

<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
        <Border Background="AntiqueWhite" >
            <Border.Effect>
                <DropShadowEffect BlurRadius="0.2" Direction="55" Color="Red"></DropShadowEffect>
            </Border.Effect>
            <StackPanel>
                <TextBlock>TextBlock 1</TextBlock>
                <TextBlock>TextBlock 2</TextBlock>
                <TextBlock>TextBlock 1</TextBlock>
                <TextBox></TextBox>
                <TextBox></TextBox>
                <TextBox></TextBox>
                <TextBox></TextBox>
                <ProgressBar Grid.Row="1" IsIndeterminate="True" Height="8" Margin="5"/>
            </StackPanel>
        </Border>
    </Grid>

Si nuestra pantalla no fuera tan sencilla sino un poco más compleja (un DataGrid y unos cuantos UserControls por ejemplo) podríamos observar una considerable caída en el rendimiento.

Para intentar mejorar el comportamiento de nuestra aplicación, vamos a analizar que componentes se están redibujando. La lógica nos diría que simplemente se tendría que redibujar el control del ProgressBar o, como mucho, el TextBox que tenga el foco (por el cursor). Para ver que es lo que realmente se está redibujando tenemos que pasar el siguiente parámetro a a nuestra aplicación Silverlight:

<param name="enableRedrawRegions" value="true"/>

Ejecutamos la aplicación y vemos que se redibuja toda la pantalla!!

RedrawAll

Esto sucede porque cuando añadimos un DropShadowEffect a un borde, siempre que se redibuja un elemento que está dentro de este borde, se redibujan todos los elementos que están dentro del borde.

Por suerte la solución es muy sencilla: tenemos que “partir” nuestro borde en dos bordes: en el primero ponemos tan solo el DropShadowEffect y en el segundo agrupamos todo los elementos que nos interesen. Por lo tanto nuestro código original quedaría así:

<Grid HorizontalAlignment="Center" VerticalAlignment="Center">
        <Border Background="AntiqueWhite" >
            <Border.Effect>
                <DropShadowEffect BlurRadius="0.2" Direction="55" Color="Red"></DropShadowEffect>
            </Border.Effect>
        </Border>
        <Border>
            <StackPanel>
                <TextBlock>TextBlock 1</TextBlock>
                <TextBlock>TextBlock 2</TextBlock>
                <TextBlock>TextBlock 1</TextBlock>
                <TextBox></TextBox>
                <TextBox></TextBox>
                <TextBox></TextBox>
                <TextBox></TextBox>
                <ProgressBar Grid.Row="1" IsIndeterminate="True" Height="8" Margin="5"/>
            </StackPanel>
        </Border>
    </Grid>

Si ahora ejecutamos la aplicación veremos que solo se redibuja la ProgressBar:

RedrawProgress

 

Así que ya sabéis, vigilad con los bordes!