Información general sobre objetos Freezable

En este tema se describe cómo utilizar y crear con eficacia objetos Freezable, que proporcionan características especiales que pueden ayudar a mejorar el rendimiento de la aplicación. Algunos ejemplos de objetos Freezable son los pinceles, los lápices, las transformaciones, las geometrías y las animaciones.

Este tema contiene las siguientes secciones:

¿Qué es un objeto Freezable?

Un objeto Freezable es un tipo especial de objeto que tiene dos estados: no inmovilizado e inmovilizado. Cuando no está inmovilizado, un objeto Freezable parece comportarse como cualquier otro objeto. Cuando está inmovilizado, un objeto Freezable ya no se puede modificar.

Un objeto Freezable proporciona un evento Changed para notificar a los observadores todas las modificaciones del objeto. Inmovilizar un objeto Freezable, puede mejorar su rendimiento, porque ya no es preciso dedicar recursos a las notificaciones de cambio. Un objeto Freezable inmovilizado también se puede compartir entre subprocesos, lo que no sucede si el objeto Freezable no está inmovilizado.

Aunque la clase Freezable tiene muchas aplicaciones, la mayoría de los objetos Freezable de Windows Presentation Foundation (WPF) están relacionados con el subsistema de gráficos.

La clase Freezable facilita el uso de algunos objetos del sistema de gráficos y puede ayudar a mejorar el rendimiento de la aplicación. Algunos ejemplos de tipos que heredan de Freezable son las clases Brush, Transform y Geometry. Dado que contienen recursos no administrados, el sistema debe supervisar estos objetos por si se producen modificaciones y, a continuación, actualizar sus recursos no administrados correspondientes cuando se produce un cambio en el objeto original. Aunque no se modifique realmente un objeto del sistema de gráficos, el sistema debe dedicar parte de sus recursos a supervisarlo, por si acaso cambia.

Por ejemplo, supongamos que se crea un pincel SolidColorBrush y se utiliza para pintar el fondo de un botón.

            Dim myButton As New Button()
            Dim myBrush As New SolidColorBrush(Colors.Yellow)
            myButton.Background = myBrush
Button myButton = new Button();
SolidColorBrush myBrush = new SolidColorBrush(Colors.Yellow);
myButton.Background = myBrush;  

Al representar el botón, el subsistema de gráficos de WPF utiliza la información que se proporciona para pintar un grupo de píxeles y crear el aspecto de un botón. Aunque se ha utilizado un pincel de color sólido para describir cómo se debe pintar el botón, en realidad este pincel de color sólido no es el que se encarga de pintar. El sistema de gráficos genera objetos rápidos de bajo nivel para el botón y el pincel, y son esos objetos los que realmente aparecen en la pantalla.

Si modifica el pincel, será preciso volver a generar estos objetos de bajo nivel. La clase Freezable es lo que proporciona a un pincel la capacidad de buscar sus objetos generados de bajo nivel correspondientes y actualizarlos cuando cambian. Cuando esta capacidad está habilitada, se dice que el pincel "no está inmovilizado".

El método Freeze de un objeto Freezable permite deshabilitar esta capacidad de actualización automática. Puede utilizar este método para "inmovilizar" el pincel, es decir, para que no se pueda modificar.

NotaNota

No todos los objetos Freezable se pueden inmovilizar.Para evitar que se inicie una excepción InvalidOperationException, compruebe el valor de la propiedad CanFreeze del objeto Freezable, a fin de determinar si se puede inmovilizar antes de intentar inmovilizarlo.

            If myBrush.CanFreeze Then
                ' Makes the brush unmodifiable.
                myBrush.Freeze()
            End If
if (myBrush.CanFreeze)
{
    // Makes the brush unmodifiable.
    myBrush.Freeze();
}

Cuando ya no es necesario modificar un objeto Freezable, inmovilizarlo aporta ventajas de rendimiento. Si en el ejemplo anterior inmoviliza el pincel, el sistema de gráficos ya no tendrá que supervisarlo por si cambia. Además, el sistema de gráficos puede realizar otras optimizaciones, porque sabe que el pincel no cambiará.

