Cenni preliminari sugli oggetti Freezable

In questo argomento viene descritto come creare e utilizzare in modo efficace gli oggetti Freezable che forniscono funzionalità speciali per migliorare le prestazioni dell'applicazione. Tra gli esempi di oggetti Freezable sono inclusi pennelli, penne, trasformazioni, geometrie e animazioni.

Di seguito sono elencate le diverse sezioni di questo argomento:

Definizione di oggetto Freezable

Freezable è un tipo di oggetto speciale con due stati: non bloccato e bloccato. Quando non è bloccato, Freezable si comporta come gli altri oggetti. Quando è bloccato, Freezable non può più essere modificato.

Freezable fornisce un evento Changed per notificare gli osservatori di qualsiasi modifica all'oggetto. Il blocco di un oggetto Freezable consente di migliorarne le prestazioni poiché non è più necessario impiegare le risorse nelle notifiche di modifica. Un oggetto Freezable bloccato può anche essere condiviso tra thread mentre ciò non è possibile per gli oggetti Freezable non bloccati.

Sebbene la classe Freezable disponga di molte applicazioni, la maggior parte degli oggetti Freezable in Windows Presentation Foundation (WPF) si riferisce al sottosistema grafico.

La classe Freezable semplifica l'utilizzo di alcuni oggetti del sistema grafico e può aiutare a migliorare le prestazioni dell'applicazione. Tra gli esempi di tipi che ereditano da Freezable sono incluse le classi Brush, Transform e Geometry. Poiché questi oggetti contengono risorse non gestite, il sistema deve monitorare le eventuali modifiche e quindi aggiornare le rispettive risorse non gestite quando viene apportata una modifica all'oggetto originale. Anche se un oggetto del sistema grafico non viene effettivamente modificato, il sistema deve comunque impiegare parte delle proprie risorse per il monitoraggio dell'oggetto per verificare eventuali modifiche.

Ad esempio, si supponga di creare un pennello SolidColorBrush e di utilizzarlo per disegnare lo sfondo di un pulsante.

            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;  

Quando viene eseguito il rendering del pulsante, il sottosistema grafico di WPF utilizza le informazioni fornite per disegnare un gruppo di pixel per definire l'aspetto di un pulsante. Anche se è stato utilizzato un pennello a tinta unita per descrivere il disegno del pulsante, tale pennello non esegue effettivamente il disegno. Il sistema grafico genera oggetti veloci e di basso livello per il pulsante e il pennello e sono questi gli oggetti che vengono effettivamente visualizzati sullo schermo.

Se fosse necessario modificare il pennello, questi oggetti di basso livello dovrebbero essere rigenerati. La classe Freezable consente al pennello di trovare i corrispondenti oggetti di basso livello generati e di aggiornarli in caso di modifiche. In questo caso, il pennello è definito "non bloccato".

Il metodo Freeze di un oggetto Freezable consente di disabilitare questa funzione di aggiornamento automatico. È possibile utilizzare questo metodo per "bloccare" il pennello o renderlo non modificabile.

NotaNota

Non tutti gli oggetti Freezable possono essere bloccati.Per evitare di generare InvalidOperationException, prima di provare a bloccare l'oggetto Freezable, verificare il valore della relativa proprietà CanFreeze per determinare se può essere bloccato.

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

Quando non è più necessario modificare un oggetto Freezable, è possibile bloccarlo per migliorarne le prestazioni. Se in questo esempio fosse necessario bloccare il pennello, il sistema grafico non lo monitorerebbe più per verificare la presenza di eventuali modifiche. Il sistema grafico può anche eseguire ulteriori ottimizzazioni perché è evidente il pennello non sarà modificato.

NotaNota

Per comodità, gli oggetti Freezable restano non bloccati a meno che non vengano bloccati in modo esplicito.

Utilizzo degli oggetti Freezable

L'utilizzo di un oggetto Freezable non bloccato è simile a quello di qualsiasi altro tipo di oggetto. Nell'esempio riportato di seguito, il colore di SolidColorBrush viene modificato da giallo a rosso dopo essere stato utilizzato per disegnare lo sfondo di un pulsante. Il sistema grafico modifica automaticamente il colore del pulsante da giallo a rosso al successivo aggiornamento dello schermo.

            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;

Blocco di un oggetto Freezable

Per rendere immodificabile Freezable, è necessario chiamare il relativo metodo Freeze. Quando viene bloccato un oggetto contenente oggetti bloccabili, anche questi oggetti vengono bloccati. Ad esempio, se si blocca PathGeometry, vengono bloccate anche le relative figure e segmenti.

Un oggetto Freezable non può essere bloccato se una delle seguenti condizioni è vera:

  • Include proprietà animate o associate a dati.

  • Include proprietà impostate da una risorsa dinamica. (Per ulteriori informazioni sulle risorse dinamiche, vedere Cenni preliminari sulle risorse).

  • Contiene oggetti secondari Freezable che non possono essere bloccati.

Se queste condizioni sono false e non si intende modificare Freezable, è consigliabile bloccarlo per migliorare le prestazioni come descritto in precedenza.

Dopo aver chiamato il metodo Freeze di un oggetto Freezable, non è più possibile apportarvi modifiche. Il tentativo di modifica di un oggetto bloccato genera InvalidOperationException. Il codice riportato di seguito genera un'eccezione perché si tenta di modificare il pennello dopo che è stato bloccato.


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

Per evitare di generare questa eccezione, è possibile utilizzare il metodo IsFrozen per determinare se un oggetto Freezable è bloccato.


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


