Visão geral de objetos congeláveis

Este tópico descreve como usar e criar Freezable objetos com eficiência, 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 Congelável?

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

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

Embora a classe tenha muitos aplicativos, a Freezable maioria dos Freezable objetos no Windows Presentation Foundation (WPF) estão relacionados ao subsistema de elementos gráficos.

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

Por exemplo, suponha que você crie um pincel e use-o para pintar o plano de fundo de um SolidColorBrush 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 gráficos 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 deveria ser pintado, seu pincel de cor sólida, na verdade, não faz a pintura. O sistema de elementos gráficos gera objetos rápidos e de baixo nível para o botão e o pincel e são esses objetos que realmente aparecem na tela.

Se você fosse modificar o pincel, esses objetos de baixo nível precisariam ser regenerados. A classe congelável é o que proporciona a um pincel a capacidade de localizar seus objetos gerados e de nível inferior correspondentes e atualizá-los quando forem alterados. Quando essa capacidade é habilitada, significa que o pincel está "descongelado."

O método de Freeze um congelável permite que você desabilite essa capacidade de atualização automática. Você pode usar esse método para fazer com que o pincel se torne "congelado" ou não modificável.

Observação

Nem todo objeto Congelável pode ser congelado. Para evitar lançar um InvalidOperationException, verifique o valor da propriedade do objeto CanFreeze 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 você não precisa mais modificar um congelável, congelá-lo fornece benefícios de desempenho. Se você congelasse o pincel nesse exemplo, o sistema de elementos gráficos não precisaria mais monitorar as alterações feitas nele. O sistema de elementos gráficos também pode fazer outras otimizações, porque sabe que o pincel não será alterado.

Observação

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

Usando Congeláveis

O uso de um congelável descongelado é como o uso de qualquer outro tipo de objeto. No exemplo a seguir, a cor de um é alterada de amarelo para vermelho depois que ele é usado para pintar o plano de fundo de um SolidColorBrush botão. O sistema de elementos gráficos 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 uma congelável

Para tornar um Freezable não modificável, você chama seu Freeze método. 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, as figuras e segmentos que ele contém também serão congelados.

Um congelável não poderá ser congelado se alguma 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 subobjetos que não podem ser congelados.

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

Depois de chamar o método de Freeze um congelável, ele não poderá mais ser modificado. A tentativa de modificar um objeto congelado faz com que um InvalidOperationException seja lançado. O código a seguir gera uma exceção, pois tenta modificar o pincel depois de ele ter sido 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 lançar essa exceção, você pode usar o IsFrozen método 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 Clone método. A próxima seção aborda a clonagem em mais detalhes.

Observação

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

Congelamento na marcação

Para congelar um Freezable objeto declarado na marcação, use o PresentationOptions:Freeze atributo. No exemplo a seguir, a 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. O PresentationOptions é o prefixo recomendado para mapear este namespace:

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

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

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

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

"Descongelando" um Congelável

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

No exemplo a seguir, a tela de fundo do botão é definida com um pincel e, então, esse pincel é congelado. Uma cópia descongelada é feita do pincel usando o Clone método. O clone é modificado e usado para alterar a tela de fundo do botão de amarela para vermelha.

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

Observação

Independentemente do método de clonagem usado, as animações nunca são copiadas para o novo Freezable.

Os Clone métodos e CloneCurrentValue produzem cópias profundas do congelável. Se o congelável contém outros objetos congeláveis congelados, eles também são clonados e tornados modificáveis. Por exemplo, se você clonar um congelado PathGeometry para torná-lo modificável, as figuras e segmentos que ele contém também serão copiados e tornados modificáveis.

Criando sua própria classe Congelável

Uma classe que deriva de Freezable ganha os seguintes recursos.

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

  • Segurança de rosca: um congelado Freezable pode ser compartilhado entre fios.

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

  • Fácil clonagem: a classe Congelável tem vários métodos já implementados que produzem clones profundos.

A Freezable é um tipo de , e, portanto, usa o sistema de propriedades de DependencyObjectdependê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, porque a Freezable classe foi projetada com propriedades de dependência em mente. Para obter mais informações sobre o sistema de propriedade de dependência, consulte o Visão geral de propriedades de dependência.

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

Se sua classe contém membros de dados de propriedade que não são de dependência, você também deve substituir os métodos a seguir:

Você também deve observar as seguintes regras para o acesso e gravação em membros de dados que não são propriedades de dependência:

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

  • No início de qualquer API que grava membros de dados de propriedade não dependente, chame o WritePreamble método. (Depois de chamar WritePreamble uma API, você não precisará fazer uma chamada adicional se ReadPreamble também ler membros de dados de propriedade não dependente.)

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

Se sua classe contiver membros de dados de propriedade não dependente que são DependencyObject objetos, você também deverá chamar o método sempre que alterar um de seus valores, mesmo que esteja definindo o OnFreezablePropertyChanged membro como null.

Observação

É muito importante que você comece cada Freezable método que você substituir com uma chamada para a implementação base.

Para obter um exemplo de uma classe personalizada, consulte o Exemplo de animação personalizadaFreezable.

Confira também