Übersicht über Freezable-Objekte
Aktualisiert: November 2007
In diesem Thema wird die effektive Verwendung und Erstellung von Freezable-Objekten beschrieben, die besondere Features zur Verbesserung der Anwendungsleistung bereitstellen. Beispiele für Freezable-Objekte sind Pinsel, Stifte, Transformationen, Geometrien und Animationen.
Dieses Thema enthält folgende Abschnitte:
Was ist ein Freezable-Objekt?
Ein Freezable ist ein besonderer Typ von Objekt, das zwei Zustände aufweisen kann: nicht fixiert und fixiert. Wenn ein Freezable-Objekt nicht fixiert ist, scheint es sich wie jedes andere Objekt zu verhalten. Ist ein Freezable-Objekt fixiert, kann es nicht mehr geändert werden.
Ein Freezable stellt ein Changed-Ereignis bereit, um die Betrachter im Falle von Objektänderungen zu benachrichtigen. Das Fixieren eines Freezable-Objekts kann zu Leistungsverbesserungen führen, da es keine Ressourcen mehr für Änderungsbenachrichtigungen bindet. Ein fixiertes Freezable kann außerdem über Threads freigegeben werden, während diese Möglichkeit für ein nicht fixiertes Freezable nicht besteht.
Obwohl die Freezable-Klasse viele Anwendungen aufweist, sind die meisten Freezable-Objekte in Windows Presentation Foundation (WPF) mit dem Grafiksubsystem verknüpft.
Die Freezable-Klasse vereinfacht die Verwendung von bestimmten Objekten des Grafiksystems und kann zu einer Verbesserung der Anwendungsleistung beitragen. Beispiele von Klassentypen, die von Freezable erben, sind die Klassen Brush, Transform und Geometry. Da sie nicht verwaltete Ressourcen enthalten, muss das System die Änderungen dieser Objekte überwachen und bei einer Änderung des Originalobjekts die entsprechenden nicht verwalteten Ressourcen aktualisieren. Auch wenn Sie in Wirklichkeit kein Objekt im Grafiksystem ändern, muss das System für den Fall einer Änderung trotzdem einige seiner Ressourcen zur Überwachung des Objekts binden.
Angenommen, Sie erstellen einen SolidColorBrush-Pinsel und verwenden ihn, um den Hintergrund einer Schaltfläche zu zeichnen.
Button myButton = new Button();
SolidColorBrush myBrush = new SolidColorBrush(Colors.Yellow);
myButton.Background = myBrush;
Beim Rendern der Schaltfläche verwendet das WPF-Grafiksubsystem die von Ihnen bereitgestellten Informationen, um eine Pixelgruppe zur Erstellung der Schaltflächendarstellung zu zeichnen. Obwohl Sie anhand des Volltonfarbenpinsels angeben, wie die Schaltfläche gezeichnet werden soll, wird das tatsächliche Zeichnen nicht vom Volltonfarbenpinsel durchgeführt. Das Grafiksystem generiert für die Schaltfläche und für den Pinsel schnelle, systemnahe Objekte; und genau diese Objekte werden dann auf dem Bildschirm angezeigt.
Sollten Sie den Pinsel ändern, müssen Sie diese systemnahen Objekte neu generieren. Die Freezable-Klasse gibt einem Pinsel die Fähigkeit, die ihm entsprechenden generierten systemnahen Objekte zu finden und sie im Falle einer Änderung zu aktualisieren. Wenn diese Fähigkeit aktiviert ist, wird der Pinsel als "nicht fixiert" bezeichnet.
Die Freeze-Methode eines Freezable-Objekts ermöglicht es Ihnen, diese selbstaktualisierende Fähigkeit zu deaktivieren. Sie können diese Methode verwenden, um den Pinsel zu "fixieren", d. h. als nicht änderbar zu definieren.
Tipp
Nicht jedes Freezable-Objekt kann fixiert werden. Um das Auslösen einer InvalidOperationException zu verhindern, müssen Sie den CanFreeze-Eigenschaftenwert des Freezable-Objekts prüfen, um vor dem Fixieren zu ermitteln, ob das Objekt fixiert werden kann.
if (myBrush.CanFreeze)
{
// Makes the brush unmodifiable.
myBrush.Freeze();
}
Wenn Sie ein Freezable-Objekt nicht mehr ändern müssen, führt das Fixieren des Objekts zu Leistungsvorteilen. Wenn Sie den Pinsel in diesem Beispiel fixieren würden, müsste das Grafiksystem ihn nicht mehr überwachen, um eventuelle Änderungen zu ermitteln. Außerdem kann das Grafiksystem andere Optimierungen vornehmen, da es davon ausgehen kann, dass sich der Pinsel nicht ändert.
Tipp
Der Einfachheit halber bleiben Freezable-Objekte so lange nicht fixiert, bis Sie sie explizit fixieren.
Verwenden von Freezable-Objekten
Ein nicht fixiertes Freezable wird wie jeder andere Objekttyp verwendet. Im folgenden Beispiel wird die Farbe eines SolidColorBrush von Gelb in Rot geändert, nachdem der Pinsel verwendet wurde, um den Hintergrund einer Schaltfläche zu zeichnen. Das Grafiksystem ändert die Schaltfläche automatisch im Hintergrund von Gelb in Rot, wenn der Bildschirm das nächste Mal 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;
Fixieren von Freezable-Objekten
Um ein Freezable als unveränderlich auszuweisen, rufen Sie seine Freeze-Methode auf. Wenn Sie ein Objekt fixieren, das Freezable-Objekte enthält, werden diese Objekte ebenfalls fixiert. Wenn Sie z. B. eine PathGeometry fixieren, werden auch die darin enthaltenen Formen und Segmente fixiert.
Ein Freezable kann nicht fixiert werden, wenn einer der folgenden Punkte zutrifft:
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 unter Übersicht über Ressourcen.)
Es enthält untergeordnete Freezable-Objekte, die nicht fixiert werden können.
Wenn diese Bedingungen nicht zutreffen und Sie das Freezable-Objekt nicht ändern möchten, sollten Sie es fixieren, um die bereits erläuterten Leistungsvorteile zu erzielen.
Nachdem Sie die Freeze-Methode eines Freezable-Objekts aufgerufen haben, kann es nicht mehr geändert werden. Der Versuch, ein fixiertes Objekt zu ändern, löst eine InvalidOperationException aus. Im folgenden Code wird eine Ausnahme ausgelöst, weil der Versuch unternommen wird, einen bereits fixierten Pinsel 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());
}
Sie können das Auslösen dieser Ausnahme verhindern, wenn Sie mithilfe der IsFrozen-Methode ermitteln, ob das Freezable-Objekt 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;
}
Im vorherigen Codebeispiel wurde eine veränderliche Kopie eines fixierten Objekts mithilfe der Clone-Methode erstellt. Ausführliche Informationen zum Klonen finden Sie im nächsten Abschnitt.
Hinweis: Da ein fixiertes Freezable-Objekt nicht animiert werden kann, erstellt das Animationssystem automatisch veränderliche Klons von fixierten Freezable-Objekten, wenn Sie sie mit einem Storyboard animieren. Um den durch das Klonen verursachten höheren Leistungsaufwand zu vermeiden, sollten Sie ein zu animierendes Objekt nicht fixieren. Weitere Informationen über Animationen mit Storyboards finden Sie unter Übersicht über Storyboards.
Fixieren über Markup
Um ein in Markup deklariertes Freezable-Objekt zu fixieren, verwenden Sie das PresentationOptions:Freeze-Attribut. Im folgenden Beispiel wird ein SolidColorBrush als Seitenressource deklariert und fixiert. Danach wird der Pinsel verwendet, um den Hintergrund einer Schaltfläche festzulegen.
<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>
Um das Freeze-Attribut verwenden zu können, müssen Sie eine Zuordnung zum Namespace der Präsentationsoptionen vornehmen: https://schemas.microsoft.com/winfx/2006/xaml/presentation/options. PresentationOptions ist das empfohlene Präfix für die Zuordnung dieses Namespaces.
xmlns:PresentationOptions="https://schemas.microsoft.com/winfx/2006/xaml/presentation/options"
Da nicht alle XAML-Reader dieses Attribut akzeptieren, sollten Sie das mc:Ignorable-Attribut verwenden, um das Presentation:Freeze-Attribut als ignorierbar zu kennzeichnen:
xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="PresentationOptions"
Weitere Informationen finden Sie auf der Seite mc:Ignorable-Attribut.
Aufheben der Fixierung von Freezable-Objekten
Sobald ein Freezable-Objekt fixiert ist, kann es nicht geändert und seine Fixierung nicht aufgehoben werden. Sie können jedoch mit der Clone-Methode oder der CloneCurrentValue-Methode einen nicht fixierten Klon erstellen.
Im folgenden Beispiel wird der Hintergrund einer Schaltfläche mit einem Pinsel festgelegt, der daraufhin fixiert wird. Mithilfe der Clone-Methode wird eine nicht fixierte Kopie des Pinsels erstellt. 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;
Tipp
Unabhängig von der verwendeten Klonmethode werden Animationen nicht in das neue Freezable-Objekt kopiert.
Die Methoden Clone und CloneCurrentValue erstellen tiefe Kopien des Freezable-Objekts. Wenn das Freezable-Objekt weitere fixierte Freezable-Objekte enthält, werden diese ebenso geklont und als veränderlich ausgewiesen. Wenn Sie z. B. eine fixierte PathGeometry klonen, damit sie geändert werden kann, werden die darin enthaltenen Formen und Segmente ebenfalls kopiert und als veränderlich ausgewiesen.
Erstellen einer eigenen Freezable-Klasse
Eine von Freezable abgeleitete Klasse erhält die folgenden Features:
Besondere Zustände: ein schreibgeschützter (fixierter) Zustand und ein nicht schreibgeschützter Zustand.
Threadsicherheit: Ein fixiertes Freezable-Objekt kann über Threads freigegeben werden.
Ausführliche Änderungsbenachrichtigung: Im Gegensatz zu einem anderen DependencyObject stellt ein Freezable-Objekt Änderungsbenachrichtigungen bereit, wenn Werte von Untereigenschaften geändert werden.
Leichtes Klonen: In der Freezable-Klasse sind bereits mehrere Methoden implementiert, die tiefe Klone erstellen.
Ein Freezable ist ein DependencyObject-Typ und verwendet demzufolge das Abhängigkeitseigenschaftensystem. Die Klasseneigenschaften müssen keine Abhängigkeitseigenschaften sein, aber die Verwendung von Abhängigkeitseigenschaften reduziert den Umfang des zu schreibenden Codes, da die Freezable-Klasse unter Berücksichtigung der Abhängigkeitseigenschaften entwickelt wurde. Weitere Informationen über das Abhängigkeitseigenschaftensystem finden Sie unter Übersicht über Abhängigkeitseigenschaften.
Jede Freezable-Unterklasse muss die CreateInstanceCore-Methode überschreiben. Verwendet die Klasse Abhängigkeitseigenschaften für alle ihre Daten, müssen Sie keine weiteren Maßnahmen ergreifen.
Wenn die Klasse Datenmember enthält, die keine Abhängigkeitseigenschaften verwenden, müssen Sie außerdem die folgenden Methoden überschreiben:
Beachten Sie außerdem die folgenden Regeln für das Aufrufen von Datenmembern und das Schreiben in Datenmember, bei denen es sich um keine Abhängigkeitseigenschaften handelt:
Rufen Sie am Anfang jeder API, die Datenmember ohne Abhängigkeitseigenschaft abruft, die ReadPreamble-Methode auf.
Rufen Sie am Anfang jeder API, die Datenmember ohne Abhängigkeitseigenschaft aufzeichnet, die WritePreamble-Methode auf. (Nachdem Sie die WritePreamble-Methode in einer API aufgerufen haben, müssen Sie ReadPreamble nicht zusätzlich aufrufen, wenn Sie auch Datenmember ohne Abhängigkeitseigenschaften abrufen.)
Rufen Sie die WritePostscript-Methode vor dem Beenden von Methoden auf, die in Datenmember ohne Abhängigkeitseigenschaften schreiben.
Wenn die Klasse Datenmember ohne Abhängigkeitseigenschaften enthält, bei denen es sich um DependencyObject-Objekte handelt, müssen Sie außerdem die OnFreezablePropertyChanged-Methode bei jeder Änderung der Werte aufrufen. Dies trifft auch dann zu, wenn Sie den Member auf null festlegen.
Tipp
Hierbei ist besonders zu beachten, dass Sie das Überschreiben jeder Freezable-Methode mit dem Aufruf an die Basisimplementierung beginnen.
Ein Beispiel für eine benutzerdefinierte Freezable-Klasse finden Sie unter Beispiel für benutzerdefinierte Animation.
Siehe auch
Aufgaben
Beispiel für benutzerdefinierte Animation
Konzepte
Übersicht über Abhängigkeitseigenschaften
Benutzerdefinierte Abhängigkeitseigenschaften