Nell'esempio di codice precedente era stata eseguita una copia modificabile di un oggetto bloccato utilizzando il metodo Clone. La clonazione viene descritta in modo più dettagliato nella sezione seguente.

Nota   Poiché un oggetto Freezable bloccato non può essere animato, il sistema di animazione crea automaticamente dei cloni modificabili degli oggetti Freezable bloccati quando si tenta di animarli con Storyboard. Per evitare la riduzione delle prestazioni provocata dalla clonazione, lasciare non bloccato l'oggetto che si intende animare. Per ulteriori informazioni sull'animazione con gli storyboard, vedere Cenni preliminari sugli storyboard.

Blocco dal markup

Per bloccare un oggetto Freezable dichiarato nel markup, è necessario utilizzare l'attributo PresentationOptions:Freeze. Nell'esempio riportato di seguito, SolidColorBrush viene dichiarato risorsa della pagina e viene bloccato. Viene quindi utilizzato per impostare lo sfondo di un pulsante.

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

Per utilizzare l'attributo Freeze, è necessario eseguire il mapping allo spazio dei nomi delle opzioni di presentazione: https://schemas.microsoft.com/winfx/2006/xaml/presentation/options. PresentationOptions è il prefisso consigliato per il mapping di questo spazio dei nomi:

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

Poiché non tutti i reader XAML riconoscono questo attributo, si consiglia di utilizzare Attributo mc:Ignorable per contrassegnare l'attributo Presentation:Freeze come ignorabile:

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

Per ulteriori informazioni, vedere la pagina Attributo mc:Ignorable.

Sblocco di un oggetto Freezable

Una volta bloccato, un oggetto Freezable non può più essere modificato o sbloccato, tuttavia è possibile crearne un clone non bloccato utilizzando il metodo Clone o CloneCurrentValue.

Nell'esempio riportato di seguito, lo sfondo del pulsante viene impostato con un pennello che poi viene bloccato. La copia non bloccata del pennello viene effettuata utilizzando il metodo Clone. Il clone viene modificato e utilizzato per modificare il colore dello sfondo da giallo a rosso.

            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

Indipendentemente dal metodo di clonazione utilizzato, le animazioni non vengono mai copiate nel nuovo oggetto Freezable.

I metodi Clone e CloneCurrentValue eseguono copie complete dell'oggetto Freezable. Se tale oggetto contiene altri oggetti Freezable bloccati, questi vengono a loro volta clonati e resi modificabili. Ad esempio, se si clona un oggetto PathGeometry bloccato per renderlo modificabile, anche le figure e i segmenti in esso contenuti vengono copiati e resi modificabili.

Creazione di una classe Freezable personalizzata

Una classe che deriva da Freezable ottiene le funzionalità riportate di seguito.

  • Stati speciali: uno stato di sola lettura (bloccato) e uno stato scrivibile.

  • Sicurezza dei thread: un oggetto Freezable bloccato può essere condiviso tra thread.

  • Notifica dettagliata delle modifiche: a differenza di altri oggetti DependencyObject, gli oggetti Freezable forniscono notifiche di modifica quando vengono modificati i valori della proprietà secondaria.

  • Clonazione facile: la classe Freezable ha già implementato molti metodi che producono cloni completi.

Freezable è un tipo di DependencyObject e perciò utilizza il sistema di proprietà di dipendenza. Le proprietà di classe personalizzate non devono essere proprietà di dipendenza ma l'utilizzo di queste ultime consente di ridurre la quantità di codice da scrivere poiché la classe Freezable è stata progettata tenendo presenti le proprietà di dipendenza. Per ulteriori informazioni sul sistema di proprietà di dipendenza, vedere Cenni preliminari sulle proprietà di dipendenza.

Ogni sottoclasse di Freezable deve eseguire l'override del metodo CreateInstanceCore. Se la classe utilizza proprietà di dipendenza per tutti i dati, l'operazione è completa.

Se la classe contiene membri dati della proprietà di non dipendenza, è anche necessario eseguire l'override dei seguenti metodi:

Per accedere e scrivere su membri dati che non sono proprietà di dipendenza è anche necessario osservare le seguenti regole:

  • All'inizio di ogni API che legge i membri dati della proprietà di non dipendenza, è necessario chiamare il metodo ReadPreamble.

  • All'inizio di ogni API che scrive i membri dati della proprietà di non dipendenza, è necessario chiamare il metodo WritePreamble. Dopo aver chiamato WritePreamble in un'API, non è necessario eseguire un'altra chiamata a ReadPreamble se vengono letti anche i membri dati della proprietà di non dipendenza.

  • È necessario chiamare il metodo WritePostscript prima di uscire dai metodi che scrivono sui membri dati della proprietà di non dipendenza.

Se la classe personalizzata contiene membri dati della proprietà di non dipendenza che sono oggetti DependencyObject, è necessario chiamare anche il metodo OnFreezablePropertyChanged ogni volta che viene modificato uno dei relativi valori, anche se il membro viene impostato su null.

NotaNota

È molto importante iniziare ogni metodo Freezable di cui si esegue l'override chiamando l'implementazione di base.

Per un esempio di classe Freezable personalizzata, vedere Esempio di animazione personalizzata (la pagina potrebbe essere in inglese).

Vedere anche

Riferimenti

Freezable

Concetti

Cenni preliminari sulle proprietà di dipendenza

Proprietà Dependency personalizzate

Altre risorse

Esempio di animazione personalizzata