Neste artigo irei demonstrar como você pode trabalhar com o pattern MVVM (Model-View-ViewModel) em suas aplicações Xamarin.Forms, da maneira que eu considero mais simples possível e sem depender de nenhum framework.
M V VM
“O padrão Model-View-ViewModel (MVVM) ajuda a separar a lógica de negócios e apresentação de um aplicativo de sua interface do usuário (UI) de forma precisa. Mantendo uma separação clara entre a lógica do aplicativo e a interface do usuário, ajudando a resolver vários problemas de desenvolvimento e tornando mais fácil de testar um aplicativo, manter e evoluir. Ele pode também melhorar muito a oportunidades de reutilização de código e permitir que os desenvolvedores e designers de interface do usuário possam mais facilmente colaborarem durante o desenvolvimento de suas respectivas partes de um aplicativo.” – Docs Microsoft
MVVMCoffee
Eu resolvi criar este pacote devido a necessidade que encontrei no meu dia-a-dia de trabalho. Lá na empresa temos o costume de não trabalhar com frameworks de MVVM, então toda vez que começo um projeto novo, necessito criar algumas classes bases para que possamos utilizar o MVVM e depois adaptarmos de acordo com a necessidade do projeto.
Pensando nisso, resolvi criar esta biblioteca para não precisar realizar este trabalho toda vez que iniciamos um novo projeto. Assim, eu ganho o tempo para tomar um café.
Gostaria de ressaltar que esta biblioteca não tem o intuito de competir ou substituir o uso dos frameworks. Caso você esteja buscando referências sobre os frameworks para MVVM, recomendo pesquisar sobre: MVVM Cross ou Prism, são dois ótimos frameworks e bem consolidados no mercado.
Vamos ao que interessa, como que você pode utilizar este plugin e também ganhar um tempo para tomar um café.
ADICIONANDO O NUGET PACKAGE
Instale o plugin em seu projeto compartilhado.
Estrutura de pastas
É de suma importância que você crie a seguintes estrutura de pastas em seu app para que o Plugin possa funcionar corretamente.
Crie três pastas, sendo elas: Models, Views e ViewModels.
- Models – Pasta para as classes que representam suas entidades.
- Views – Pasta para as suas pages.
- ViewModels – Pasta para as classes que representam suas ViewModels.
Nomenclatura de arquivos
É também de suma importância para que o plugin funcione corretamente que todos os seus arquivos que representam suas Views e ViewModels estejam corretamente nomeados.
- Views – Todo arquivo de page, precisa terminar com a palavra “Page”.
- ViewModels – Todos os arquivos que representem ViewModel precisa terminar com a palavra “ViewModel”.
Essa nomenclatura é necessária para que o plugin possa identificar qual Page pertence a sua respectiva ViewModel.
Model
Toda classe que represente sua entidade, necessita herdar de “BaseModel”, para que dessa forma você possa utilizar o método SetProperty e com isso a interface INotifyPropertyChanged.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using MVVMCoffee.Models; | |
namespace Sample.Models | |
{ | |
public class Customer : BaseModel | |
{ | |
public string Name | |
{ | |
get { return _name; } | |
set { SetProperty(ref _name, value); } | |
} | |
string _name = string.Empty; | |
public int Age | |
{ | |
get { return _age; } | |
set { SetProperty(ref _age, value); } | |
} | |
int _age; | |
} | |
} |
ViewModels
Toda classe que represente uma ViewModel, necessita herdar de “BaseViewModel”, para que dessa forma você possa utilizar as ViewModels nos exemplos de navegação a seguir.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using MVVMCoffee.ViewModels; | |
namespace Sample.ViewModels | |
{ | |
public class MainViewModel : BaseViewModel | |
{ | |
public MainViewModel() | |
{ | |
} | |
} | |
} |
Views
No Code-behind de suas páginas, defina na propriedade BindingContext qual ViewModel pertence aquela Page, como demonstrado a seguir.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.ComponentModel; | |
using Sample.ViewModels; | |
using Xamarin.Forms; | |
namespace Sample | |
{ | |
[DesignTimeVisible(false)] | |
public partial class MainPage : ContentPage | |
{ | |
private MainViewModel ViewModel => BindingContext as MainViewModel; | |
public MainPage() | |
{ | |
InitializeComponent(); | |
BindingContext = new MainViewModel(); | |
} | |
} | |
} |
Navegação
Para navegação temos 4 métodos, sendo eles:
PushAsync – Colocar uma nova página na pilha de páginas.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
await Navigation.PushAsync<CustomerViewModel>(false); |
SetRootAsync – Definir uma nova página como a página root, ou seja, a primeira da pilha de páginas.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
await Navigation.SetRootAsync<CustomerViewModel>(); |
PopAsync – Navegar para a página anterior da pilha de páginas.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
await Navigation.PopAsync(); |
PopToRootAsync – Navegar para a primeira página da pilha de páginas.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
await Navigation.PopToRootAsync(); |
Hands-on
App
Na classe App.cs comece atribuindo uma nova NavigationPage para MainPage, como demonstrado a seguir.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using Xamarin.Forms; | |
namespace Sample | |
{ | |
public partial class App : Application | |
{ | |
public App() | |
{ | |
InitializeComponent(); | |
MainPage = new NavigationPage(new MainPage()); | |
} | |
} | |
} |
XAML
Edite sua MainPage adicionando dois Buttons, como demonstrado a seguir.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="utf-8" ?> | |
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" | |
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" | |
xmlns:d="http://xamarin.com/schemas/2014/forms/design" | |
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" | |
mc:Ignorable="d" | |
x:Class="Sample.MainPage" | |
Title="{Binding TitlePage}"> | |
<StackLayout Margin="20" VerticalOptions="Center"> | |
<Label Text="Examples of using MVVMCoffee plugin." | |
HorizontalTextAlignment="Center"/> | |
<Button Text="PushAsync to Form" BackgroundColor="#F3F3F3" | |
Command="{Binding PushAsyncToFormCommand}"/> | |
<Button Text="SetRootAsync" BackgroundColor="#F3F3F3" | |
Command="{Binding SetRootFormCommand}"/> | |
</StackLayout> | |
</ContentPage> |
Crie a FormPage, contendo um Entry e uma Label para testar o uso da interface INotifyPropertyChanged e dois Buttons para utilizar os métodos PopAsync e PopToRootAsync, como demonstrado a seguir.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" | |
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" | |
x:Class="Sample.Views.FormPage" | |
Title="{Binding TitlePage}"> | |
<ContentPage.Content> | |
<StackLayout Margin="20"> | |
<StackLayout> | |
<Label Text="Example of Customer object for demonstrate using interface INotifyPropertyChanged" | |
HorizontalTextAlignment="Center"/> | |
<Entry Text="{Binding Customer.Name}"/> | |
<Label Text="{Binding Customer.Name}"/> | |
</StackLayout> | |
<StackLayout Padding="0,50"> | |
<Button Text="PopAsync" BackgroundColor="#F3F3F3" | |
Command="{Binding PopAsyncCommand}"/> | |
<Button Text="PopToRootAsync" BackgroundColor="#F3F3F3" | |
Command="{Binding PopToRootAsyncCommand}"/> | |
</StackLayout> | |
</StackLayout> | |
</ContentPage.Content> | |
</ContentPage> |
Observação: Lembre-se de adicionar no Code-behind de suas pages a sua respectiva ViewModel.
ViewModel
Na MainViewModel crie os commands para navegação e seus respectivos métodos.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using MVVMCoffee.ViewModels; | |
using Xamarin.Forms; | |
namespace Sample.ViewModels | |
{ | |
public class MainViewModel : BaseViewModel | |
{ | |
public Command PushAsyncToFormCommand { get; } | |
public Command SetRootFormCommand { get; } | |
public MainViewModel() | |
{ | |
TitlePage = "MainPage"; | |
PushAsyncToFormCommand = new Command(ExecutePushAsyncToFormCommand); | |
SetRootFormCommand = new Command(ExecuteSetRootFormCommand); | |
} | |
private async void ExecutePushAsyncToFormCommand() | |
{ | |
await Navigation.PushAsync<FormViewModel>(false); | |
} | |
private async void ExecuteSetRootFormCommand() | |
{ | |
await Navigation.SetRootAsync<FormViewModel>(); | |
} | |
} | |
} |
Crie a classe FormViewModel, com os Commads de navegação e seus respectivos métodos, também crie um objeto do tipo Customer.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
using MVVMCoffee.ViewModels; | |
using Sample.Models; | |
using Xamarin.Forms; | |
namespace Sample.ViewModels | |
{ | |
public class FormViewModel : BaseViewModel | |
{ | |
public Command PopAsyncCommand { get; } | |
public Command PopToRootAsyncCommand { get; } | |
private Customer _customer; | |
public Customer Customer | |
{ | |
get { return _customer; } | |
set { SetProperty(ref _customer, value); } | |
} | |
public FormViewModel() | |
{ | |
TitlePage = "FormPage"; | |
Customer = new Customer(); | |
PopAsyncCommand = new Command(ExecutePopAsyncCommand); | |
PopToRootAsyncCommand = new Command(ExecutePopToRootAsyncCommand); | |
} | |
private async void ExecutePopAsyncCommand() | |
{ | |
await Navigation.PopAsync(); | |
} | |
private async void ExecutePopToRootAsyncCommand() | |
{ | |
await Navigation.PopToRootAsync(); | |
} | |
} | |
} |
No final teremos a seguinte estrutura de projeto
Resultado
Esse e todos os exemplos deste blog encontram-se disponíveis no GitHub.
Boa, irei testar , Parabens!!!
CurtirCurtir
Obrigado !! É apenas uma primeira versão, aceito pull request kkk
CurtirCurtir