Übersicht über Freezable-Objekte

In diesem Thema wird beschrieben, wie Sie Freezable-Objekte effektiv verwenden und erstellen, die spezielle Features bereitstellen, die die Anwendungsleistung verbessern können. Beispiele für Freezable-Objekte sind Pinsel, Stifte, Transformationen, Geometrien und Animationen.

Was ist ein Freezable-Objekt?

Ein Freezable ist eine besondere Art von Objekt, das zwei Zustände hat: nicht fixiert und fixiert. Wenn nicht fixiert, verhält sich ein Freezable-Objekt wie jedes andere Objekt. Wenn fixiert, kann eine Freezable-Änderung nicht mehr erfolgen.

Ein Freezable-Ereignis stellt ein Changed-Ereignis bereit, um Beobachter von Änderungen an dem Objekt zu benachrichtigen. Das Fixieren eines Freezable-Objekts kann dessen Leistung verbessern, da es keine Ressourcen für Änderungsbenachrichtigungen mehr aufwenden muss. Ein fixierter Freezable kann auch über Threads freigegeben werden, während dies bei einem nicht fixierten Freezable nicht möglich ist.

Obwohl die Freezable-Klasse viele Anwendungen aufweist, sind die meisten Freezable-Objekte in Windows Presentation Foundation (WPF) mit dem Grafikuntersystem verknüpft.

Die Freezable-Klasse erleichtert die Verwendung bestimmter Grafiksystemobjekte und kann die Anwendungsleistung verbessern. Beispiele für Typen, die von Freezable erben, umfassen die Brush-, Transform- und Geometry-Klassen. Da sie nicht verwaltete Ressourcen enthalten, muss das System diese Objekte für Änderungen überwachen und dann ihre entsprechenden nicht verwalteten Ressourcen aktualisieren, wenn eine Änderung des ursprünglichen Objekts vorhanden ist. Selbst wenn Sie kein Grafiksystemobjekt ändern, muss das System weiterhin einige seiner Ressourcen verbringen, die das Objekt überwachen, falls Sie es ändern.

Angenommen, Sie erstellen einen SolidColorBrush-Pinsel und verwenden sie zum Zeichnen des Hintergrunds einer Schaltfläche.

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

Wenn die Schaltfläche gerendert wird, verwendet das WPF-Grafik-Untersystem die Informationen, die Sie bereitgestellt haben, um eine Gruppe von Pixeln zu zeichnen, um die Darstellung einer Schaltfläche zu erstellen. Obwohl Sie einen Vollfarbenpinsel verwendet haben, um zu beschreiben, wie die Schaltfläche bemalt werden soll, wird der Farbpinsel nicht tatsächlich das Gemälde ausgeführt. Das Grafiksystem generiert schnelle, niedrige Objekte für die Schaltfläche und den Pinsel, und es handelt sich um diese Objekte, die tatsächlich auf dem Bildschirm angezeigt werden.

Wenn Sie den Pinsel ändern möchten, müssen diese Objekte auf niedriger Ebene neu generiert werden. Die freizable Klasse bietet einem Pinsel die Möglichkeit, die entsprechenden generierten, niedrigen Objekte zu finden und sie zu aktualisieren, wenn sie geändert werden. Wenn diese Fähigkeit aktiviert ist, wird der Pinsel als "nicht fixiert" bezeichnet.

Mit der Freeze-Methode eines Freezable-Objekts können Sie diese Selbstaktualisierungsmöglichkeit deaktivieren. Sie können diese Methode verwenden, um den Pinsel „fixiert“ bzw. unmodifizierbar zu machen.

Hinweis

Nicht jedes Freezable-Objekt kann fixiert werden. Um zu vermeiden, dass ein InvalidOperationException-Fehler ausgelöst wird, überprüfen Sie den Wert der Eigenschaft des Freezable-Objekts CanFreeze, um festzustellen, ob es fixiert werden kann, bevor Sie versuchen, es zu fixieren.

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

Wenn Sie keine Änderungen an einem Freezable-Objekt mehr durchführen müssen, bietet Fixieren Leistungsvorteile. Wenn Sie den Pinsel in diesem Beispiel fixieren möchten, muss das Grafiksystem sie nicht mehr für Änderungen überwachen. Das Grafiksystem kann auch andere Optimierungen vornehmen, da es weiß, dass sich der Pinsel nicht ändert.

