Przegląd Obiekty Freezable

W tym temacie opisano sposób efektywnego używania i tworzenia Freezable obiektów, 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 zamrażanie?

A Freezable jest specjalnym typem obiektu, który ma dwa stany: niezamrozzone i zamrożone. Gdy odroczysz, obiekt Freezable wygląda na to, że zachowuje się jak każdy inny obiekt. W przypadku zamarzniętego Freezable elementu nie można już modyfikować.

Element Freezable udostępnia Changed zdarzenie do powiadamiania obserwatorów o wszelkich modyfikacjach obiektu. Zamrożenie elementu Freezable może zwiększyć wydajność, ponieważ nie musi już wydawać zasobów na powiadomienia o zmianie. Zamrożone Freezable mogą być również udostępniane między wątkami, podczas gdy nie można rozmrozić Freezable .

Freezable Mimo że klasa ma wiele aplikacji, większość Freezable obiektów w programie Windows Presentation Foundation (WPF) jest związana z podsystemem grafiki.

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 dziedziczą z Freezable klas 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 wydać niektóre jego zasoby monitorujące obiekt, jeśli go zmienisz.

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żyliśmy pędzla w kolorze stałym, aby opisać sposób malowania przycisku, twój szczotka w kolorze stałym 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ć szczotkę, te obiekty niskiego poziomu musiałyby zostać ponownie wygenerowane. Klasa zamrażalna daje szczotki możliwość znalezienia odpowiednich wygenerowanych obiektów niskiego poziomu i zaktualizowania ich po zmianie. Gdy ta możliwość jest włączona, szczotka mówi się, że jest "niezamrożona".

Metoda zamrażania Freeze umożliwia wyłączenie tej możliwości samodzielnej aktualizacji. Możesz użyć tej metody, aby szczotka stała się "zamrożona" lub niezmodyfikowalna.

Uwaga

Nie każdy obiekt zamrażalny może być zamrożony. Aby uniknąć zgłaszania InvalidOperationExceptionobiektu , sprawdź wartość właściwości Obiektu zamrażalnego CanFreeze , aby określić, czy można go zamrozić przed próbą zablokowania.

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

Gdy nie trzeba już modyfikować zamrażalnego, zamrażanie zapewnia korzyści z wydajności. Gdyby w tym przykładzie zamrozić szczotkę, system graficzny nie będzie już musiał monitorować go pod kątem zmian. System graficzny może również dokonać innych optymalizacji, ponieważ wie, że szczotka nie ulegnie zmianie.

Uwaga

Dla wygody obiekty zamrażalne pozostają niezamrozzone, chyba że jawnie je zamrozisz.

Korzystanie z zamrażalnych

Użycie niezamrozzonego zamrażania jest podobne do używania dowolnego innego typu obiektu. W poniższym przykładzie kolor elementu 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

Zamrażanie zamrażania

Aby utworzyć Freezable niemodyfikowalny element, należy wywołać jego Freeze metodę. 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 PathGeometryobiekt , dane i segmenty, które zawiera, również zostaną zamrożone.

Nie można zamrozić zamrażania, jeśli którekolwiek z następujących warunków ma wartość true:

  • 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 na temat zasobów dynamicznych).

  • Zawiera Freezable on obiekty podrzędne, których nie można zablokować.

Jeśli te warunki są fałszywe i nie zamierzasz modyfikować elementu , należy je zablokować Freezable, aby uzyskać korzyści z wydajności opisane wcześniej.

Po wywołaniu metody zamrażalnej Freeze nie można jej już modyfikować. Próba zmodyfikowania zamrożonego obiektu powoduje zgłoszenie obiektu InvalidOperationException . Poniższy kod zgłasza wyjątek, ponieważ próbujemy zmodyfikować szczotkę po jego zablokowaniu.


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ć IsFrozen metody w celu określenia, czy element Freezable jest zamrożony.


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 Clone metody . W następnej sekcji omówiono klonowanie bardziej szczegółowo.

Uwaga

Ponieważ nie można animować zamrożonego zamrażania, system animacji automatycznie utworzy modyfikowalne klony zamrożonych Freezable obiektów podczas próby animowania ich za pomocą elementu 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 (Omówienie scenorysów).

Zamrażanie z adiustacji

Aby zablokować obiekt zadeklarowany Freezable w adiustacji, należy użyć atrybutu PresentationOptions:Freeze . W poniższym przykładzie element 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 zamapować na przestrzeń nazw opcji prezentacji: http://schemas.microsoft.com/winfx/2006/xaml/presentation/options. PresentationOptions jest zalecanym prefiksem 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 atrybutu mc:Ignorable, aby oznaczyć PresentationOptions:Freeze atrybut jako ignorowalny:

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

Aby uzyskać więcej informacji, zobacz stronę mc:Ignorable Attribute (Atrybut mc:Ignorable).

"Odmrażanie" zamrażania

Po zamrożeniu Freezable można nigdy nie modyfikować lub odmrozić. Można jednak utworzyć niezamrożoną klon przy użyciu Clone metody lub CloneCurrentValue .

W poniższym przykładzie tło przycisku jest ustawione za pomocą pędzla, a następnie szczotka jest zamrożona. Niezamroziona kopia jest wykonana z pędzla Clone przy użyciu metody . 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

Uwaga

Niezależnie od używanej metody klonowania animacje nigdy nie są kopiowane do nowej Freezablemetody .

Metody Clone i CloneCurrentValue tworzą głębokie kopie zamrażania. Jeśli zamrażanie zawiera inne zamrożone obiekty zamrażalne, są one również klonowane i modyfikowalne. Jeśli na przykład sklonujesz zamrożone PathGeometry , aby można było go modyfikować, dane i segmenty, które zawiera, również są kopiowane i modyfikowalne.

Tworzenie własnej klasy zamrażalnej

Klasa, która pochodzi z Freezable zysków z następujących funkcji.

  • 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 DependencyObjectobiektów, obiekty z możliwością zamrażania udostępniają powiadomienia o zmianie, gdy wartości właściwości podrzędnych ulegają zmianie.

  • Łatwe klonowanie: klasa Freezable zaimplementowała już kilka metod, które tworzą głębokie klony.

A Freezable jest typem DependencyObject, a zatem 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ż Freezable klasa 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 Omówienie właściwości zależności.

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 właściwości innych niż zależność, 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 i zapisywania ich w następujących elementach:

  • Na początku dowolnego interfejsu API, który odczytuje elementy członkowskie danych właściwości niezależności, wywołaj metodę ReadPreamble .

  • Na początku dowolnego interfejsu API, który zapisuje elementy członkowskie danych właściwości niezależności, wywołaj metodę WritePreamble . (Po wywołaniu WritePreamble interfejsu API nie musisz wykonywać dodatkowego wywołania ReadPreamble , jeśli odczytujesz również elementy członkowskie danych właściwości niezwiązanych z zależnościami).

  • Wywołaj metodę WritePostscript przed zamknięciem metod zapisu w elementach członkowskich danych właściwości innych niż zależność.

Jeśli klasa zawiera elementy członkowskie danych niezwiązanych z zależnościami, które są DependencyObject obiektami, 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.

Uwaga

Bardzo ważne jest, aby rozpocząć każdą Freezable metodę zastępowaną wywołaniem implementacji podstawowej.

Przykład klasy niestandardowej Freezable można znaleźć w przykładzie niestandardowej animacji.

Zobacz też