Partager via


Vue d'ensemble des objets Freezable

Cette rubrique décrit comment utiliser efficacement et créer des objets Freezable, qui offrent des fonctionnalités spéciales susceptibles de contribuer à l'amélioration des performances. Les exemples d'objets freezable incluent les pinceaux, les stylets, les transformations, les géométries et les animations.

Cette rubrique contient les sections suivantes :

Qu'est-ce qu'un Freezable ?

Un Freezable est un type d'objet spécial qui possède deux états : figé et non figé. Quand il est non figé, un objet Freezable semble se comporter comme n'importe quel autre objet. Quand il est figé, un objet Freezable ne peut plus être modifié.

Un objet Freezable fournit un événement Changed pour signaler aux observateurs toute modification apportée à l'objet. Le fait de figer un objet Freezable peut améliorer sa performance, car il n'a plus besoin de consacrer des ressources aux notifications des modifications. Un objet figé Freezable peut également être partagé entre plusieurs threads, ce qui n'est pas le cas d'un objet non figé Freezable.

Bien que la classe Freezable ait de nombreuses applications, la plupart des objets Freezable dans Windows Presentation Foundation (WPF) sont liés au sous-système graphique.

La classe Freezable simplifie l'utilisation de certains objets du système graphique et peut améliorer les performances de l'application. Les exemples des types qui héritent de Freezable incluent les classes Brush, Transform et Geometry. Dans la mesure où il contient certaines ressources non managées, le système doit surveiller ces objets en cas de modifications, puis mettre à jour leurs ressources non managées correspondantes quand une modification intervient dans l'objet d'origine. Même si vous ne modifiez pas en fait un objet du système graphique, le système doit encore consacrer une partie de ses ressources à la surveillance de l'objet, au cas où vous le modifiez.

Par exemple, supposons que vous créez un pinceau SolidColorBrush et que vous l'utilisez pour peindre l'arrière-plan d'un bouton.

            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;  

Lors du rendu du bouton, le sous-système graphique WPF utilise les informations fournies pour peindre un groupe de pixels en vue de créer l'apparence d'un bouton. Bien que vous ayez utilisé un pinceau de couleur unie pour décrire comment le bouton doit être peint, votre pinceau de couleur unie ne peint pas vraiment. Le système graphique génère des objets rapides de bas niveau pour le bouton et le pinceau, et ce sont ces objets qui apparaissent en fait sur l'écran.

Si vous deviez modifier le pinceau, ces objets seraient à régénérer. La classe freezable est ce qui permet au pinceau de trouver ses objets de bas niveau générés correspondants pour les mettre à jour quand il change. Quand cette fonctionnalité est activée, le pinceau est dit « non figé ».

Une méthode de freezable Freeze permet de désactiver cette fonctionnalité d'auto-mise à jour. Vous pouvez utiliser cette méthode pour rendre le pinceau « figé », ou non modifiable.

RemarqueRemarque

Les objets Freezable ne peuvent tous être figés.Pour éviter de générer un InvalidOperationException, vérifiez la propriété CanFreeze d'un objet Freezable afin de déterminer s'il peut être figé avant d'essayer de le figer.

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

Lorsque vous n'avez plus besoin de modifier un objet freezable, le fait de le figer présente des avantages. Si vous deviez figer le pinceau dans cet exemple, le système graphique n'aurait plus à surveiller ses modifications. Le système graphique peut également effectuer des optimisations, car il sait que le pinceau ne changera pas.

RemarqueRemarque

Par souci pratique, les objets de type freezable restent non figés jusqu'à ce qu'ils soient figés.

Utilisation des Freezables

L'utilisation d'un Freezable non figé est similaire à l'utilisation de tout autre type d'objet. Dans l'exemple suivant, la couleur d'un SolidColorBrush passe du jaune au rouge une fois qu'il a été utilisé pour peindre l'arrière-plan d'un bouton. Le système graphique travaille en coulisses pour modifier automatiquement le bouton en faisant passer sa couleur du jaune au rouge lors de la prochaine actualisation de son écran.

            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;

Figer un Freezable

Pour rendre Freezable non modifiable, vous appelez sa méthode Freeze. Lorsque vous figez un objet qui contient des objets pouvant être figés, ces objets sont également figés. Par exemple, si vous figez un PathGeometry, les figures et les segments qu'il contient seraient également figés.

Un Freezable ne peut pas être figé si l'une des conditions suivantes est vraie :

  • Il dispose de propriétés animées ou liées aux données.

  • Il dispose de propriétés définies par une ressource dynamique. (Pour plus d'informations sur les ressources dynamiques, consultez Vue d'ensemble des ressources.)

  • Il contient des sous-objets Freezable qui ne peuvent pas être figés.

Si ces conditions ne sont pas réalisées, et que vous n'envisagez pas de modifier Freezable, vous devez alors le figer afin de bénéficier du gain de performances décrit plus haut.

Une fois que vous appelez la méthodeFreeze d'un freezable, celui-ci ne peut plus être modifié. Toute tentative visant à modifier un objet figé provoque la levée d'une InvalidOperationException. Le code suivant lève une exception, car nous tentons de modifier le pinceau une fois qu'il a été figé.


            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());
            }

Pour éviter de lever cette exception, vous pouvez utiliser la méthode IsFrozen pour déterminer si Freezable est figé.


            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;
            }


