Usar um modelo de exibição
Depois de aprender sobre os componentes que compõem o padrão MVVM, você provavelmente descobriu que o modelo e a visualização eram fáceis de definir. Vamos explorar como usar o viewmodel, para definir melhor seu papel no padrão.
Expor propriedades à interface do usuário
Como no exemplo anterior, os modelos de visualização geralmente dependem de modelos para a maioria de seus dados e qualquer lógica de negócios. Mas é o viewmodel que formata, converte e enriquece os dados de qualquer maneira que a exibição atual exija.
Formatar usando um viewmodel
Você já viu um exemplo de formatação com tempo de férias. Formatação de data, codificação de caracteres e serialização são exemplos de como o viewmodel pode formatar dados do modelo.
Converter usando um viewmodel
Muitas vezes, o modelo fornece informações de forma indireta. Mas o viewmodel pode corrigir isso. Por exemplo, suponha que você queira mostrar na tela se um funcionário é um supervisor. Mas o nosso Employee
modelo não nos diz isso diretamente. Em vez disso, você tem que inferir esse fato com base em se a pessoa tem outras pessoas relatando a ela. Suponha que o modelo tenha esta propriedade:
public IList<Employee> DirectReports
{
get
{
...
}
}
Se a lista estiver vazia, você pode inferir que não se trata Employee
de um supervisor. Neste caso, EmployeeViewModel
inclui a propriedade IsSupervisor
que fornece essa lógica:
public bool IsSupervisor => _model.DirectReports.Any();
Enriqueça usando um viewmodel
Às vezes, um modelo pode fornecer apenas uma ID para dados relacionados. Ou talvez seja necessário ir para várias classes de modelo para correlacionar os dados necessários para uma única tela. O viewmodel fornece um local ideal para executar essas tarefas também. Suponha que você queira mostrar todos os projetos que um funcionário está gerenciando no momento. Esses dados não fazem parte da Employee
classe de modelo. Ele pode ser acessado olhando para a CompanyProjects
classe de modelo. A nossa EmployeeViewModel
, como sempre, expõe o seu trabalho como propriedade pública:
public IEnumerable<string> ActiveProjects => CompanyProjects.All
.Where(p => p.Owner == _model.Id && p.IsActive)
.Select(p => p.Name);
Usar propriedades de passagem com um viewmodel
Freqüentemente um viewmodel precisa exatamente da propriedade que o modelo fornece. Para essas propriedades, o viewmodel apenas passa os dados por:
public string Name
{
get => _model.Name;
set => _model.Name = value;
}
Definir o escopo para o viewmodel
Você pode usar um viewmodel em qualquer nível onde haja uma exibição. Uma página geralmente tem um modelo de visualização, mas também subvisualizações da página. Um motivo comum para modelos de exibição aninhados é quando a página exibe um ListView
na página. A lista tem um viewmodel que representa a coleção, como EmployeeListViewModel
. Cada elemento da lista é um EmployeeViewModel
arquivo .
Também é comum ter um modelo de exibição de nível superior que contém dados e estado para todo o aplicativo, mas não está associado a nenhuma página específica. Esse modelo de visualização é comumente usado para manter o item "ativo". Considere o exemplo que acabamos ListView
de descrever. Quando o usuário seleciona uma linha de funcionário, esse funcionário representa o item atual. Se o usuário navegar para uma página de detalhes ou selecionar um botão da barra de ferramentas enquanto essa linha estiver selecionada, a ação ou exibição deverá ser para esse funcionário. Uma maneira elegante de lidar com esse cenário é ter os ListView.SelectItem
dados vinculados a uma propriedade que a barra de ferramentas ou a página de detalhes também pode acessar. Colocar essa propriedade em um viewmodel central funciona bem.
Identificar quando reutilizar modelos de exibição com modos de exibição
A forma como você define a relação entre o viewmodel e o model e entre o viewmodel e o view é ditada mais pelos requisitos do aplicativo do que pelas regras. O objetivo do viewmodel é fornecer à visualização a estrutura e os dados de que ela precisa. Isso deve orientar as decisões sobre "quão grande" será o escopo de um modelo de visualização.
Viewmodels geralmente refletem de perto a estrutura de uma classe de modelo, e eles têm uma relação um-para-um com essa classe. Você viu um exemplo anteriormente com o EmployeeViewModel
que envolveu e aumentou uma única Employee
instância. Mas nem sempre é uma relação um-para-um. Se o viewmodel for projetado para fornecer o que o modo de exibição precisa, você pode acabar com algo como HRDashboardViewModel
fornecer uma visão geral de um departamento de RH, que não tem relação explícita com nenhum modelo, mas pode usar dados de qualquer classe de modelo.
Da mesma forma, você pode achar que os modelos de exibição e os modos de exibição geralmente têm uma relação um-para-um. Mas este também não é necessariamente o caso. Vamos pensar novamente em um ListView
que mostre uma linha para cada funcionário. Ao selecionar uma das linhas, você vai para uma página de detalhes do funcionário.
A página de listagem tem seu viewmodel com uma coleção. Como sugerido anteriormente, essa coleção poderia ser uma coleção de EmployeeViewModel
objetos. E quando o usuário seleciona uma linha, a EmployeeViewModel
instância pode ser passada para o EmployeeDetailPage
. E a página de detalhes poderia usar isso EmployeeViewModel
como seu BindingContext
.
Este cenário pode ser uma excelente oportunidade para a reutilização do viewmodel. Mas tenha em mente que os modelos de visualização destinam-se a fornecer o que a exibição precisa. Em alguns casos, você pode querer modelos de exibição separados, mesmo que todos sejam baseados na mesma classe de modelo. Neste exemplo, é provável que as ListView
linhas precisem de muito menos informações do que a página de detalhes completa. Se a recuperação dos dados de que a página de detalhes precisa adicionar muita sobrecarga, convém ter ambos e EmployeeListRowViewModel
EmployeeDetailViewModel
modelos que atendam a essas respetivas exibições.
Modelo de objeto Viewmodel
Usar uma classe base que implementa INotifyPropertyChanged
significa que você não precisa reimplementar a interface em cada viewmodel. Considere o aplicativo de RH conforme descrito na parte anterior deste módulo de treinamento. A EmployeeViewModel
classe implementou a INotifyPropertyChanged
interface e forneceu um método auxiliar nomeado OnPropertyChanged
para gerar o PropertyChanged
evento. Outros modelos de exibição no projeto, como aqueles que descrevem recursos atribuídos a um funcionário, também exigiriam INotifyPropertyChanged
uma integração completa com uma exibição.
A biblioteca MVVM Toolkit , parte do .NET Community Toolkit, é uma coleção de tipos padrão, autônomos e leves que fornecem uma implementação inicial para a criação de aplicativos modernos usando o padrão MVVM.
Em vez de escrever sua própria classe base viewmodel, você herda da classe do kit de ferramentas ObservableObject
, que fornece tudo o que você precisa para uma classe base viewmodel. O EmployeeViewModel
pode ser simplificado a partir de:
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));
}
Para o seguinte código:
using Microsoft.Toolkit.Mvvm.ComponentModel;
public class EmployeeViewModel : ObservableObject
{
private string _name;
public string Name
{
get => _name;
set => SetProperty(ref _name, value);
}
}
O código pode ser ainda mais simplificado usando geradores de código-fonte fornecidos pelo MVVM Toolkit. Ao fazer a classe partial
e adicionar a [ObservableProperty]
à private
variável, a propriedade Name
pública é gerada com as notificações de alteração de propriedade apropriadas.
using Microsoft.Toolkit.Mvvm.ComponentModel;
public partial class EmployeeViewModel : ObservableObject
{
[ObservableProperty]
private string _name;
}
O MVVM Toolkit é distribuído por meio do CommunityToolkit.Mvvm
pacote NuGet.