Preservando ligações e valores registrados no diário

O WPF fornece um sistema de vinculação de dados para sincronizar valores entre visualizações e modelos de visualização.

O WPF também fornece um sistema de navegação que lida com o histórico e pode descarregar páginas que não são exibidas no momento. Ao usar PageFunctions, a propriedade KeepAlive definida como false (o padrão), os valores de todas as propriedades de dependência com o sinalizador de diário são salvos na entrada do histórico de forma que os valores possam ser colocados de volta nos controles se o usuário usar a entrada do histórico para retornar para a página.

Infelizmente, essas duas coisas não podem ser usadas juntas . O problema parece ser o provedor de estado de histórico automático restaura os valores para a página usando DependencyObject.SetValue.

A sequência parece funcionar assim:

  1. A página é construída com ligações.
  2. O serviço de navegação restaura o estado da página, sobrescrevendo as ligações.
  3. A página é inserida na árvore lógica.
  4. O evento de carregamento da página é disparado.
  5. A página é renderizada, mas os valores não são sincronizados com o modelo de visualização e a validação não ocorre.

Chango posta uma solução para o problema de ligação, mas não é perfeita. Se você criar uma subclasse dos controles e desativar o sinalizador de diário, a vinculação será preservada, mas os valores não serão preservados, a menos que o modelo de visualização também seja preservado.

Não encontrei nenhuma boa maneira de substituir o comportamento que usa DependencyObject.SetValue, mas encontrei uma solução alternativa mesmo assim.

  1. Subclasse os controles para remover o sinalizador de diário. Você provavelmente desejará extrair o código que remove o sinalizador em um método auxiliar em uma classe estática.

  2. Ao mesmo tempo em que remove o sinalizador de Diário de uma propriedade, cria outra propriedade do mesmo tipo, pertencente à sua subclasse, com o sinalizador de Diário.

  3. Quando a propriedade original for alterada, defina o valor da nova propriedade. Quando a nova propriedade for alterada, defina o valor da propriedade original. Você precisará evitar a reentrada aqui, caso contrário, você repetirá propriedades de configuração infinita.

  4. Isso deve gerar exatamente o mesmo comportamento que o WPF já estava fornecendo. Altere o código que define a propriedade original para verificar se há uma expressão de vinculação na propriedade. Se houver uma expressão de ligação, use .SetCurrentValue e atualize a fonte de ligação.

Isso deve funcionar para alguns casos, mas não funcionou para o meu. Meu modelo de visualização está disponível apenas após a página ter sido inserida na árvore lógica, o que significa que a fonte em todas as ligações é nula no momento em que o valor é restaurado, o que faz com que todos os valores sejam redefinidos para os padrões quando o modelo de visualização o faz carga.

Além disso, tive que verificar se o FrameworkElement foi carregado antes de definir o valor. Se o elemento não foi carregado, um manipulador de eventos único precisa ser registrado para o evento Load e esse manipulador de eventos precisa usar Dispatcher.BeginInvoke com a prioridade Load para enfileirar outro delegado que faz a configuração real. O Dispatcher.BeginInvoke ocorre porque a configuração do valor durante o evento Load parece causar problemas com o estado inicial do adorno de validação.

Isso parece funcionar bem para o que eu preciso. Isso não funcionará se você tiver interdependências entre as propriedades definidas pelo usuário em seu modelo de visualização, porque os valores serão atribuídos em uma ordem indefinida. É possível defini-los em uma ordem específica se você realmente quiser, mas parece que seria melhor tentar remover as interdependências.