Compartilhar via


Visão Geral dos Objetos Congeláveis

Este tópico descreve como usar e criar efetivamente objetos Freezable, que fornecem recursos especiais que podem ajudar a melhorar o desempenho do aplicativo. Exemplos de objetos congeláveis incluem pincéis, canetas, transformações, geometrias e animações.

O que é um Freezable?

Um Freezable é um tipo especial de objeto que tem dois estados: descongelado e congelado. Quando descongelado, um Freezable parece se comportar como qualquer outro objeto. Quando congelado, um Freezable não pode mais ser modificado.

Um Freezable fornece um evento Changed para notificar os observadores sobre quaisquer modificações no objeto. Congelar um Freezable pode melhorar seu desempenho, pois ele não precisa mais gastar recursos em notificações de alteração. Um Freezable congelado também pode ser compartilhado entre threads, enquanto um Freezable não pode ser descongelado.

Embora a classe Freezable tenha muitos aplicativos, a maioria dos objetos Freezable no WPF (Windows Presentation Foundation) está relacionada ao subsistema gráfico.

A classe Freezable facilita o uso de determinados objetos gráficos do sistema e pode ajudar a melhorar o desempenho do aplicativo. Exemplos de tipos que herdam de Freezable incluem as classes Brush, Transforme Geometry. Como eles contêm recursos não gerenciados, o sistema deve monitorar esses objetos para modificações e, em seguida, atualizar seus recursos não gerenciados correspondentes quando houver uma alteração no objeto original. Mesmo que você não modifique realmente um objeto de sistema gráfico, o sistema ainda deverá gastar alguns de seus recursos monitorando o objeto, caso você o altere.

Por exemplo, suponha que você crie um pincel SolidColorBrush e use-o para pintar a tela de fundo de um botão.

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

Quando o botão é renderizado, o subsistema de elementos gráficos do WPF usa as informações fornecidas para pintar um grupo de pixels para criar a aparência de um botão. Embora você tenha usado um pincel de cor sólida para descrever como o botão deve ser pintado, seu pincel de cor sólida não faz a pintura. O sistema gráfico gera objetos rápidos e de baixo nível para o botão e o pincel, e são os objetos que realmente aparecem na tela.

Se você modificasse o pincel, esses objetos de baixo nível precisariam ser regenerados. A classe freezable é o que dá a um pincel a capacidade de encontrar seus objetos de baixo nível correspondentes gerados e atualizá-los quando eles são alterados. Quando essa capacidade é habilitada, diz-se que o pincel está "descongelado".

O método Freeze de um freezable permite desabilitar essa capacidade de autoatualização. Você pode usar esse método para tornar o pincel "congelado" ou não modificável.

Nota

Nem todos os objetos Freezable podem ser congelados. Para evitar lançar um InvalidOperationException, verifique o valor da propriedade CanFreeze do objeto Freezable para determinar se ele pode ser congelado antes de tentar congelá-lo.

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

Quando não for mais necessário modificar um freezable, congelá-lo oferece benefícios de desempenho. Se você congelasse o pincel neste exemplo, o sistema gráfico não precisaria mais monitorá-lo para alterações. O sistema gráfico também pode fazer outras otimizações, pois sabe que o pincel não será alterado.

Nota

Por conveniência, os objetos congeláveis permanecem descongelados, a menos que você os congele explicitamente.

Usando Freezables

Usar um congelável não congelado é como usar qualquer outro tipo de objeto. No exemplo a seguir, a cor de um SolidColorBrush é alterada de amarelo para vermelho depois de ser usada para pintar a tela de fundo de um botão. O sistema gráfico funciona nos bastidores para alterar automaticamente o botão de amarelo para vermelho na próxima vez que a tela for atualizada.

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

Congelando um congelável

Para tornar um Freezable não modificável, chame seu método Freeze. Quando você congela um objeto que contém objetos congeláveis, esses objetos também são congelados. Por exemplo, se você congelar um PathGeometry, os números e segmentos que ele contém também serão congelados.

Um não poderá ser congelado se qualquer uma das seguintes condições for verdadeira:

  • Ele tem propriedades animadas ou associadas a dados.

  • Ele tem propriedades definidas por um recurso dinâmico. (Consulte os recursos XAML para obter mais informações sobre recursos dinâmicos.)

  • Ele contém Freezable sub-objetos que não podem ser congelados.

Se essas condições forem falsas e você não pretende modificar a Freezable, então deve congelá-la para obter os benefícios de desempenho descritos anteriormente.

Depois de chamar o método Freeze de um freezable, ele não poderá mais ser modificado. A tentativa de modificar um objeto congelado faz com que um InvalidOperationException seja gerado. O código a seguir gera uma exceção, pois tentamos modificar o pincel depois que ele é congelado.


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