NotaNota

Para mayor comodidad, los objetos Freezable permanecen no inmovilizados hasta que se inmovilizan de manera explícita.

Utilizar Freezables

Utilizar un objeto Freezable no inmovilizado es igual que utilizar cualquier otro tipo de objeto. En el ejemplo siguiente, se cambia el color de SolidColorBrush del amarillo al rojo después de utilizarlo para pintar el fondo de un botón. El sistema de gráficos actúa en segundo plano para cambiar automáticamente el botón del amarillo al rojo la próxima vez que se actualiza la pantalla.

            Dim myButton As New Button()
            Dim myBrush As New SolidColorBrush(Colors.Yellow)
            myButton.Background = myBrush


            ' Changes the button's background to red.
            myBrush.Color = Colors.Red
Button myButton = new Button();
SolidColorBrush myBrush = new SolidColorBrush(Colors.Yellow);
myButton.Background = myBrush;  


// Changes the button's background to red.
myBrush.Color = Colors.Red;

Inmovilizar un objeto Freezable

Para impedir que un objeto Freezable, se pueda modificar, llame a su método Freeze. Al inmovilizar un objeto que contiene objetos Freezable, éstos últimos se inmovilizan también. Por ejemplo, si inmoviliza una PathGeometry, las figuras y los segmentos que contiene también se inmovilizan.

No se puede inmovilizar un objeto Freezable en ninguna de las situaciones siguientes:

  • Tiene propiedades animadas o enlazadas a datos.

  • Tiene propiedades establecidas por un recurso dinámico. (Para obtener más información acerca de recursos dinámicos, consulte Información general sobre recursos.)

  • Contiene subobjetos Freezable que no se pueden inmovilizar.

Si estas condiciones no se cumplen y tampoco tiene previsto modificar un objeto Freezable, entonces es conveniente inmovilizarlo para obtener las ventajas de rendimiento descritas anteriormente.

Cuando se llama al método Freeze de un objeto Freezable, éste ya no se puede modificar. Si intenta modificar un objeto inmovilizado, se inicia una excepción InvalidOperationException. Al ejecutar el código siguiente se inicia una excepción, porque se intenta modificar el pincel después de inmovilizarlo.


            Dim myButton As New Button()
            Dim myBrush As New SolidColorBrush(Colors.Yellow)

            If myBrush.CanFreeze Then
                ' Makes the brush unmodifiable.
                myBrush.Freeze()
            End If

            myButton.Background = myBrush

            Try

                ' Throws an InvalidOperationException, because the brush is frozen.
                myBrush.Color = Colors.Red
            Catch ex As InvalidOperationException
                MessageBox.Show("Invalid operation: " & ex.ToString())
            End Try


            Button myButton = new Button();
            SolidColorBrush myBrush = new SolidColorBrush(Colors.Yellow);          

            if (myBrush.CanFreeze)
            {
                // Makes the brush unmodifiable.
                myBrush.Freeze();
            }

            myButton.Background = myBrush;  

            try {

                // Throws an InvalidOperationException, because the brush is frozen.
                myBrush.Color = Colors.Red;
            }catch(InvalidOperationException ex)
            {
                MessageBox.Show("Invalid operation: " + ex.ToString());
            }

