Condividi tramite


Cenni preliminari sugli oggetti congelabili

Questo argomento descrive come usare e creare oggetti Freezable in modo efficace, che offrono funzionalità speciali che consentono di migliorare le prestazioni dell'applicazione. Esempi di oggetti congelabili includono pennelli, penne, trasformazioni, geometrie e animazioni.

Che cos'è un freezable?

Un Freezable è un tipo speciale di oggetto con due stati: scongelato e congelato. Quando viene scongelato, un Freezable sembra comportarsi come qualsiasi altro oggetto. In caso di blocco, non è più possibile modificare un Freezable.

Un Freezable fornisce un evento Changed per notificare agli osservatori eventuali modifiche all'oggetto. Il blocco di un Freezable può migliorare le prestazioni, perché non deve più spendere risorse per le notifiche di modifica. Un Freezable bloccato può anche essere condiviso tra thread, mentre un Freezable sbloccato non può esserlo.

Anche se la classe Freezable include molte applicazioni, la maggior parte degli oggetti Freezable in Windows Presentation Foundation (WPF) è correlata al sotto system grafico.

La classe Freezable semplifica l'uso di determinati oggetti di sistema grafici e consente di migliorare le prestazioni dell'applicazione. Esempi di tipi che ereditano da Freezable includono le classi Brush, Transforme Geometry . Poiché contengono risorse non gestite, il sistema deve monitorare questi oggetti per verificare la disponibilità di modifiche e quindi aggiornare le risorse non gestite corrispondenti quando viene apportata una modifica all'oggetto originale. Anche se non modifichi effettivamente un oggetto di sistema grafico, il sistema deve comunque spendere alcune delle sue risorse monitorando l'oggetto, nel caso in cui lo modifichi.

Si supponga, ad esempio, di creare un pennello SolidColorBrush e usarlo per disegnare lo sfondo di un pulsante.

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

Quando viene eseguito il rendering del pulsante, il sotto sistema grafico WPF usa le informazioni fornite per disegnare un gruppo di pixel per creare l'aspetto di un pulsante. Anche se hai usato un pennello a tinta unita per descrivere come deve essere dipinto il pulsante, il pennello a tinta unita non esegue effettivamente il disegno. Il sistema grafico genera oggetti veloci e di basso livello per il pulsante e il pennello, e sono quegli oggetti che effettivamente appaiono sullo schermo.

Se si dovesse modificare il pennello, è necessario rigenerare gli oggetti di basso livello. La classe freezable è ciò che offre a un pennello la possibilità di trovare i corrispondenti oggetti generati, di basso livello e di aggiornarli quando cambia. Quando questa capacità è abilitata, si dice che il pennello è "unfrozen".

Il metodo Freeze di un Freezable consente di disabilitare questa capacità di auto-aggiornamento. È possibile usare questo metodo per rendere il pennello "bloccato" o non modificabile.

Nota

Non tutti gli oggetti Freezable possono essere congelati. Per evitare di generare un InvalidOperationException, controllare il valore della proprietà CanFreeze dell'oggetto Freezable per determinare se può essere congelato prima di tentare di congelarlo.

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

Quando non è più necessario modificare un oggetto congelabile, congelarlo offre vantaggi in termini di prestazioni. Se si bloccasse il pennello in questo esempio, il sistema grafico non dovrà più monitorarlo per le modifiche. Il sistema grafico può anche apportare altre ottimizzazioni, perché sa che il pennello non cambierà.

Nota

Per praticità, gli oggetti congelabili rimangono non congelati a meno che non vengano bloccati in modo esplicito.

Uso di Freezables

Utilizzare un oggetto congelabile scongelato è come utilizzare qualsiasi altro tipo di oggetto. Nell'esempio seguente il colore di un SolidColorBrush viene modificato da giallo a rosso dopo che viene usato per disegnare lo sfondo di un pulsante. Il sistema grafico funziona dietro le quinte per modificare automaticamente il pulsante da giallo a rosso alla successiva aggiornamento dello schermo.

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

// Changes the button's background to red.
myBrush.Color = Colors.Red;
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

Congelamento di un elemento congelabile

Per rendere un Freezable non modificabile, devi chiamare il suo metodo Freeze. Quando si congela un oggetto che contiene oggetti congelabili, anche questi vengono congelati. Ad esempio, se si blocca un PathGeometry, anche le figure e i segmenti in esso contenuti verrebbero bloccati.

Una congelabile non può essere congelata se è vera una delle seguenti condizioni:

  • Contiene proprietà animate o vincolate ai dati.

  • Dispone di proprietà impostate da una risorsa dinamica. Per ulteriori informazioni sulle risorse dinamiche, consultare le risorse XAML .

  • Contiene Freezable oggetti secondari che non possono essere congelati.

Se queste condizioni sono false e non si intende modificare il Freezable, è consigliabile bloccarlo per ottenere i vantaggi delle prestazioni descritti in precedenza.

