Os controles do Silverlight devem ser recarregados nas páginas?
Alguns meses atrás, comecei a desenvolver um aplicativo Silverlight por conta própria. Eu rapidamente descobri que não conseguia obter a coleta de lixo esperada para a maioria dos meus controles. Eu lutei por cerca de uma semana com o gerenciador de perfis de memória WinDBG e ANTS e, em seguida, encontrei o fio "DataTemplate memory leak" no fórum do Silverlight (http://forums.silverlight.net/forums/t/171739.aspx).
Dado que tantas pessoas pareciam estar frustradas com vários problemas de memória, decidi adiar mais investigações sobre a situação da memória até que o problema mais óbvio fosse resolvido.
Enfim, agora estou analisando o problema novamente e percebo que o problema que estou tendo é muito mais fundamental do que eu pensava. Eu simplesmente não tenho um paradigma para escrever controles colecionáveis de lixo do Silverlight quando: a) o controle tem propriedades de dependência que podem ser vinculadas eb) o controle pode ser descarregado de um controle e, posteriormente, carregado novamente.
Estou começando a pensar que a segunda dessas demandas é grande demais. alguém pode confirmar isso?
Para dar um pouquinho mais de detalhes, o padrão mais robusto que posso criar para escrever bons controles Silverlight colecionáveis por lixo é o seguinte:
1) Quando um modelo de controle é aplicado (na substituição OnApplyTemplate), eu configuro quaisquer ligações internas entre propriedades locais e TemplateParts. Por exemplo, eu posso configurar uma Ligação entre uma propriedade local chamada CanSearch e um botão.
if (x_Button_Search != null)
{
Binding b = new Binding("CanSearch");
b.Source = this;
this.x_Button_Search.SetBinding(Button.IsEnabledProperty, b);
}
2) Quando o controle gera o evento Unloaded, limpe as ligações internas e desconecte todos os manipuladores de eventos.
if (x_Button_Search != null)
{
this.x_Button_Search.ClearValue(Button.IsEnabledProperty);
}
Essa parece ser a maneira mais limpa de garantir que não existam referências finais entre o elemento x_Button_Search e o Control. Não sei se isso é estritamente necessário.
3) Novamente, quando o Controle gera o evento Unloaded, eu limpo as ligações às propriedades de dependência existentes.
this.ClearValue(SearchParametersProperty);
Se eu não fizer isso, posso causar vazamentos. Por exemplo, se a propriedade SearchParameters estiver vinculada a algum objeto INotifyPropertyChanged, uma referência ao Control permanecerá no evento PropertyChanged no objeto INotifyPropertyChanged ao qual estou vinculado, mesmo após o descarregamento do controle, ou seja, a View permanecerá por enquanto Modelo e isso pode não ser desejado.
4) Eu 'clico' o valor do modelo para que da próxima vez que o controle seja carregado, o modelo seja reaplicado e o método OnApplyTemplate seja acionado novamente.
var oldTemplate = this.Template;
this.Template = null;
this.Template = oldTemplate;
O motivo para fazer 4 é que preciso restabelecer as ligações quando o Controle é recarregado em uma página. No Silverlight, há dois pontos de entrada pelos quais fazer isso: na substituição OnApplyTemplate ou após o controle disparar o evento Loaded. Como desejo impor valores de ligação antes do carregamento do controle (para evitar oscilações), há apenas um ponto de entrada disponível, OnApplyTemplate. Eu tenho que piscar o modelo para forçar o modelo a reaplicar quando o controle é recarregado.
Parece que esse padrão até o ponto 3 é o mínimo necessário para fornecer controles de coleta de lixo.
Meu problema surge quando você deseja descarregar seu controle (removê-lo de um painel, por exemplo) e recarregá-lo posteriormente. Quaisquer propriedades de dependência no controle foram definidas como nulas no ponto 3. Por exemplo, imagine que haja uma ligação na declaração do controle, por exemplo. . Tanto quanto posso dizer, não há como restabelecer essa associação depois que o valor de SearchParameters foi definido como nulo, afinal não faz parte de um modelo. O resultado é que, quando o controle é recarregado, é como se o valor de SearchParameters fosse nulo. Portanto, pulo a etapa 3 do padrão e obtenho um controle recarregável que não é coletado pelo lixo, ou mantenho 3 e obtenho um controle não recarregável.