Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Entender o comportamento intrínseco de objetos WPF ajudará você a fazer as compensações certas entre a funcionalidade e o desempenho.
Não remover manipuladores de eventos em objetos pode manter objetos vivos
O delegado que um objeto passa ao seu evento é efetivamente uma referência a esse objeto. Portanto, os manipuladores de eventos podem manter os objetos vivos por mais tempo do que o esperado. Ao executar a limpeza de um objeto que foi registrado para escutar um evento, é essencial remover esse delegate antes de liberar o objeto. Manter objetos desnecessários ativos aumenta o uso de memória do aplicativo. Isso é especialmente verdadeiro quando o objeto é a raiz de uma árvore lógica ou de uma árvore visual.
O WPF apresenta um padrão de ouvinte de eventos fraco para eventos que podem ser úteis em situações em que as relações de tempo de vida do objeto entre a origem e o ouvinte são difíceis de acompanhar. Alguns eventos existentes do WPF usam esse padrão. Se você estiver implementando objetos com eventos personalizados, esse padrão poderá ser útil para você. Para obter detalhes, consulte Padrões de Eventos Fracos.
Há várias ferramentas, como o CLR Profiler e o Visualizador do Conjunto de Trabalho, que podem fornecer informações sobre o uso de memória de um processo especificado. O CLR Profiler inclui várias exibições muito úteis do perfil de alocação, incluindo um histograma de tipos alocados, grafos de alocação e chamada, uma linha de tempo mostrando coletas de lixo de várias gerações e o estado resultante do heap gerenciado após essas coleções e uma árvore de chamadas mostrando alocações por método e cargas de assembly. Para obter mais informações, consulte Desempenho.
Propriedades e objetos de dependência
Em geral, acessar uma propriedade de dependência de um DependencyObject não é mais lento do que acessar uma propriedade CLR. Embora haja uma pequena sobrecarga de desempenho para definir um valor de propriedade, obter um valor é tão rápido quanto obter o de uma propriedade CLR. Compensar a pequena sobrecarga de desempenho é o fato de que as propriedades de dependência dão suporte a recursos robustos, como associação de dados, animação, herança e estilo. Para obter mais informações, consulte Visão geral das propriedades de dependência.
Otimizações de DependencyProperty
Você deve definir as propriedades de dependência em seu aplicativo com muito cuidado. Se o seu DependencyProperty afetar apenas as opções de metadados do tipo de renderização, em vez de outras opções de metadados, como AffectsMeasure, você deve marcá-los como tal pela substituição dos metadados. Para obter mais informações sobre como substituir ou obter metadados de propriedade, consulte Metadados de propriedade de dependência.
Pode ser mais eficiente ter um manipulador de alteração de propriedade invalidar a medida, organizar e renderizar passagens manualmente se nem todas as alterações de propriedade realmente afetarem a medida, a organização e a renderização. Por exemplo, você pode decidir renderizar novamente um plano de fundo somente quando um valor for maior que um limite definido. Nesse caso, o manipulador de alteração de propriedade só invalidaria a renderização quando o valor excedesse o limite definido.
Tornar um DependencyProperty herdável não é sem custo
Por padrão, as propriedades de dependência registradas são não herdáveis. No entanto, você pode tornar explicitamente qualquer propriedade herdável. Embora esse seja um recurso útil, converter uma propriedade para ser herdável afeta o desempenho aumentando o período de tempo para invalidação da propriedade.
Uso do RegisterClassHandler com cuidado
Embora a chamada RegisterClassHandler permita que você salve o estado da instância, é importante estar ciente de que o manipulador é chamado em todas as instâncias, o que pode causar problemas de desempenho. RegisterClassHandler Use somente quando o aplicativo exigir que você salve o estado da instância.
Definir o valor padrão para uma DependencyProperty durante o registro
Ao criar um DependencyProperty que requer um valor padrão, defina o valor usando os metadados padrão passados como parâmetro para o método Register do DependencyProperty. Use essa técnica em vez de definir o valor da propriedade em um construtor ou em cada instância de um elemento.
Definir o valor PropertyMetadata usando Register
Ao criar um DependencyProperty, você tem a opção de definir o PropertyMetadata usando os métodos Register ou OverrideMetadata. Embora seu objeto possa ter um construtor estático para chamar OverrideMetadata, essa não é a solução ideal e afetará o desempenho. Para obter melhor desempenho, defina o PropertyMetadata durante a chamada como Register.
Objetos Congeláveis
Um Freezable é um tipo especial de objeto que tem dois estados: descongelado e congelado. Congelar objetos sempre que possível melhora o desempenho do aplicativo e reduz seu conjunto de trabalho. Para obter mais informações, consulte Visão Geral dos Objetos Freezable.
Cada Freezable tem um evento Changed que é gerado sempre que é alterado. No entanto, as notificações de alteração são caras em termos de desempenho do aplicativo.
Considere o exemplo a seguir no qual cada um Rectangle usa o mesmo Brush objeto:
rectangle_1.Fill = myBrush;
rectangle_2.Fill = myBrush;
rectangle_3.Fill = myBrush;
// ...
rectangle_10.Fill = myBrush;
rectangle_1.Fill = myBrush
rectangle_2.Fill = myBrush
rectangle_3.Fill = myBrush
' ...
rectangle_10.Fill = myBrush
Por padrão, o WPF fornece um manipulador de eventos para o SolidColorBrush evento do Changed objeto para invalidar a Rectangle propriedade do Fill objeto. Nesse caso, cada vez que o evento SolidColorBrush do Changed precisa ser acionado, é necessário invocar a função de callback para cada Rectangle — o acúmulo dessas invocações de funções de callback impõe uma penalidade de desempenho significativa. Além disso, demanda muita performance adicionar e remover manipuladores neste momento, já que o aplicativo teria que percorrer toda a lista para fazer isso. Se o cenário do seu aplicativo nunca mudar SolidColorBrush, você pagará desnecessariamente o custo de manutenção de Changed manipuladores de eventos.
Congelar um Freezable pode melhorar seu desempenho, pois ele não precisa mais gastar recursos na manutenção de notificações de alteração. A tabela abaixo mostra o tamanho de um SolidColorBrush simples quando sua propriedade IsFrozen é definida como true
, em comparação com quando não é. Isso pressupõe a aplicação de um pincel à Fill propriedade de dez Rectangle objetos.
Estado | Tamanho |
---|---|
Congelado SolidColorBrush | 212 Bytes |
Não congelado SolidColorBrush | 972 Bytes |
O exemplo de código a seguir demonstra esse conceito:
Brush frozenBrush = new SolidColorBrush(Colors.Blue);
frozenBrush.Freeze();
Brush nonFrozenBrush = new SolidColorBrush(Colors.Blue);
for (int i = 0; i < 10; i++)
{
// Create a Rectangle using a non-frozed Brush.
Rectangle rectangleNonFrozen = new Rectangle();
rectangleNonFrozen.Fill = nonFrozenBrush;
// Create a Rectangle using a frozed Brush.
Rectangle rectangleFrozen = new Rectangle();
rectangleFrozen.Fill = frozenBrush;
}
Dim frozenBrush As Brush = New SolidColorBrush(Colors.Blue)
frozenBrush.Freeze()
Dim nonFrozenBrush As Brush = New SolidColorBrush(Colors.Blue)
For i As Integer = 0 To 9
' Create a Rectangle using a non-frozed Brush.
Dim rectangleNonFrozen As New Rectangle()
rectangleNonFrozen.Fill = nonFrozenBrush
' Create a Rectangle using a frozed Brush.
Dim rectangleFrozen As New Rectangle()
rectangleFrozen.Fill = frozenBrush
Next i
Manipuladores modificados em Freezables não congelados podem manter objetos ativos
O delegado que um objeto passa para o evento de Freezable um Changed objeto é efetivamente uma referência a esse objeto. Portanto, Changed os manipuladores de eventos podem manter os objetos vivos por mais tempo do que o esperado. Ao executar a limpeza de um objeto registrado para ouvir o evento de Freezable do objeto Changed, é essencial remover essa delegação antes de liberar o objeto.
O WPF também conecta Changed eventos internamente. Por exemplo, todas as propriedades de dependência que tomam Freezable como valor escutarão Changed eventos automaticamente. A Fill propriedade, que usa um Brush, ilustra esse conceito.
Brush myBrush = new SolidColorBrush(Colors.Red);
Rectangle myRectangle = new Rectangle();
myRectangle.Fill = myBrush;
Dim myBrush As Brush = New SolidColorBrush(Colors.Red)
Dim myRectangle As New Rectangle()
myRectangle.Fill = myBrush
Na atribuição de myBrush
a myRectangle.Fill
, um delegado apontando de volta para o objeto Rectangle será adicionado ao evento do objeto SolidColorBrush. Isso significa que o código a seguir não torna o myRect
elegível para coleta de lixo.
myRectangle = null;
myRectangle = Nothing
Neste caso myBrush
ainda está mantendo myRectangle
vivo e retomará a comunicação com ele quando disparar o evento Changed. Observe que atribuir myBrush
à propriedade Fill de um novo Rectangle simplesmente adicionará outro manipulador de eventos a myBrush
.
A maneira recomendada de limpar esses tipos de objetos é remover o Brush da propriedade Fill, o que, por sua vez, removerá o manipulador de eventos Changed.
myRectangle.Fill = null;
myRectangle = null;
myRectangle.Fill = Nothing
myRectangle = Nothing
Virtualização da Interface do Usuário
O WPF também fornece uma variação do elemento StackPanel que "virtualiza" automaticamente o conteúdo filho vinculado a dados. Nesse contexto, a palavra virtualizar refere-se a uma técnica pela qual um subconjunto de objetos é gerado a partir de um número maior de itens de dados com base em quais itens são visíveis na tela. É intensivo, tanto em termos de memória quanto de processador, gerar um grande número de elementos de interface do usuário quando apenas alguns podem estar na tela em um determinado momento. VirtualizingStackPanel (por meio da funcionalidade fornecida por VirtualizingPanel) calcula itens visíveis e funciona com o ItemContainerGenerator de um ItemsControl (como ListBox ou ListView) para criar apenas elementos para itens visíveis.
Como otimização de desempenho, os objetos visuais desses itens só são gerados ou mantidos vivos se estiverem visíveis na tela. Quando eles não estiverem mais na área visível do controle, os objetos visuais poderão ser removidos. Isso não deve ser confundido com a virtualização de dados, em que os objetos de dados não estão todos presentes na coleção local, e sim transmitidos conforme necessário.
A tabela a seguir mostra o tempo decorrido adicionando e renderizando 5.000 TextBlock elementos a um StackPanel e um VirtualizingStackPanel. Nesse cenário, as medidas representam o tempo entre anexar uma cadeia de caracteres de texto à ItemsSource propriedade de um ItemsControl objeto ao momento em que os elementos do painel exibem a cadeia de caracteres de texto.
Painel de host | tempo de renderização (ms) |
---|---|
StackPanel | 3210 |
VirtualizingStackPanel | 46 |
Consulte também
.NET Desktop feedback