Introdução
No MVVM, a prática usual é fazer com que as Views encontrem seus ViewModels resolvendo-os a partir de um contêiner de injeção de dependência (DI). Isso acontece automaticamente quando o contêiner é solicitado a fornecer (resolver) uma instância da classe View. O contêiner injeta ViewModel em View chamando um construtor de View que aceita um parâmetro ViewModel; este esquema é denominado inversão de controle (IoC).
Benefícios do DI
O principal benefício aqui é que o contêiner pode ser configurado em tempo de execução com instruções sobre como resolver os tipos que solicitamos dele. Isso permite maior testabilidade, instruindo-o a resolver os tipos (Views e ViewModels) que usamos quando nosso aplicativo realmente é executado, mas instruindo-o de forma diferente ao executar os testes de unidade para o aplicativo. No último caso, o aplicativo nem mesmo terá uma IU (não está em execução; apenas os testes estão), portanto, o contêiner resolverá simulações no lugar dos tipos "normais" usados quando o aplicativo é executado.
Problemas decorrentes de DI
Até agora, vimos que a abordagem de DI permite fácil testabilidade para o aplicativo, adicionando uma camada de abstração sobre a criação de componentes do aplicativo. Há um problema com essa abordagem: ela não funciona bem com designers visuais como o Microsoft Expression Blend.
O problema é que, tanto na execução normal do aplicativo quanto na execução do teste de unidade, alguém precisa configurar o contêiner com instruções sobre quais tipos resolver; além disso, alguém tem que pedir ao contêiner para resolver as visualizações para que os ViewModels possam ser injetados nelas.
No entanto, em tempo de design não há nenhum código nosso rodando . O designer tenta usar reflexão para criar instâncias de nossas visualizações, o que significa que:
- Se o construtor de View requer uma instância de ViewModel, o designer não será capaz de instanciar a View - ocorrerá um erro de alguma maneira controlada
- Se a View tem um construtor sem parâmetros, a View será instanciada, mas assim
DataContext
será null
, teremos uma view "vazia" no designer - o que não é muito útil
Insira ViewModelLocator
O ViewModelLocator é uma abstração adicional usada assim:
- O próprio View instancia um ViewModelLocator como parte de seus recursos e vincula seu DataContext à propriedade ViewModel do localizador
- O localizador de alguma forma detecta se estamos no modo de design
- Se não estiver no modo de design, o localizador retorna um ViewModel que ele resolve do contêiner DI, conforme explicado acima
- Se estiver no modo de design, o localizador retorna um ViewModel "fictício" fixo usando sua própria lógica (lembre-se: não há contêiner em tempo de design!); este ViewModel normalmente vem pré-preenchido com dados fictícios
Claro, isso significa que a View deve ter um construtor sem parâmetros para começar (caso contrário, o designer não será capaz de instanciá-lo).
Resumo
ViewModelLocator é um idioma que permite manter os benefícios do DI em seu aplicativo MVVM enquanto também permite que seu código funcione bem com designers visuais. Isso às vezes é chamado de "capacidade de mesclagem" de seu aplicativo (referindo-se ao Expression Blend).
Depois de digerir o acima, veja um exemplo prático aqui .
Finalmente, usar modelos de dados não é uma alternativa ao uso de ViewModelLocator, mas uma alternativa ao uso de pares View / ViewModel explícitos para partes de sua IU. Freqüentemente, você pode descobrir que não há necessidade de definir um View para um ViewModel porque você pode usar um modelo de dados em seu lugar.