.NET - Apresentando o padrão Model View ViewModel


O padrão Model View ViewModel (MVVM) foi introduzido por John Gosmann em 2005 e é uma especialização do padrão de projeto Presentation Model (PM) que foi introduzido em 2004 por Martin Fowler.

Um dos objetivos principais do padrão Presentation Model (PM) é separar e abstrair a View - a interface visual do usuário (UI) - da lógica de apresentação para fazer a interface do usuário testável. Além disso podemos também querer criar uma lógica de apresentação reutilizável para interfaces diferentes e diferentes tecnologias de interface do usuário, reduzir o acoplamento entre a interface do usuário e outros códigos e permitindo que os designers de interface possam trabalhar de forma mais independente.

Dessa forma o padrão MVVM é uma interpretação especializada do padrão PM concebidos para satisfazer os requisitos do Windows Presentation Foundation (WPF) e do Silverlight.

Estruturalmente, uma aplicação que usa o padrão MVVM consiste basicamente em três componentes principais: o Modelo, a Visão(View) e a ViewModel.

No início apenas o WPF atendia as exigências para implementação do padrão MVVM. No SilverLight 2 o padrão era mais difícil de implementar. Com o SilverLight 4 você pode aplicar o padrão MVVM tanto na WPF como no SilverLight da mesma forma usando os recursos do databinding.

Ao aplicar o padrão MVVM você deve ter cuidado com o ViewModel pois ele possui muitas responsabilidades e é fácil se perder.Ao aplicar o padrão MVVM você deve ter cuidado com o ViewModel pois ele possui muitas responsabilidades e é fácil se perder. No entanto se você usar uma abordagem adequada o padrão MVVM pode tornar a sua interface mais fácil de testar, te ajuda a economizar tempo e facilita a manutenção da sua aplicação.

Para usar corretamente o MVVM você tem que conhecer bem o XAML e sua estrutura, saber como o mecanismo de binding do XAML funciona, e como os objetos de comando, os comportamentos de comando (ICommand) e modelos de dados (data templates) são estruturados.

SilverLight ou WPF ?

Você já deve ter percebido as similaridades entre WPF e SilverLight. Ambas as tecnologias possui o mesmo núcleo : O Microsoft NET Framework. Em ambas usamos a linguagem XAML para construir a interface com o usuário.

O WPF é o sucessor do Windows Forms e foi projetado para incorporar um conjunto completo de controles de interface e elementos de mídia com os quais podemos criar aplicações clientes ricas e interativas. O SilverLight é um cross-browser, e uma tecnologia multiplatforma que suporta aplicações ricas para a web. Navegadores podem hospedar aplicações WPF e o SilverLight pode ser executado fora do navegador em desktops mas em geral WPF destina-se a aplicações Windows e SilverLight a aplicações web.

A compatibilidade entre o Silverlight e WPF existe porque ambos utilizam a mesma linguagem de descrição de interface do usuário: a linguagem XAML,a mesma pilha de componentes de interface (embora o SilverLight use apenas um subconjunto da pilha), a mesma library de classe e CLR. (A única grande diferença é que o Silverlight atualmente usa uma implementação diferente do. NET CLR.)

Na figura abaixo temos uma visão da arquitetura do WPF e do SilverLight e suas diferenças:

Como o SilverLight é focado para multiplataforma e audiência da web em múltiplos navegadores a Microsoft teve que manter o seu runtime pequeno e leve e por isso antes de começar a usar uma das duas tecnologias você tem que definir bem qual o seu objetivo pois você não vai encontrar todos os recursos WPF disponíveis no SilverLight e , dependendo do seu projeto, pode ser muito mais difícil realizar uma migração.

Obs: O Silverlight 4 vem com um rico conjunto de controles, estilos e modelos, um dos quais é o modelo de web site Model View Contoller (MVC) para aplicações de negócio. Já o WPF vem com uma caixa de ferramentas de controles menor e mais leve.

Mas afinal , o que você deve usar o Silverlight ou o WPF ?

A resposta é: "Faça a sua escolha com base no tipo de aplicação que você está construindo e os objetivos mais comuns da sua aplicação."

Camadas Lógicas (Layers) , Camadas Físicas (Tiers) e Serviços

Ao utilizar um padrão como o MVVM você fica obrigado a dividir o código da aplicação em diferentes blocos. Isso é bom, pois te ajuda a manter a aplicação mais flexível e mais fácil de testar. De forma geral tais blocos são chamados camadas. (Á vezes, um conjunto de camadas possuem uma interação específica e compões uma camada física como a camada do cliente em uma aplicação MVVM)

Na figura abaixo vemos um esquema mostrando a diferença conceitual entre layers (camada lógica) e tiers (camada física)