Para evitar que se inicie esta excepción, puede utilizar el método IsFrozen para determinar si un objeto Freezable está inmovilizado.


            Dim myButton As New Button()
            Dim myBrush As New SolidColorBrush(Colors.Yellow)

            If myBrush.CanFreeze Then
                ' Makes the brush unmodifiable.
                myBrush.Freeze()
            End If

            myButton.Background = myBrush


            If myBrush.IsFrozen Then ' Evaluates to true.
                ' If the brush is frozen, create a clone and
                ' modify the clone.
                Dim myBrushClone As SolidColorBrush = myBrush.Clone()
                myBrushClone.Color = Colors.Red
                myButton.Background = myBrushClone
            Else
                ' If the brush is not frozen,
                ' it can be modified directly.
                myBrush.Color = Colors.Red
            End If



            Button myButton = new Button();
            SolidColorBrush myBrush = new SolidColorBrush(Colors.Yellow);

            if (myBrush.CanFreeze)
            {
                // Makes the brush unmodifiable.
                myBrush.Freeze();
            }            

            myButton.Background = myBrush;


            if (myBrush.IsFrozen) // Evaluates to true.
            {
                // If the brush is frozen, create a clone and
                // modify the clone.
                SolidColorBrush myBrushClone = myBrush.Clone();
                myBrushClone.Color = Colors.Red;
                myButton.Background = myBrushClone;
            }
            else
            {
                // If the brush is not frozen,
                // it can be modified directly.
                myBrush.Color = Colors.Red;
            }


En el ejemplo de código anterior, se ha realizado una copia modificable de un objeto inmovilizado mediante el método Clone. En la sección siguiente se explica la clonación con más detalle.

Nota: puesto que los objetos Freezable inmovilizados no se pueden animar, el sistema de animación crea automáticamente clones modificables de los objetos Freezable inmovilizados si intenta animarlos con Storyboard. Para eliminar la sobrecarga de rendimiento causada por la clonación, no inmovilice los objetos si tiene previsto animarlos. Para obtener más información sobre animación con guiones gráficos, consulte Información general sobre objetos Storyboard.

Inmovilizar mediante marcado

Para inmovilizar un objeto Freezable declarado en el marcado, se utiliza el atributo PresentationOptions:Freeze. En el ejemplo siguiente, se declara SolidColorBrush como recurso de página y se inmoviliza. A continuación, se utiliza para establecer el fondo de un botón.

<Page 
  xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:PresentationOptions="https://schemas.microsoft.com/winfx/2006/xaml/presentation/options" 
  xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
  mc:Ignorable="PresentationOptions">

  <Page.Resources>

    <!-- This resource is frozen. -->
    <SolidColorBrush 
      x:Key="MyBrush"
      PresentationOptions:Freeze="True" 
      Color="Red" />
  </Page.Resources>


  <StackPanel>

    <Button Content="A Button" 
      Background="{StaticResource MyBrush}">
    </Button>

  </StackPanel>
</Page>

Para utilizar el atributo Freeze, debe efectuar la asignación al espacio de nombres de opciones de presentación: https://schemas.microsoft.com/winfx/2006/xaml/presentation/options. PresentationOptions es el prefijo recomendado para asignar este espacio de nombres:

xmlns:PresentationOptions="https://schemas.microsoft.com/winfx/2006/xaml/presentation/options" 

Dado que no todos los lectores de XAML reconocen este atributo, se recomienda utilizar Atributo mc:Ignorable para marcar el atributo Presentation:Freeze como omitible:

  xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
  mc:Ignorable="PresentationOptions"

Para obtener más información, consulte la página Atributo mc:Ignorable.

"Liberar" un objeto Freezable

Una vez inmovilizado, un objeto Freezable nunca se puede modificar ni liberar; sin embargo, sí puede crear un clon no inmovilizado mediante el método Clone o CloneCurrentValue.

En el ejemplo siguiente, se establece el fondo de un botón con un pincel y, a continuación, se inmoviliza el pincel. Se realiza una copia no inmovilizada del pincel mediante el método Clone. El clon se modifica y se utiliza para cambiar el fondo del botón del amarillo al rojo.

            Dim myButton As New Button()
            Dim myBrush As New SolidColorBrush(Colors.Yellow)

            ' Freezing a Freezable before it provides
            ' performance improvements if you don't
            ' intend on modifying it. 
            If myBrush.CanFreeze Then
                ' Makes the brush unmodifiable.
                myBrush.Freeze()
            End If


            myButton.Background = myBrush

            ' If you need to modify a frozen brush,
            ' the Clone method can be used to
            ' create a modifiable copy.
            Dim myBrushClone As SolidColorBrush = myBrush.Clone()

            ' Changing myBrushClone does not change
            ' the color of myButton, because its
            ' background is still set by myBrush.
            myBrushClone.Color = Colors.Red

            ' Replacing myBrush with myBrushClone
            ' makes the button change to red.
            myButton.Background = myBrushClone
