O padrão MVVM ajuda a separar claramente a lógica de negócios e apresentação de um aplicativo de sua interface de usuário (UI). Manter uma separação clara entre a lógica do aplicativo e a interface do usuário ajuda a resolver vários problemas de desenvolvimento e facilita o teste, a manutenção e a evolução de um aplicativo. Ele também pode melhorar significativamente as oportunidades de reutilização de código e permite que desenvolvedores e designers de interface do usuário colaborem mais facilmente ao desenvolver suas respectivas partes de um aplicativo.

Além de entender as responsabilidades de cada componente, também é importante entender como eles interagem. Em um alto nível, a view “conhece” o view model e o view model “conhece” o model, mas o model não conhece o view model e o view model não conhece a view. Portanto, o view model isola a view do model e permite que o model evolua independentemente da view.

A view é responsável por definir a estrutura, layout e aparência do que o usuário vê na tela. Idealmente, cada view é definida em XAML, com um code-behind limitado que não contém a lógica de negócio. No entanto, em alguns casos, o code-behind pode conter lógica de interface do usuário que implementa um comportamento visual difícil de expressar em XAML, como animações.

Dica:

Evite habilitar e desabilitar elementos de interface no code-behind.

O view model implementa propriedades e comandos aos quais a view pode vincular dados e notifica a view de quaisquer alterações de estado por meio de eventos de notificação de alteração. As propriedades e comandos que o view model fornece definem a funcionalidade a ser oferecida pela interface do usuário, mas a view determina como essa funcionalidade deve ser exibida.

Embora seja possível expor a implementação real da interface ICommand do view model (por exemplo, Command<T> ou RelayCommand), é recomendável expor seus comandos publicamente como ICommand. Dessa forma, se for necessário alterar a implementação posteriormente, ela poderá ser facilmente trocada.

Dicas:

  • Mantenha a interface de usuário com operações assíncronas.
  • Centralize conversões de dados em uma camada de conversão.
  • Para coleções utilize ObservableCollection<T>.

As classes model são classes não visuais que encapsulam os dados do aplicativo. Portanto, o model pode ser pensado como uma representação do modelo de domínio do aplicativo, que geralmente inclui um modelo de dados junto com a lógica de negócio e validação. Exemplos de objetos de modelo incluem objetos de transferência de dados (DTOs), objetos CLR antigos simples (POCOs) e objetos de entidade e proxy gerados.

Os aplicativos devem ser arquitetados para o uso correto da propriedade PropertyChanged, atendendo aos seguintes requisitos:

  • Sempre disparar um evento PropertyChanged para quaisquer propriedades calculadas cujos valores sejam usados ​​por outras propriedades no model ou view model.
  • Sempre disparar um evento PropertyChanged no final do método que faz uma alteração de propriedade, ou quando se sabe que o objeto está em um estado seguro. Gerar o evento interrompe a operação invocando os manipuladores do evento de forma síncrona. Se isso acontecer no meio de uma operação, poderá expor o objeto a funções de retorno de chamada quando estiver em um estado não seguro e parcialmente atualizado. Além disso, é possível que mudanças em cascata sejam acionadas por eventos PropertyChanged. As alterações em cascata geralmente exigem que as atualizações sejam concluídas antes que a alteração em cascata seja segura para execução.
  • Nunca disparar um evento PropertyChanged se a propriedade não for alterada. Isso significa que você deve comparar os valores antigos e novos antes de gerar o evento PropertyChanged.
  • Nunca disparar um evento PropertyChanged durante o construtor de um view model se você estiver inicializando uma propriedade. Os controles vinculados a dados na view não estarão assinados para receber notificações de alteração neste momento.
  • Nunca disparar mais de um evento PropertyChanged com o mesmo argumento de nome de propriedade em uma única invocação síncrona de um método público de uma classe. Por exemplo, dada uma propriedade NumberOfItems cujo armazenamento de apoio é o campo _numberOfItems, se um método incrementa _numberOfItems cinquenta vezes durante a execução de um loop, ele só deve gerar notificação de alteração de na propriedade NumberOfItems uma vez, depois que todo o trabalho estiver concluído. Para métodos assíncronos, gere o evento PropertyChanged para um determinado nome de propriedade em cada segmento síncrono de uma cadeia de continuação assíncrona.