Hinweis

Für die Bequemlichkeit bleiben Freezable-Objekte nicht fixiert, es sei denn, Sie fixieren sie explizit.

Verwenden von Freezables

Die Verwendung eines nicht fixierten Freezable-Objekts ähnelt der Verwendung eines anderen Objekttyps. Im folgenden Beispiel wird die Farbe einer SolidColorBrush-Schaltfläche von Gelb in Rot geändert, nachdem sie zum Zeichnen des Hintergrunds einer Schaltfläche verwendet wurde. Das Grafiksystem funktioniert hinter den Kulissen, um die Schaltfläche automatisch von Gelb zu Rot zu ändern, wenn der Bildschirm aktualisiert wird.

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

Fixieren eines Freezable-Objekts

Um ein Freezable unmodifizierbar zu machen, rufen Sie die Methode Freeze auf. Wenn Sie ein Objekt fixieren, das Freezable-Objekte enthält, werden diese Objekte ebenfalls fixiert. Wenn Sie z. B. PathGeometry fixieren, würden die enthaltenen Zahlen und Segmente ebenfalls fixiert.

Ein Freezable-Objekt kann nicht fixiert werden, wenn eine der folgenden Elemente wahr ist:

  • Es verfügt über animierte oder datengebundene Eigenschaften.

  • Es verfügt über Eigenschaften, die von einer dynamischen Ressource festgelegt werden. (Weitere Informationen zu dynamischen Ressourcen finden Sie in den XAML-Ressourcen .)

  • Es enthält Freezable-Unterobjekte, die nicht fixiert werden können.

Wenn diese Bedingungen falsch sind und Sie nicht beabsichtigen, Freezable zu ändern, sollten Sie es fixieren, um die zuvor beschriebenen Leistungsvorteile zu erhalten.

Sobald Sie die Freeze-Methode eines Freezable-Objekts aufrufen, kann sie nicht mehr geändert werden. Beim Versuch, ein Freezable-Objekt zu ändern, wird ein InvalidOperationException ausgelöst. Der folgende Code löst eine Ausnahme aus, da wir versuchen, den Pinsel nach dem Fixieren zu ändern.


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

Um diese Ausnahme zu vermeiden, können Sie die IsFrozen-Methode verwenden, um zu ermitteln, ob eine Freezable fixiert ist.


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


Im vorhergehenden Codebeispiel wurde eine modifizierbare Kopie aus einem fixierten Objekt mit der Clone-Methode erstellt. Im nächsten Abschnitt wird das Klonen ausführlicher erläutert.

Hinweis

Da fixierte Freezable-Objekte nicht animiert werden können, erstellt das Animationssystem automatisch modifizierbare Klonen von fixierten Freezable-Objekten, wenn Sie versuchen, sie mit einem Storyboard zu animieren. Um den Leistungsaufwand zu beseitigen, der durch das Klonen verursacht wird, lassen Sie ein Objekt „nicht fixiert“, wenn Sie es animieren möchten. Weitere Informationen zu Animieren mit Storyboards finden Sie unter Übersicht über Storyboards.

Fixieren von Markup

Um ein in Markup deklariertes Freezable-Objekt zu fixieren, verwenden Sie das PresentationOptions:Freeze-Attribut. Im folgenden Beispiel wird eine SolidColorBrush als Seitenressource deklariert und fixiert. Anschließend wird der Hintergrund einer Schaltfläche festgelegt.

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

Zur Nutzung des Freeze-Attributs müssen Sie dem Präsentationsoptionennamespace http://schemas.microsoft.com/winfx/2006/xaml/presentation/options zuordnen. PresentationOptions ist das empfohlene Präfix für die Zuordnung dieses Namespaces:

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

Da nicht alle XAML-Leser dieses Attributs erkennen, empfiehlt es sich, das mc:Ignorable-Attribut zum Markieren des PresentationOptions:Freeze- Attributs als ignorierbar zu markieren:

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

Weitere Informationen finden Sie auf der Seite mc:Ignorable-Attribut.

Fixieren eines Freezable-Objekts aufheben

Sobald eine Freezable fixiert wurde, kann sie niemals geändert bzw. ihre Fixierung nicht aufgehoben werden. Sie können jedoch einen nicht fixierten Klon mit der Clone- oder CloneCurrentValue-Methode erstellen.

