O que é o MVVM?
Aplicativos .NET MAUI que não usam MVVM geralmente têm mais código em seus arquivos code-behind. Os arquivos code-behind no .NET MAUI seguem este padrão: {something}.xaml.cs. A maioria do código no arquivo code-behind geralmente controla o comportamento da interface do usuário. O comportamento da interface do usuário pode incluir qualquer coisa que acontecer na interface do usuário, como a alteração de uma cor ou texto. E pode incluir qualquer coisa que acontecer por causa da interface do usuário, incluindo manipuladores de clique de botão.
Um problema com essa abordagem é que é difícil escrever testes de unidade para arquivos code-behind. Os arquivos code-behind frequentemente assumem um estado de aplicativo criado pela análise do XAML ou até mesmo criado por outras páginas. Essas condições são difíceis de manipular para um executor de teste de unidade que talvez nem esteja em execução em um dispositivo móvel, muito menos com uma interface do usuário. Portanto, os testes de unidades são raramente capazes de testar os comportamentos da interface do usuário nesses arquivos.
Mas é aqui que o padrão MVVM é útil. Quando usado corretamente, o padrão MVVM resolve esses problemas movendo a maioria da lógica de comportamento da interface do usuário para classes testáveis em unidade que são chamadas de viewmodels. O padrão MVVM é mais comumente usado com estruturas que dão suporte à associação de dados. Isso porque com o .NET MAUI, você pode associar dados de cada elemento da interface do usuário a um viewmodel
e eliminar ou praticamente eliminar o código em uma exibição (view) ou code-behind.
Quais são as partes de um aplicativo MVVM?
Embora o viewmodel
seja a parte única do padrão MVVM (Model-View-ViewModel), esse padrão também define uma parte modelo (model) e uma parte exibição (view). As definições dessas partes são consistentes com alguns outros padrões comuns, como um MVC (Model-View-Controller).
O que é um modelo?
Em um aplicativo MVVM, o termo modelo é usado para denotar seus dados e operações de negócios. O modelo não se envolve com a apresentação do usuário do aplicativo.
Uma regra útil para determinar qual código pertence ao modelo é que ele deve ser portátil entre diferentes plataformas. De um aplicativo móvel a uma interface Web ou até mesmo um programa de linha de comando, usando o mesmo modelo em todas as instâncias. Isso não está relacionado à forma como as informações são exibidas para o usuário.
Ao pensar sobre o aplicativo de RH do nosso cenário, o modelo pode incluir uma classe Employee
e uma classe Department
que contenham dados e lógica sobre essas entidades. O modelo também pode incluir itens como uma classe EmployeeRepository
que contém a lógica de persistência. Alguns outros padrões de design de software considerariam coisas como repositórios como algo separado do modelo. Mas no contexto do MVVM, normalmente nos referimos a qualquer lógica de negócios ou os dados de negócios como parte do modelo.
Aqui estão dois exemplos de um modelo em C#:
public class Employee
{
public int Id { get; }
public string Name { get; set; }
public Employee Supervisor { get; set; }
public DateTime HireDate { get; set; }
public void ClockIn() { ... }
}
public class EmployeeRepository
{
public IList<Employee> QueryEmployees() { ... }
...
}
O que é uma exibição?
O código exibição controla coisas que interagem diretamente com o usuário, como controles como botões e campos de entrada, bem como outros elementos puramente visuais como temas, estilos e fontes.
No .NET MAUI, você não precisa escrever nenhum código C# para gerar uma exibição por conta própria. Em vez disso, você frequentemente define suas exibições por meio de arquivos XAML. Claro, existem situações que exigem um controle de usuário personalizado, no qual você cria sua própria exibição por meio de código.
O que é um viewmodel
?
Isso nos leva de volta ao viewmodel
. O viewmodel
é o intermediário entre nossa lógica de negócios (modelo) e nossas exibições (interface do usuário).
Pense no que um viewmodel
pode fazer pelo aplicativo de RH. Digamos que haja uma exibição que exibe o tempo de férias disponível de um funcionário, e você deseja que o saldo de férias seja exibido como "2 semanas, 3 dias, 4 horas." Mas a lógica de negócios no modelo fornece esse mesmo valor como 13,5 dias, um número decimal que representa o total de dias em um dia de trabalho de 8 horas. O modelo do objeto pode parecer com a seguinte lista:
Modelo – A classe
Employee
, que inclui um método:public decimal GetVacationBalanceInDays() { //Some math that calculates current vacation balance ... }
ViewModel – Uma classe
EmployeeViewModel
que tem uma propriedade assim:public class EmployeeViewModel { private Employee _model; public string FormattedVacationBalance { get { decimal vacationBalance = _model.GetVacationBalanceInDays(); ... // Some logic to format and return the string as "X weeks, Y days, Z hours" } } }
Exibição – Uma página XAML que contém um único rótulo e um botão de fechar. O rótulo tem uma associação com a propriedade do
viewmodel
:<Label Text="{Binding FormattedVacationBalance}" />
Você só precisa que o
BindingContext
para a página seja definido para uma instância deEmployeeViewModel
.
Neste exemplo, o modelo contém a lógica de negócios. Essa lógica não está associada a uma exibição visual ou dispositivo. Você poderia usar a mesma lógica para um dispositivo portátil ou computador desktop. A exibição não sabe nada sobre a lógica de negócios. Os controles da exibição, como o rótulo, sabem como colocar texto na tela, mas não se importam se é um saldo de férias ou uma cadeia de caracteres aleatória. O viewmodel
sabe um pouco dos dois mundos, portanto, ele pode agir como intermediário.
O interessante é como o viewmodel
desempenha a função de intermediário: Ele expõe propriedades às quais uma exibição pode se associar. As propriedades públicas são a única forma de um viewmodel
fornecer dados. Isso nos leva a entender por que ele é chamado de viewmodel
. Enquanto o "modelo" no MVVM representa a estrutura, os dados e a lógica dos processos de negócios, o viewmodel
representa a estrutura, os dados e a lógica que a exibição exige.
Como a exibição (view) funciona com o viewmodel
?
Quando você observa a relação que o viewmodel
tem com o modelo, é uma relação padrão de classe para classe. O viewmodel
tem uma instância do modelo e expõe os aspectos desse modelo para a exibição através de propriedades. Mas, como uma exibição obtém e define propriedades no viewmodel
? Quando o viewmodel
é alterado, como a exibição atualiza os controles visuais com novos valores? A resposta é associação de dados.
As propriedades do viewmodel
são lidas no momento em que o objeto é associado à exibição. No entanto, a associação não tem como saber se as propriedades do viewmodel
foram alteradas após a associação ter sido aplicada à exibição. Alterar o viewmodel
não propaga automaticamente o novo valor por meio da associação para a exibição. Para atualizar do viewmodel
para a exibição, o viewmodel
deve implementar a interface System.ComponentModel.INotifyPropertyChanged
.
A interface INotifyPropertyChanged
declara um único evento chamado PropertyChanged
. Ele recebe um único parâmetro, o nome da propriedade que alterou seu valor. O sistema de associação usado no .NET MAUI entende essa interface e ouve esse evento. Quando uma propriedade é alterada no viewmodel
e dispara o evento, a associação notifica o destino da alteração.
Pense em como isso funciona no aplicativo de RH com um viewmodel
de funcionário. O viewmodel
tem propriedades que representam um funcionário, como o nome do funcionário. O viewmodel
implementa a interface INotifyPropertyChanged
e, quando a propriedade Name
é alterada, gera o evento PropertyChanged
, assim:
using System.ComponentModel;
public class EmployeeViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
private Employee _model;
public string Name
{
get {...}
set
{
_model.Name = value;
OnPropertyChanged(nameof(Name))
}
}
protected void OnPropertyChanged(string propertyName) =>
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
A exibição que descreve os detalhes do funcionário possui um controle de rótulo que está vinculado à propriedade Name
do viewmodel
:
<Label Text="{Binding Name}" />
Quando a propriedade Name
é alterada no viewmodel
, o evento PropertyChanged
é gerado com o nome dessa propriedade. A associação ouve o evento e então notifica o rótulo que a propriedade Name
foi alterada. Então, a propriedade Text
do rótulo é atualizada com o valor mais recente.