Para evitar gerar essa exceção, você pode usar o método IsFrozen para determinar se um Freezable está congelado.


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


No exemplo de código anterior, uma cópia modificável foi feita de um objeto congelado usando o método Clone. A próxima seção discute a clonagem com mais detalhes.

Nota

Como um freezable congelado não pode ser animado, o sistema de animação criará automaticamente clones modificáveis de objetos de Freezable congelados quando você tentar animá-los com um Storyboard. Para eliminar a sobrecarga de desempenho causada pela clonagem, deixe um objeto descongelado se você pretende animá-lo. Para obter mais informações sobre como animar com storyboards, consulte a Visão Geral dos Storyboards.

Congelamento da marcação

Para congelar um objeto Freezable declarado na marcação, use o atributo PresentationOptions:Freeze. No exemplo a seguir, o SolidColorBrush é declarado como um recurso de página e congelado. Em seguida, ele é usado para definir a tela de fundo de um botão.

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

Para usar o atributo Freeze, você deve mapear para o namespace de opções de apresentação: http://schemas.microsoft.com/winfx/2006/xaml/presentation/options. PresentationOptions é o prefixo recomendado para mapear este namespace:

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

Como nem todos os leitores XAML reconhecem esse atributo, é recomendável que você use o de atributo mc:Ignorable para marcar o atributo como ignorável:

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

Para mais informações, consulte a página do atributo mc:Ignorable.

"Descongelar" um Freezable

Uma vez congelado, um Freezable nunca pode ser modificado ou descongelado; no entanto, você pode criar um clone descongelado usando o método Clone ou CloneCurrentValue.

No exemplo a seguir, o fundo do botão é definido com um pincel e esse pincel é congelado. Uma cópia descongelada é feita do pincel usando o método Clone. O clone é modificado e usado para alterar o plano de fundo do botão de amarelo para vermelho.

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

Nota

Independentemente de qual método de clone você usa, as animações nunca são copiadas para o novo Freezable.

Os métodos Clone e CloneCurrentValue produzem cópias profundas do Freezable. Se o objeto congelável contiver outros objetos congeláveis congelados, eles também serão clonados e modificáveis. Por exemplo, se você clonar uma PathGeometry congelada para torná-la modificável, as figuras e segmentos que ela contém também serão copiadas e tornadas modificáveis.

Criando sua própria classe freezable

Uma classe que deriva de Freezable ganha as seguintes características.

  • Estados especiais: um estado somente leitura (congelado) e um estado gravável.

  • Segurança de thread: um Freezable congelado pode ser compartilhado entre threads.

  • Notificação de alteração detalhada: Ao contrário de outros DependencyObject, os objetos Freezable fornecem notificações de alteração quando os valores de subpropriedades são alterados.

  • Clonagem fácil: a classe Freezable já implementou vários métodos que produzem clones profundos.

Um Freezable é um tipo de DependencyObjecte, portanto, usa o sistema de propriedades de dependência. Suas propriedades de classe não precisam ser propriedades de dependência, mas o uso de propriedades de dependência reduzirá a quantidade de código que você precisa escrever, pois a classe Freezable foi projetada com as propriedades de dependência em mente. Para obter mais informações sobre o sistema de propriedades de dependência, consulte a visão geral das propriedades de dependência .

Cada subclasse Freezable deve substituir o método CreateInstanceCore. Se sua classe utiliza propriedades de dependência para todos os seus dados, você já terminou.

Se sua classe contiver membros de dados de propriedade que não são de dependência, você também deverá substituir os seguintes métodos:

Você também deve observar as seguintes regras para acessar e gravar em campos de dados que não são propriedades de dependência:

  • No início de qualquer API que leia membros de dados de propriedades não dependentes, chame o método ReadPreamble.

  • No início de qualquer API que grava membros de dados que não sejam propriedades de dependência, chame o método WritePreamble. (Depois de chamar WritePreamble em uma API, você não precisa fazer uma chamada adicional para ReadPreamble se também ler dados de membros de propriedade que não são de dependência.)

  • Chame o método WritePostscript antes de sair dos métodos que gravam em dados de membros de propriedade não dependente.

Se sua classe contiver membros de dados que não são propriedades de dependência, sendo objetos DependencyObject, você também deverá chamar o método OnFreezablePropertyChanged sempre que alterar um dos valores, mesmo se estiver definindo o valor do membro como null.

Nota

É muito importante que você comece cada método Freezable substituído por uma chamada para a implementação base.

Para obter um exemplo de uma classe Freezable personalizada, consulte o Exemplo de Animação Personalizada.

Consulte também