Dans l'exemple de code précédent, une copie modifiable a été effectuée d'un objet figé à l'aide de la méthode Clone. La section traite du clonage plus en détail.

Remarque   Dans la mesure où un Freezable figé ne peut pas être animé, le système d'animation créera automatiquement des clones modifiables des objets Freezable figés lorsque vous essayez de les animer avec une Storyboard. Pour éliminer la surcharge de performance liée au clonage, laissez un objet non figé si vous avez l'intention de l'animer. Pour plus d'informations sur l'animation avec les tables de montage séquentiel, consultez Vue d'ensemble des storyboards.

Geler des objets à partir du balisage

Pour geler un objet Freezable déclaré dans le marquage, vous utilisez l'attribut PresentationOptions:Freeze. Dans l'exemple suivant, un SolidColorBrush est déclaré en tant que ressource de page et figé. Il est ensuite utilisé pour définir l'arrière-plan d'un bouton.

<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>

Pour utiliser l'attribut Freeze, vous devez mapper à l'espace de noms des options de présentation : https://schemas.microsoft.com/winfx/2006/xaml/presentation/options PresentationOptions est le préfixe recommandé pour le mappage de cet espace de noms :

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

Dans la mesure où les lecteurs XAML ne reconnaissent pas cet attribut, il est recommandé d'utiliser l'mc:Ignorable, attribut pour marquer l'attribut Presentation:Freeze comme pouvant être ignoré :

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

Pour plus d'informations, consultez la page mc:Ignorable, attribut.

« Libération » d'un Freezable figé

Une fois figé, un Freezable ne peut jamais être modifié ou libéré de son état figé ; cependant, vous pouvez créer un clone non figé à l'aide de la méthode Clone ou CloneCurrentValue.

Dans l'exemple suivant, l'arrière-plan d'un bouton est défini à l'aide d'un pinceau et ce pinceau est figé. Une copie non figée du pinceau est effectuée à l'aide de la méthode Clone. Le clone est modifié et utilisé pour changer l'arrière-plan du bouton en faisant passer sa couleur de jaune à rouge.

            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;
RemarqueRemarque

Indépendamment de la méthode de clone que vous utilisez, les animations ne sont jamais copiées vers le nouveau Freezable.

Les méthodes Clone et CloneCurrentValue génèrent des copies complètes du Freezable. Si le Freezable contient d'autres objets Freezable figés, ils sont également clonés et rendus modifiables. Par exemple, si vous clonez un PathGeometry figé pour le rendre modifiable, les figures et segments qu'il contient sont également copiés et rendus modifiables.

Création de votre propre classe Freezable

Une classe qui dérive de Freezable acquiert les fonctionnalités suivantes.

  • États spéciaux : en lecture seule (figé) et un état d'écriture.

  • Sécurité des threads : un objet Freezable figé peut être partagé entre plusieurs threads.

  • Notification de modifications détaillée : contrairement à d'autres objets DependencyObject, les objets Freezable fournissent des notifications de modifications lorsque les valeurs de sous-propriété sont modifiées.

  • Clonage facile : la classe Freezable a déjà implémenté plusieurs méthodes qui génèrent des clones complets.

Un Freezable est un type de DependencyObject, et utilise en conséquence le système de propriétés de dépendance. Il n'est pas nécessaire que vos propriétés de classe soient des propriétés de dépendance, mais l'utilisation des propriétés de dépendance réduit la quantité de code que vous devez écrire, car la classe Freezable a été conçue avec les propriétés de dépendance en tête. Pour plus d'informations sur le système de propriétés de dépendance, consultez Vue d'ensemble des propriétés de dépendance.

Chaque sous-classe Freezable doit substituer la méthode CreateInstanceCore. Si votre classe utilise des propriétés de dépendance pour toutes ses données, votre tâche est terminée.

Si votre classe contient des membres de données de propriétés de non dépendance, vous devez également substituer les méthodes suivantes :

Vous devez également observer les règles suivantes pour l'accès aux données membres (et leur écriture) qui ne correspondent pas à des propriétés de dépendance :

  • Au début de toute API qui lit les données membres de propriétés de non dépendance, appelez la méthode ReadPreamble.

  • Au début de toute API qui écrit des données membres de propriétés de non dépendance, appelez la méthode WritePreamble. (Une fois que vous avez appelé WritePreamble dans une API, vous n'avez pas besoin d'effectuer un appel supplémentaire à ReadPreamble si vous lisez également les données membres de propriétés de non dépendance.)

  • Appelez la méthode WritePostscript avant de quitter les méthodes qui écrivent dans les données membres de propriétés de non dépendance.

Si votre classe contient des données membres de propriétés de non dépendance qui sont des objets DependencyObject, vous devez également appeler la méthode OnFreezablePropertyChanged chaque fois que vous changez leurs valeurs, même si vous attribuez au membre la valeur null.

RemarqueRemarque

Il est très important de commencer chaque méthode Freezable que vous substituez avec un appel à l'implémentation de base.

Pour obtenir un exemple d'une classe Freezable personnalisée, consultez Animation personnalisée, exemple.

Voir aussi

Référence

Freezable

Concepts

Vue d'ensemble des propriétés de dépendance

Propriétés de dépendance personnalisées

Autres ressources

Animation personnalisée, exemple (page éventuellement en anglais)