Im folgenden Beispiel wird der Hintergrund der Schaltfläche mit einem Pinsel festgelegt und dieser Pinsel wird dann fixiert. Eine nicht fixierte Kopie wird mit der Clone-Methode aus dem Pinsel hergestellt. Der Klon wird geändert und verwendet, um den Hintergrund der Schaltfläche von Gelb in Rot zu ändern.

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

Hinweis

Unabhängig davon, welche Klonmethode Sie verwenden, werden Animationen nie in das neue Freezable kopiert.

Die und Clone- und CloneCurrentValue-Methoden produzieren tiefe Kopien des Freezable-Objekts. Wenn ein Freezable-Objekt andere Freezable-Objekte enthält, werden sie auch geklont und geändert. Wenn Sie z. B. einen fixierten PathGeometry klonen, um ihn zu ändern, werden die darin enthaltenen Zahlen und Segmente auch kopiert und geändert.

Erstellen ihrer eigenen Freezable-Klasse

Eine Klasse, die von Freezable abgeleitet wird, erhält folgende Funktionen.

  • Spezielle Zustände: Schreibgeschützter (fixierter) und ein beschreibbarer Zustand.

  • Threadsicherheit: Ein fixierter Freezable kann über Threads freigegeben werden.

  • Detaillierte Änderungsbenachrichtigung: Im Gegensatz zu anderen DependencyObjects stellen Freezable-Objekte Änderungsbenachrichtigungen bereit, wenn untere Eigenschaftenwerte geändert werden.

  • Einfaches Klonen: Die Freezable-Klasse hat bereits mehrere Methoden implementiert, die tiefe Klonen erzeugen.

Ein Freezable ist eine Art von DependencyObject, und verwendet daher das Abhängigkeitseigenschaftssystem. Ihre Klasseneigenschaften müssen keine Abhängigkeitseigenschaften sein, aber die Verwendung von Abhängigkeitseigenschaften reduziert den Code, den Sie schreiben müssen, da die Freezable-Klasse mit Abhängigkeitseigenschaften entwickelt wurde. Weitere Informationen zum Abhängigkeitseigenschaftssystem finden Sie in der Übersicht über die Abhängigkeitseigenschaften.

Jede Freezable-Unterklasse muss die CreateInstanceCore-Methode außer Kraft setzen. Wenn Ihre Klasse Abhängigkeitseigenschaften für alle Daten verwendet, sind Sie fertig.

Wenn Ihre Klasse Nicht-Abhängigkeitseigenschaftsdatenelemente enthält, müssen Sie auch die folgenden Methoden außer Kraft setzen:

Außerdem müssen Sie die folgenden Regeln für den Zugriff und das Schreiben von Datenmitgliedern beachten, die keine Abhängigkeitseigenschaften sind:

  • Rufen Sie am Anfang einer API, die Nicht-Abhängigkeitseigenschaftsdatenelemente liest, die ReadPreamble-Methode auf.

  • Rufen Sie am Anfang jeder API, die Nicht-Abhängigkeitseigenschaftsdatenelemente schreibt, die WritePreamble-Methode auf. (Nachdem Sie in einer API WritePreamble aufgerufen haben, müssen Sie keinen zusätzlichen Aufruf von ReadPreamble vornehmen, wenn Sie auch Nicht-Abhängigkeitseigenschaftsdatenelemente lesen.)

  • Rufen Sie die WritePostscript-Methode auf, bevor Sie Methoden beenden, die in Nicht-Abhängigkeitseigenschaftsdatenelemente schreiben.

Wenn Ihre Klasse Nicht-Abhängigkeits-Eigenschaftsdatenelemente enthält, die DependencyObject-Objekte sind, müssen Sie die OnFreezablePropertyChanged-Methode auch jedes Mal aufrufen, wenn Sie einen ihrer Werte ändern, auch wenn Sie das Element auf null festlegen.

Hinweis

Es ist sehr wichtig, dass Sie mit jeder Freezable-Methode beginnen, die Sie mit einem Aufruf der Basisimplementierung außer Kraft setzen.

Ein Beispiel für eine benutzerdefinierte Freezable-Klasse finden Sie im Beispiel für benutzerdefinierte Animationen.

Weitere Informationen