Dopo aver chiamato il metodo Freeze di freezable, non può più essere modificato. Se si tenta di modificare un oggetto bloccato, viene generata una InvalidOperationException. Il codice seguente genera un'eccezione, perché si tenta di modificare il pennello dopo essere stato bloccato.


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


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

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


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


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


Nell'esempio di codice precedente è stata creata una copia modificabile di un oggetto bloccato usando il metodo Clone. La sezione successiva illustra in modo più dettagliato la clonazione.

Nota

Poiché non è possibile animare un freezable congelato, il sistema di animazione creerà automaticamente cloni modificabili degli oggetti Freezable congelati quando si tenta di animarli con un Storyboard. Per eliminare il sovraccarico delle prestazioni causato dalla clonazione, lascia un oggetto non congelato se intendi animarlo. Per ulteriori informazioni sull'animazione con storyboard, vedere la Panoramica degli Storyboards .

Blocco dal markup

Per bloccare un oggetto Freezable dichiarato nel markup, usare l'attributo PresentationOptions:Freeze. Nell'esempio seguente un SolidColorBrush viene dichiarato come risorsa di pagina e bloccato. Viene quindi usato per impostare lo sfondo di un pulsante.

<Page 
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:PresentationOptions="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options" 
  xmlns:mc="http://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 usare l'attributo Freeze, è necessario eseguire il mapping allo spazio dei nomi delle opzioni di presentazione: http://schemas.microsoft.com/winfx/2006/xaml/presentation/options. PresentationOptions è il prefisso consigliato per la mappatura di questo spazio dei nomi:

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

Poiché non tutti i lettori XAML riconoscono questo attributo, è consigliabile usare il attributo mc:Ignorable per contrassegnare l'attributo come ignorabile:

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

Per ulteriori informazioni, vedere la pagina dell'attributo mc:Ignorable .

Scongelare un Freezable

Una volta bloccato, un Freezable non può mai essere modificato o sbloccato; tuttavia, è possibile creare un clone sbloccato usando il metodo Clone o CloneCurrentValue.

Nell'esempio seguente, lo sfondo del pulsante viene impostato utilizzando un pennello e questo pennello viene quindi congelato. Viene creata una copia non congelata di pennello usando il metodo Clone. Il clone viene modificato e usato per modificare lo sfondo del pulsante da giallo a rosso.

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

Nota

Indipendentemente dal metodo clone usato, le animazioni non vengono mai copiate nel nuovo Freezable.

I metodi Clone e CloneCurrentValue producono copie complete dell'oggetto congelabile. Se l'oggetto freezable contiene altri oggetti congelati, questi vengono clonati e resi modificabili. Ad esempio, se si clona un PathGeometry congelato per farlo diventare 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à seguenti.

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

  • Thread safety: un Freezable congelato può essere condiviso tra i thread.

  • Notifica dettagliata delle modifiche: a differenza di altri oggetti DependencyObject, gli oggetti Freezable forniscono notifiche di modifica quando i valori delle loro sottoproprietà cambiano.

  • Clonazione semplice: la classe Freezable ha già implementato diversi metodi che producono cloni profondi.

Un Freezable è un tipo di DependencyObjecte quindi usa il sistema di proprietà di dipendenza. Le proprietà della classe non devono essere proprietà di dipendenza, ma l'uso delle proprietà di dipendenza ridurrà la quantità di codice che è necessario scrivere, perché la classe Freezable è stata progettata tenendo presenti le proprietà di dipendenza. Per altre informazioni sul sistema di proprietà di dipendenza, vedere Cenni preliminari sulle proprietà di dipendenza.

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

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

È inoltre necessario osservare le regole seguenti per l'accesso e la scrittura ai membri dati che non sono proprietà di dipendenza:

  • All'inizio di un'API che legge membri dati appartenenti a proprietà diverse dalla dipendenza, chiamare il metodo ReadPreamble.

  • All'inizio di qualsiasi API che scrive membri di dati delle proprietà non di dipendenza, chiamare il metodo WritePreamble. Dopo aver chiamato WritePreamble in un'API, non è necessario effettuare una chiamata aggiuntiva a ReadPreamble se si leggono anche membri dati di proprietà non dipendenti.

  • Chiamare il metodo WritePostscript prima di uscire dai metodi che scrivono in membri dati delle proprietà non di dipendenza.

Se la classe contiene membri dati che non sono proprietà di dipendenza ma sono oggetti DependencyObject, è necessario chiamare anche il metodo OnFreezablePropertyChanged ogni volta che si modifica uno dei relativi valori, anche se si assegna al membro il valore null.

Nota

È molto importante che inizi ogni metodo Freezable di cui esegui l'override con una chiamata all'implementazione di base.

Per un esempio di classe Freezable personalizzata, consultare l'esempio di animazione personalizzata .

Vedere anche