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 freezable to pędzle, pióra, przekształcenia, geometrie i animacje.

Co to jest freezable?

A Freezable to specjalny typ obiektu, który ma dwa stany: niefrozenny i zamrożony. Gdy obiekt jest odfruzen, obiekt Freezable wydaje się zachowywać jak każdy inny obiekt. W przypadku zamrożonych Freezable wartości nie można już modyfikować.

Obiekt Freezable udostępnia zdarzenie Changed w celu powiadamiania obserwatorów o wszelkich modyfikacjach obiektu. Zamarzanie Freezable może poprawić jego wydajność, ponieważ nie musi już wydawać zasobów na powiadomienia o zmianach. Zamrożony Freezable element może być również współużytżony w różnych wątkach, podczas gdy nie można tego zrobić Freezable .

Mimo że Freezable klasa ma wiele aplikacji, większość Freezable obiektów w Windows Presentation Foundation (WPF) jest powiązana z podfolderem 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 dziedziczące z klasy FreezableBrushobejmują klasy , Transformi Geometry . Ponieważ zawierają zasoby nieza zarządzania, system musi monitorować te obiekty pod celu modyfikacji, a następnie aktualizować odpowiadające im zasoby nieza zarządzania w przypadku zmiany oryginalnego obiektu. Nawet jeśli w rzeczywistości nie zmodyfikuje się obiektu systemu graficznego, system musi nadal wydawać część zasobów monitorujących obiekt w przypadku jego zmiany.

Załóżmy na przykład, że tworzysz pędzl SolidColorBrush i używasz go 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

Podczas renderowania przycisku podsieć grafiki WPF używa podanych informacji do malowania grupy pikseli w celu utworzenia wyglądu przycisku. Mimo że do opisania sposobu malowania przycisku został użyty pełny pędzl kolorów, nie jest on w rzeczywistości malowany. System graficzny generuje szybkie obiekty niskiego poziomu dla przycisku i pędzla i są to obiekty, które faktycznie pojawiają się na ekranie.

W przypadku zmodyfikowania pędzla te obiekty niskiego poziomu muszą zostać ponownie wygenerowane. Klasa freezable zapewnia pędzlowi możliwość znalezienia odpowiednich wygenerowanych obiektów niskiego poziomu i zaktualizowania ich po zmianie. Gdy ta możliwość jest włączona, pędzl jest mówiny jako "unfrozen".

Metoda freezable Freeze umożliwia wyłączenie tej możliwości samodzielnego aktualizowania. Za pomocą tej metody można sprawić, że pędzl stanie się "zamrożony" lub niemodyfikowalny.

Uwaga

Nie każdy obiekt Freezable może być zamrożony. Aby uniknąć zgłaszania , InvalidOperationExceptionsprawdź wartość właściwości obiektu Freezable CanFreeze , aby określić, czy może być zamrożona przed próbą zablokowania obiektu.

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ć freezable, blokowanie zapewnia korzyści wydajności. Jeśli w tym przykładzie chcesz zablokować pędzl, system graficzny nie będzie już musiał monitorować zmian. System graficzny może również wprowadzić inne optymalizacje, ponieważ wie, że pędzl nie zmieni się.

Uwaga

Dla wygody obiekty freezable pozostają nieodświązywalne, chyba że jawnie je zablokowasz.

Korzystanie z freezables

Użycie unfrozen freezable jest podobne do korzystania z dowolnego innego typu obiektu. W poniższym przykładzie kolor SolidColorBrush koloru jest zmieniany z żółtego na czerwony po tym, jak jest on używany 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

Blokowanie freezable

Aby niemodyfikować Freezable , należy wywołać jego Freeze metodę . Po zablokowaniu obiektu, który zawiera obiekty freezable, te obiekty również są zamrożone. Jeśli na przykład zablokujemy , PathGeometrywartości i segmenty, które zawiera, również będą zamrożone.

Freezable nie może być zamrożony, jeśli dowolne z następujących jest prawdziwe:

  • Ma właściwości animowane lub powiązane z danymi.

  • Ma właściwości ustawiane przez zasób dynamiczny. (Zobacz zasoby XAML, aby uzyskać więcej informacji na temat zasobów dynamicznych).

  • Zawiera obiekty Freezable podrzędne, których nie można zamrożonych.

Jeśli te warunki są fałszywe i Freezablenie zamierzasz modyfikować właściwości , należy zablokować ją, aby uzyskać opisane wcześniej korzyści związane z wydajnością.