Button myButton = new Button();
SolidColorBrush myBrush = new SolidColorBrush(Colors.Yellow);

// Freezing a Freezable before it provides
// performance improvements if you don't
// intend on modifying it. 
if (myBrush.CanFreeze)
{
    // Makes the brush unmodifiable.
    myBrush.Freeze();
}


myButton.Background = myBrush;  

// If you need to modify a frozen brush,
// the Clone method can be used to
// create a modifiable copy.
SolidColorBrush myBrushClone = myBrush.Clone();

// Changing myBrushClone does not change
// the color of myButton, because its
// background is still set by myBrush.
myBrushClone.Color = Colors.Red;

// Replacing myBrush with myBrushClone
// makes the button change to red.
myButton.Background = myBrushClone;
NotaNota

Sea cual sea el método de clonación que utilice, las animaciones nunca se copian en el nuevo objeto Freezable.

Los métodos Clone y CloneCurrentValue generan copias en profundidad del objeto Freezable. Si el objeto Freezable contiene otros objetos Freezable inmovilizados, éstos también se clonan y son modificables. Por ejemplo, si clona un PathGeometry inmovilizado para poder modificarlo, las figuras y los segmentos que contiene también se copian y se pueden modificar.

Crear su propia clase Freezable

Una clase que se deriva de Freezable incluye las siguientes características.

  • Estados especiales: estado de sólo lectura (inmovilizado) y estado modificable.

  • Seguridad de subprocesos: un objeto Freezable inmovilizado se puede compartir entre subprocesos.

  • Notificación de los cambios detallados: a diferencia de otros objetos DependencyObject, los objetos Freezable proporcionan notificaciones de cambios cuando cambian los valores de la subpropiedad.

  • Clonación fácil: la clase Freezable ya ha implementado varios métodos que generan clones perfectos.

Un objeto Freezable es un tipo de DependencyObject y, por consiguiente, utiliza el sistema de propiedades de dependencia. No es necesario que las propiedades de clase sean propiedades de dependencia, pero al utilizar propiedades de dependencia se reduce la cantidad de código que hay que escribir, porque la clase Freezable se ha diseñado teniendo en cuenta las propiedades de dependencia. Para obtener más información sobre el sistema de propiedades de dependencia, consulte Información general sobre las propiedades de dependencia.

Cada subclase de Freezable debe invalidar el método CreateInstanceCore. Si la clase utiliza propiedades de dependencia para todos los datos, con esto basta.

Si la clase contiene miembros de datos de propiedades que no son de dependencia, también debe invalidar los métodos siguientes:

También debe observar las reglas siguientes para obtener acceso a los miembros de datos que no son propiedades de dependencia y escribir en ellos:

  • Al principio de cualquier API que lee miembros de datos de propiedades que no son de dependencia, llame al método ReadPreamble.

  • Al principio de cualquier API que escribe miembros de datos de propiedades que no son de dependencia, llame al método WritePreamble. (Después de llamar al método WritePreamble de una API, ya no necesita realizar una llamada adicional a ReadPreamble si también va a leer miembros de datos de propiedades que no son de dependencia.)

  • Llame al método WritePostscript antes de salir de los métodos que escriben en los miembros de datos de propiedades que no son de dependencia.

Si la clase contiene miembros de datos de propiedades que no son de dependencia y que son objetos DependencyObject, también debe llamar al método OnFreezablePropertyChanged cada vez que cambie uno de sus valores, aunque establezca el miembro en null.

NotaNota

Es muy importante que comience cada método de Freezable que invalide con una llamada a la implementación base.

Para obtener un ejemplo de una clase Freezable personalizada, vea Custom Animation Sample.

Vea también

Referencia

Freezable

Conceptos

Información general sobre las propiedades de dependencia

Propiedades de dependencia personalizadas

Otros recursos

Ejemplo Custom Animation