Cenni preliminari sugli oggetti Freezable

In questo argomento viene descritto come usare e creare Freezable oggetti in modo efficace, che forniscono funzionalità speciali che consentono di migliorare le prestazioni dell'applicazione. Esempi di oggetti liberabili includono pennelli, penne, trasformazioni, geometrie e animazioni.

Che cos'è un freezable?

Un Freezable è un tipo speciale di oggetto con due stati: unfrozen e bloccato. Quando unfrozen, un oggetto Freezable sembra comportarsi come qualsiasi altro oggetto. In caso di blocco, un oggetto Freezable non può più essere modificato.

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

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

La Freezable classe semplifica l'uso di determinati oggetti di sistema grafico e consente di migliorare le prestazioni dell'applicazione. Esempi di tipi che ereditano da Freezable includono le Brushclassi , 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 SolidColorBrush pennello 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 ed è gli oggetti effettivamente visualizzati 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, il pennello viene detto "unfrozen".

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

Nota

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

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 freezable, il blocco 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 freezable rimangono inflessibili a meno che non vengano bloccate in modo esplicito.

Uso di Freezables

L'uso di un freezable unfrozen è simile all'uso di qualsiasi altro tipo di oggetto. Nell'esempio seguente il colore di un SolidColorBrush oggetto 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 freezable

Per rendere Freezable un oggetto non modificabile, chiamare il relativo Freeze metodo. Quando si blocca un oggetto che contiene oggetti liberabili, tali oggetti vengono bloccati. Ad esempio, se si blocca un oggetto PathGeometry, anche le figure e i segmenti in esso contenuti verrebbero bloccate.

Un oggetto Freezable non può essere bloccato se si verifica una delle condizioni seguenti:

  • Contiene proprietà animate o associate a dati.

  • Dispone di proprietà impostate da una risorsa dinamica. (Vedere il Risorse XAML per altre informazioni sulle risorse dinamiche.

  • Contiene Freezable oggetti secondari che non possono essere bloccati.

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

Dopo aver chiamato il metodo freezable Freeze , non può più essere modificato. Se si tenta di modificare un oggetto bloccato, viene generata un'eccezione InvalidOperationException . Il codice seguente genera un'eccezione, perché si tenta di modificare il pennello dopo che è 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 IsFrozen metodo per determinare se un oggetto 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 Clone metodo . La sezione successiva illustra in modo più dettagliato la clonazione.

Nota

Poiché non è possibile animare un oggetto freezable bloccato, il sistema di animazione creerà automaticamente cloni modificabili di oggetti bloccati Freezable quando si tenta di animarli con un oggetto Storyboard. Per eliminare il sovraccarico delle prestazioni causato dalla clonazione, lasciare un oggetto non danneggiato se si intende animarlo. Per altre informazioni sull'animazione con storyboard, vedere Cenni preliminari sugli storyboard.

Blocco dal markup

Per bloccare un Freezable oggetto dichiarato nel markup, usare l'attributo PresentationOptions:Freeze . Nell'esempio seguente un SolidColorBrush oggetto 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 il mapping 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 l'attributo mc:Ignorable per contrassegnare l'attributo PresentationOptions:Freeze come ignorabile:

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

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

"Liberare" un Freezable

Una volta bloccato, un oggetto Freezable non può mai essere modificato o annullabile. Tuttavia, è possibile creare un clone unfrozen usando il Clone metodo o CloneCurrentValue .

Nell'esempio seguente lo sfondo del pulsante viene impostato con un pennello e tale pennello viene quindi bloccato. Una copia unfrozen viene eseguita con il pennello usando il Clone metodo . 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 Freezableoggetto .

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

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

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

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

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

Un Freezable è un tipo di e quindi usa il sistema di DependencyObjectproprietà 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 Freezable classe è 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 Freezable sottoclasse deve eseguire l'override del CreateInstanceCore metodo . 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 qualsiasi API che legge i membri dei dati delle proprietà non di dipendenza, chiamare il ReadPreamble metodo .

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

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

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

Nota

È molto importante iniziare ogni Freezable metodo di cui si esegue l'override con una chiamata all'implementazione di base.

Per un esempio di classe personalizzata, vedi l'esempio di animazione personalizzataFreezable.

Vedi anche