Po wywołaniu metody freezable Freeze nie można jej już modyfikować. Próba zmodyfikowania zamrożonego obiektu powoduje InvalidOperationException wygenerowanie obiektu . Poniższy kod zgłasza wyjątek, ponieważ próbujemy zmodyfikować pędzl po jego zamrożonym kodzie.


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żna użyć IsFrozen metody , aby określić, czy jest ona zamrożona Freezable .


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 zmodyfikowalna kopia została wykonana z zamrożonego obiektu przy użyciu Clone metody . W następnej sekcji bardziej szczegółowo omówiono klonowanie.

Uwaga

Ponieważ zamrożonych obiektów freezable nie można animować, system animacji automatycznie tworzy modyfikowalne Freezable klony zamrożonych obiektów podczas próby ich animowania za pomocą obiektu Storyboard. Aby wyeliminować obciążenie związane z wydajnością spowodowane klonowaniem, pozostaw obiekt niefrozenny, jeśli zamierzasz go animować. Aby uzyskać więcej informacji na temat animowania za pomocą storyboards, zobacz Storyboards Overview (Omówienie storyboards).

Blokowanie z znaczników

Aby zablokować obiekt Freezable zadeklarowany w znacznikach, należy użyć atrybutu PresentationOptions:Freeze . W poniższym przykładzie a jest zadeklarowany SolidColorBrush jako zasób strony i zamrożony. Następnie służy do ustawienia 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:IgnorablePresentation:Freeze w celu oznaczenia atrybutu jako możliwego do zignorowania:

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

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

"Unfreezing" a Freezable

Gdy element jest zamrożony Freezable , nigdy nie można go zmodyfikować ani odroczyć, ale możesz utworzyć klon niefrozenny przy użyciu Clone metody lub CloneCurrentValue .

W poniższym przykładzie tło przycisku jest ustawiane za pomocą pędzla, a następnie jest on zamrożony. Niefrozenna kopia jest owana za pomocą pędzla przy użyciu Clone 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 metody klonowania, której używasz, animacje nigdy nie są kopiowane do nowej metody Freezable.

Metody Clone i CloneCurrentValue dają głębokie kopie freezable. Jeśli freezable zawiera inne obiekty freezable zamrożonych, są one również klonowane i modyfikowalne. Jeśli na przykład sklonowany PathGeometry zostanie zamrożony, aby można go było zmodyfikować, dane liczbowe i segmenty, które zawiera, również zostaną skopiowane i zmodyfikowalne.

Tworzenie własnej klasy Freezable

Klasa, która pochodzi od klasy , Freezable uzyskuje następujące funkcje.

  • Stany specjalne: stan tylko do odczytu (zamrożony) i stan zapisywalny.

  • Bezpieczeństwo wątków: zamrożony Freezable może być współużytowany przez wątki.

  • Szczegółowe powiadomienie o zmianie: W przeciwieństwie do innych DependencyObjectobiektów Freezable zapewniają powiadomienia o zmianie w przypadku zmiany wartości właściwości podrzędnych.

  • Łatwe klonowanie: klasa Freezable zaimplementowała już kilka metod tworzenia głębokich klonów.

Obiekt Freezable jest typem typu 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ć, Freezable ponieważ klasa została zaprojektowana z myślą o właściwościach zależności. Aby uzyskać więcej informacji na temat systemu właściwości zależności, zobacz Przegląd właściwości zależności.

Każda Freezable podklasa musi zastąpić CreateInstanceCore metodę . Jeśli klasa używa właściwości zależności dla wszystkich swoich danych, wszystko jest gotowe.

Jeśli klasa zawiera składowe danych właściwości innych niż zależności, 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 i zapisywania ich w nich, które nie są właściwościami zależności:

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

  • Na początku dowolnego interfejsu API, który zapisuje elementy członkowskie danych właściwości innych niż zależności, wywołaj metodę WritePreamble . (Po wywołaniu w WritePreamble interfejsie API ReadPreamble nie musisz wykonać dodatkowego wywołania , jeśli odczytasz również elementy członkowskie danych właściwości bez zależności).

  • Wywołaj WritePostscript metodę przed zamknięciem metod zapisujących elementy członkowskie danych właściwości bez zależności.

Jeśli klasa zawiera DependencyObject składowe danych inne niż zależności, które są obiektami, OnFreezablePropertyChanged należy również wywołać metodę za każdym razem, gdy zmienisz jedną z ich wartości, nawet jeśli ustawiasz składową na null.

Uwaga

Bardzo ważne jest, aby każda przesłonięcie Freezable każdej metody było rozpoczynane wywołaniem implementacji podstawowej.

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

Zobacz też