Em uma aplicação comum de três camadas, como uma aplicação MVVM (interface, lógica de negócios e banco de dados), você geralmente possui duas ou três camadas lógicas por camadas físicas. As camadas físicas são a aplicação cliente e o banco de dados remoto (fisicamente separados), e as camadas lógicas para a camada do cliente podem ser a interface do usuário e da apresentação, enquanto que para a camada física de negócios, podem ser a camada de negócio e a camada de acesso a dados.

O padrão PM e o MVVM

O padrão PM surgiu quando tecnologias como o WPF e o Silverlight ainda não estavam disponíveis. Quando surgiu, a Microsoft aplicou o padrão PM para WPF e Silverlight usando o padrão MVVM.

A orientação do padrão PM é manter um relacionamento fracamente acoplado entre a camada de apresentação e a View tornando a View um observador da camada de apresentação, e utilizando o binding para conseguir realizar esse objetivo.

A camada de apresentação conhece o modelo mas ela não precisa conhecer a respectiva View. A View conhece a usa camada de apresentação única e exclusivamente através do binding. O poder da flexibilidade do databinding do WPF/SilverLight torna este padrão adequado para usar nessas tecnologias.

O padrão MVVM é uma evolução do PM que tem os três componentes :

A figura abaixo mostra a estrutura básica de uma aplicação MVVM:

Para poder implementar o padrão MVVM em suas aplicações WPF ou SilverLight a ViewModel deverá implementar interfaces requeridas pelo mecanismo de binding dessas tecnologias.

Uma dessas interfaces é a interface INotifyPropertyChanged que implementa um sistema de notificação que é ativado quando o valor de uma propriedade é alterada. Ela é requerida no ViewModel para fazer com que o mecanismo de vinculação do XAML funcione de forma correta.

Outra customização da PM é o Command exposto pela interface ICommand. Este Command específico pode ser vinculando a qualquer controle XAML e determina se o controle pode ou não pode executar uma ação específica.

Outra importante componente usado é o DataTemplate que é uma estrutura XAML que define como processar um ViewModel específico ou um estado específico do ViewModel. Os componentes DataTemplate são visões que são renderizados em tempo de execução que são particularmente tipos de View que não podem conter qualquer código pois são criados dinamicamente. Logicamente você esta exibindo um ViewModel ou Model diretamente na interface do usuário, mas a view é invocada em tempo de execução e anexada ao ViewModel ou modelo (através do datacontext)

Aplicando o padrão MVVM

Vamos agora criar uma aplicação WPF e aplicar o padrão MVVM. Vamos começar com algo bem simples e que seja fácil de entender.

Abra o Visual C# 2010 Express Edition e crie uma nova aplicação WPF :

A seguir vamos criar 3 pastas no projeto de forma a organizar melhor o nosso código.

No menu Project selecione New Folder e informe o nome Model para a pasta a ser criada. Repita o processo e crie as pasta View e ViewModel;

Na janela Solution Explorer você deverá visualizar a estrutura da solução contendo 1 projeto
e a estrutura conforme a figura ao lado.

Criando o Model, a View e o ViewModel:

1- Vamos iniciar definindo o nosso Model.

Para isso clique com o botão direito do mouse sobre a pasta Model e selecione Add -> Class;

A seguir informe o nome Clientes.cs e defina nesta classe o seguinte código:

using System.ComponentModel;

namespace Aplicando_MVVM.Model
{
    class Cliente : INotifyPropertyChanged
    {

        public int ID { get; set; }
        private string _nome;
        private string _email;

        public string Nome
        {
            get { return _nome; }
            set
            {
                if (_nome != value)
                {
                    _nome = value;
                    RaisePropertyChanged("Nome");
                }
            }
        }

        public string Email
        {
            get { return _email; }
            set
            {
                if (_email != value)
                {
                    _email = value;
                    RaisePropertyChanged("Email");
                }
            }
        }

        public event PropertyChangedEventHandler PropertyChanged;
        private void RaisePropertyChanged(string property)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(property));
            }
        }
    }
}

Este é o nosso Model e representa a entidade de negócio da nossa aplicação.

2- Agora vamos definir a nossa View.

Para isso clique com o botão direito do mouse sobre a pasta View e selecione Add -> New Item;

A seguir selecione o template User Control e informe o nome ClienteView.xaml

A seguir vamos definir no arquivo ClienteView.xaml o seguinte código XAML:

<UserControl x:Class="Aplicando_MVVM.View.ClienteView"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
             mc:Ignorable="d"
             d:DesignHeight="300" d:DesignWidth="300">
    <Grid>
        <Grid>
            <StackPanel HorizontalAlignment="Left">
                <ItemsControl ItemsSource="{Binding Path=Clientes}">
                    <ItemsControl.ItemTemplate>
                        <DataTemplate>
                            <StackPanel Orientation="Horizontal">
                                <StackPanel Orientation="Horizontal">
                                    <TextBox
                                       Text="{Binding Path=Nome, Mode=TwoWay}"
                                       Width="100"
                                       Margin="3 5 3 5"/>
                                    <TextBox

                                   Text="{Binding Path=Endereco, Mode=TwoWay}"
                                     Width="100"
                                     Margin="0 5 3 5"/>

                                </StackPanel>
                            </StackPanel>
                        </DataTemplate>
                    </ItemsControl.ItemTemplate>
                </ItemsControl>
            </StackPanel>
        </Grid>
    </Grid>
</UserControl>

A nossa View utiliza um User Control para exibir o nome e endereço dos clientes definidos no Model;

3- Definindo o ViewModel

Clique com o botão direito do mouse sobre a pasta ViewModel e selecione Add -> New Item;

A seguir selecione o template Class e informe o nome ClienteViewModel.cs;

Defina o seguinte código na classe ClienteViewModel.cs:

using System.Collections.ObjectModel;
using Aplicando_MVVM.Model;

namespace Aplicando_MVVM.ViewModel
{
    class ClienteViewModel
    {
        public ObservableCollection<Cliente> Clientes { get; set; }

        public void LoadClientes()
        {
            ObservableCollection<Cliente> clientes = new ObservableCollection<Cliente>();

            clientes.Add(new Cliente { Nome = "Macoratti", Email = "macoratti@yahoo.com" });
            clientes.Add(new Cliente { Nome = "Janice", Email = "janice@bol.com.br" });
            clientes.Add(new Cliente { Nome = "Jefferson", Email = "jeff@uol.com.br" });
            clientes.Add(new Cliente { Nome = "Jessica", Email = "jessica@net.com" });
            clientes.Add(new Cliente { Nome = "Miriam", Email = "mimi@uol.com.br" });

            Clientes = clientes;
        }
    }
}

A nossa ViewModel utiliza o Model e preenche um ObservableCollection do tipo Cliente com alguns dados que iremos exibir na View.

A classe ObservableCollection<T> fica no namespace System.Collections.ObjectModel, e fornece notificações, quando uma coleção é modificada, incluindo, alterando ou mesmo excluindo algum item.

Já temos definidos o Model, a View e o ViewModel vamos agora por o padrão para funcionar.

Na janela MainWindow.xaml vamos definir o seguinte leiaute:

Para isso selecione o arquivo MainWindow.xaml e digite o seguinte código:

<Window x:Class="Aplicando_MVVM.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:view="clr-namespace:Aplicando_MVVM.View"
        Title="Usando o Padrão MVVM" Height="350" Width="525" Loaded="Window_Loaded">
    <Grid Margin="10">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="460*"/>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition Height="40"/>
            <RowDefinition Height="80*"/>
        </Grid.RowDefinitions>
        <Border CornerRadius="5" BorderBrush="SteelBlue" BorderThickness="2" Grid.Row="0">
            <Label Height="30" Width="400" Content="Exemplo de utilização do Padrão MVVM - WPF" Background="Aqua"/>
        </Border>
        <Border CornerRadius="5" BorderBrush="SteelBlue" BorderThickness="2" Grid.Row="1" Margin="0,5,0,0" Background="Beige">
            <view:ClienteView  x:Name="ClienteView"/>
        </Border>
    </Grid>
</Window>

O código XAML declara a view que criamos e a utiliza para exibir a visualização ao usuário do nosso Model. Perceba que temos que definir o código no evento Window_Loaded da janela MainWindow.xaml.

Abra o arquivo MainWindow.xaml.cs e defina o seguinte código no mesmo:

using System.Windows;
using Aplicando_MVVM.ViewModel;

namespace Aplicando_MVVM
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }
        private void Window_Loaded(object sender, RoutedEventArgs e)
        {
       
    ClienteViewModel cliViewModel = new ClienteViewModel();
            cliViewModel.LoadClientes();
            ClienteView.DataContext = cliViewModel;

        }
    }
}

Neste código estou criando uma instância do nosso ViewModel e carregando os dados a partir da definição do Model e a seguir definindo o DataContext do View para poder exibir as informações.

Executando o projeto iremos obter a visualização das informações através da aplicação do padrão Model View ViewModel;

Quero destacar que este exemplo é o mais simples que conseguir fazer para mostrar a aplicação do padrão, lembrando que esta é uma das muitas maneiras de implementar o padrão. Se você procurar na web por exemplos talvez não encontre duas implementações idênticas.

Pegue o projeto completo aqui: Aplicando_MVVM.zip

Eu sei é apenas WPF e MVVM mas eu gosto...

"Em verdade , em verdade vos digo que vem a hora, e agora é, em que os mortos ouvirão a voz do Filho de Deus, e os que a ouvirem viverão."(João-5:25)

Referências:


José Carlos Macoratti