Omówienie obiektów zamrażalnych
W tym temacie opisano sposób efektywnego używania i tworzenia obiektów Freezable, które zapewniają specjalne funkcje, które mogą pomóc zwiększyć wydajność aplikacji. Przykłady obiektów zamrażalnych obejmują pędzle, długopisy, przekształcenia, geometrie i animacje.
Co to jest "Freezable"?
Freezable jest specjalnym typem obiektu, który ma dwa stany: rozmrożony i zamrożony. Po rozmrożeniu Freezable zachowuje się jak każdy inny obiekt. Gdy Freezable jest zamarznięte, nie można go już modyfikować.
Freezable zapewnia zdarzenie Changed, aby powiadomić obserwatorów o wszelkich zmianach w obiekcie. Zamrożenie Freezable może zwiększyć wydajność, ponieważ nie musi już przeznaczać zasobów na powiadomienia o zmianach. Zamrożone Freezable można również udostępniać między wątkami, podczas gdy niezamarzniętego Freezable nie można.
Mimo że klasa Freezable ma wiele aplikacji, większość obiektów Freezable w programie Windows Presentation Foundation (WPF) jest związana z podsystemem graficznym.
Klasa Freezable ułatwia korzystanie z niektórych obiektów systemu graficznego i może pomóc zwiększyć wydajność aplikacji. Przykłady typów dziedziczone z Freezable obejmują klasy Brush, Transformi Geometry. Ponieważ zawierają niezarządzane zasoby, system musi monitorować te obiekty pod kątem modyfikacji, a następnie aktualizować odpowiednie niezarządzane zasoby, gdy nastąpi zmiana oryginalnego obiektu. Nawet jeśli nie zmodyfikujesz obiektu systemu graficznego, system nadal musi poświęcić część swoich zasobów na monitorowanie obiektu na wypadek, gdybyś go zmienił.
Załóżmy na przykład, że utworzysz szczotkę SolidColorBrush i użyjesz jej do malowania tła przycisku.
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
Gdy przycisk jest renderowany, podsystem graficzny WPF używa podanych informacji, aby namalować grupę pikseli w celu utworzenia wyglądu przycisku. Chociaż użyłeś pędzla jednobarwnego, aby opisać sposób malowania przycisku, twój pędzel jednobarwny faktycznie nie wykonuje malowania. System graficzny generuje szybkie, niskiego poziomu obiekty dla przycisku i pędzla, i jest to te obiekty, które rzeczywiście pojawiają się na ekranie.
Gdyby zmodyfikować pędzel, te obiekty niskiego poziomu musiałyby zostać ponownie wygenerowane. Klasa Freezable umożliwia pędzlowi znalezienie swoich odpowiadających wygenerowanych obiektów na niskim poziomie i ich aktualizację w momencie ich zmiany. Kiedy ta funkcja jest włączona, mówi się, że szczotka jest "niezamrożona".
Metoda Freeze umożliwia wyłączenie funkcji automatycznej aktualizacji dla obiektu podlegającego zamrażaniu. Możesz użyć tej metody, aby szczotka stała się "zamrożona" lub niezmodyfikowalna.
Notatka
Nie każdy obiekt zamrażalny może być zamrożony. Aby uniknąć zgłaszania InvalidOperationException, sprawdź wartość właściwości CanFreeze obiektu Freezable, aby określić, czy można go zamrozić przed próbą zamrożenia.
if (myBrush.CanFreeze)
{
// Makes the brush unmodifiable.
myBrush.Freeze();
}
If myBrush.CanFreeze Then
' Makes the brush unmodifiable.
myBrush.Freeze()
End If
Kiedy nie trzeba już modyfikować obiektu podatnego na zamrażanie, zamrożenie go zapewnia korzyści dla wydajności. Gdyby w tym przykładzie zamrozić szczotkę, system graficzny nie musiałby już jej monitorować pod kątem zmian. System graficzny może również dokonać innych optymalizacji, ponieważ wie, że szczotka nie ulegnie zmianie.
Notatka
Dla wygody obiekty zamrażalne pozostają niezamrozzone, chyba że jawnie je zamrozisz.
Korzystanie z Freezables
Użycie rozmrożonego obiektu zamrażalnego jest jak używanie dowolnego innego typu obiektu. W poniższym przykładzie kolor SolidColorBrush jest zmieniany z żółtego na czerwony po jego użyciu do malowania tła przycisku. System graficzny działa w tle, aby automatycznie zmienić przycisk z żółtego na czerwony przy następnym odświeżeniu ekranu.
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
Mrożenie czegoś, co można zamrozić
Aby uczynić Freezable niezmodyfikowalnym, należy wywołać jego metodę Freeze. W przypadku zamarznięcia obiektu, który zawiera obiekty z możliwością zamrażania, te obiekty również są zamrożone. Jeśli na przykład zamrozisz PathGeometry, dane i segmenty, które zawiera, również zostaną zamrożone.
Nie można zamrozić zamrażalnego
Ma właściwości animowane lub powiązane z danymi.
Ma właściwości ustawione przez zasób dynamiczny. (Zobacz zasoby XAML , aby uzyskać więcej informacji o zasobach dynamicznych).
Zawiera Freezable obiektów podrzędnych, których nie można zamrozić.
Jeśli te warunki są fałszywe i nie zamierzasz modyfikować Freezable, należy je zablokować, aby uzyskać korzyści z wydajności opisane wcześniej.
Po wywołaniu metody Freeze obiektu zamrażalnego, nie można jej już modyfikować. Próba zmodyfikowania zamrożonego obiektu powoduje zgłoszenie InvalidOperationException. Poniższy kod zgłasza wyjątek, ponieważ próbujemy zmodyfikować pędzel po jego zamrożeniu.
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
Aby uniknąć zgłaszania tego wyjątku, możesz użyć metody IsFrozen, aby określić, czy Freezable jest zamrożona.
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
W poprzednim przykładzie kodu zmodyfikowana kopia została wykonana z obiektu zamrożonego przy użyciu metody Clone. W następnej sekcji omówiono klonowanie bardziej szczegółowo.
Notatka
Ponieważ nie można animować zamrożonego obiektu typu "freezable", system animacji automatycznie utworzy modyfikowalne klony zamrożonych obiektów Freezable podczas próby animowania ich za pomocą Storyboard. Aby wyeliminować obciążenie związane z wydajnością spowodowane klonowaniem, pozostaw niezamrożony obiekt, jeśli zamierzasz go animować. Aby uzyskać więcej informacji na temat animowania za pomocą scenorysów, zobacz Storyboards Overview.
Zamrażanie z kodu znaczników
Aby zablokować obiekt Freezable zadeklarowany w znaczniku, użyj atrybutu PresentationOptions:Freeze
. W poniższym przykładzie SolidColorBrush jest zadeklarowany jako zasób strony i zamrożony. Następnie służy do ustawiania tła przycisku.
<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>
Aby użyć atrybutu Freeze
, należy przypisać do przestrzeni nazw opcji prezentacji: http://schemas.microsoft.com/winfx/2006/xaml/presentation/options
.
PresentationOptions
jest zalecanym prefiksem do mapowania tej przestrzeni nazw:
xmlns:PresentationOptions="http://schemas.microsoft.com/winfx/2006/xaml/presentation/options"
Ponieważ nie wszyscy czytelnicy XAML rozpoznają ten atrybut, zaleca się użycie mc:Ignorable Attribute do oznaczania atrybutu PresentationOptions:Freeze
jako ignorowanego:
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="PresentationOptions"
Aby uzyskać więcej informacji, zobacz stronę mc:Ignorable Attribute.
"Odmrażanie obiektu zamrażalnego"
Po zamrożeniu Freezable nigdy nie można modyfikować ani odmrozić; można jednak utworzyć klon niezamrożony przy użyciu metody Clone lub CloneCurrentValue.
W poniższym przykładzie tło przycisku jest ustawione za pomocą pędzla, a następnie szczotka jest zamrożona. Niezamrożona kopia pędzla jest tworzona przy użyciu metody Clone. Klon jest modyfikowany i używany do zmiany tła przycisku z żółtego na czerwony.
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
Notatka
Niezależnie od używanej metody klonowania animacje nigdy nie są kopiowane do nowej Freezable.
Metody Clone i CloneCurrentValue tworzą głębokie kopie obiektu typu freezable. Jeśli obiekt zamrażalny zawiera inne zamrożone obiekty zamrażalne, są one również klonowane i mogą być modyfikowane. Jeśli na przykład sklonujesz zamrożone PathGeometry, aby można je było modyfikować, dane i segmenty, które zawiera, również są kopiowane i modyfikowalne.
Tworzenie własnej klasy niemodyfikowalnej
Klasa, która pochodzi z Freezable zyskuje następujące funkcje.
Stany specjalne: tylko do odczytu (zamrożony) i stan zapisywalny.
Bezpieczeństwo wątków: zamrożone Freezable można udostępniać między wątkami.
Szczegółowe powiadomienie o zmianie: W przeciwieństwie do innych DependencyObjectobiekty z możliwością zamrożenia udostępniają powiadomienia o zmianie, gdy wartości właściwości podrzędnych zmieniają się.
Łatwe klonowanie: klasa Freezable zaimplementowała już kilka metod, które tworzą głębokie klony.
Freezable jest typem DependencyObjecti dlatego używa systemu właściwości zależności. Właściwości klasy nie muszą być właściwościami zależności, ale użycie właściwości zależności zmniejszy ilość kodu, który trzeba napisać, ponieważ klasa Freezable została zaprojektowana z uwzględnieniem właściwości zależności. Aby uzyskać więcej informacji na temat systemu właściwości zależności, zobacz Właściwości zależności — omówienie.
Każda podklasa Freezable musi zastąpić metodę CreateInstanceCore. Jeśli klasa używa właściwości zależności dla wszystkich jej danych, skończysz.
Jeśli klasa zawiera elementy członkowskie danych, które nie są właściwościami zależnymi, należy również zastąpić następujące metody:
Należy również przestrzegać następujących reguł dotyczących uzyskiwania dostępu do elementów członkowskich danych, które nie są właściwościami zależności, oraz zapisywania do tych elementów:
Na początku dowolnego interfejsu API, który odczytuje członków danych właściwości niebędących zależnościami, wywołaj metodę ReadPreamble.
Na początku każdego interfejsu API, które zapisuje członków danych, które nie są własnościami zależności, wywołaj metodę WritePreamble. (Po wywołaniu WritePreamble w interfejsie API nie musisz wykonywać dodatkowego wywołania do ReadPreamble, jeśli również odczytujesz elementy członkowskie danych właściwości niezależnych od zależności.)
Wywołaj metodę WritePostscript przed zamknięciem metod zapisujących dane właściwości innych niż zależności.
Jeśli klasa zawiera elementy członkowskie danych niezwiązanych z zależnościami, które są obiektami DependencyObject, należy również wywołać metodę OnFreezablePropertyChanged za każdym razem, gdy zmieniasz jedną z ich wartości, nawet jeśli ustawiasz element członkowski na null
.
Notatka
Bardzo ważne jest, aby rozpocząć każdą metodę Freezable zastąpić wywołaniem implementacji podstawowej.
Aby zapoznać się z przykładem niestandardowej klasy Freezable, zobacz próbkę niestandardowej animacji .
Zobacz też
.NET Desktop feedback