Silverlight 4 Dica do do Dia #8 Criando um CRUD com MVVM e Silverlight Parte 1 – Retrieve

By rdorta at julho 10, 2010 10:55
Filed Under: Arquitetura, Silverlight, MVVM

Olá,

Uma das coisas que sempre tenho dito em palestras e artigos é que um dos maiores potenciais do Silverlight, se não o maior, é a contrução de aplicações LOB(Line-of-business).

Aplicações deste tipo são aplicações que podem chegar a um grande nível de complexidade dependendo das regras de negócio da empresa que está precisando da aplicação.

Após grandes apelos da comunidade, o Silverlight 4 agora tem suporte a comandos. Isso facilita e muito a implementação de MVVM.

Pensando  nisso e atendendo a alguns pedidos de clientes e amigos, vou criar aqui um exemplo sobre como construir um CRUD simples com Silverlight utilizando o padrão de arquitetura MVVM.

Para iniciar, leiam o post que escrevi sobre MVVM para entender um pouco melhor este padrão.

Em nosso exemplo vamos usar a famosa base Northwind, para gerar as operações de CRUD(Create,Retrieve,Update, Delete). Vamos criar um WCF RIA SERVICE baseado em um modelo criado com Entity Framework.

Montando a Solução

Agora no Visual Studio vamos criar um novo projeto com o template Silverlight Businnes Application

image

Em nosso projeto Silverlight, vamos criar 3 novas pastas, uma para nossas ViewModel,outra para nossos Command e outra para nossas Interfaces.(eu gosto de organizar assim, mas fica a gosto de vocês)

image

Criando a Model de Dados

Agora vamos criar nosso modelo de dados e o serviço que irá recuperar estes dados.

Para manter o exemplo simples, vamos utilizar apenas a tabela Product da base Northwind.

No projeto Web, na pasta Models, clique com o botão direito depois em Add –> New File.

Na aba Data selecione ADO.Net Entity Data Model.

image

No Wizard de criação do EF, selecione Generate from Database e crie/escolha sua conexão com a base Northwind

image

Selecione a tabela de Produto e clique em Finalizar

image

Temos agora nossa Model de Produtos

image

Agora, vamos criar o serviço que expõe os dados dessa model.

NESTE PONTO VOCÊ DEVE COMPILAR SUA APLICAÇÃO PARA QUE NOSSO SERVIÇO ENXERGUE NOSSA MODEL.

Novamente no projeto Web, clique com o botão direito na pasta Services e adicione um novo Domain Service Class

image

No wizard de criação do serviço clique selecione a nossa model de produtos e habilite a edição

image

 

 

 

 

 

 

 

 

Agora temos a nossa classe de serviço:

   1:  
   2: namespace BlogSLMVVM.Web.Services
   3: {
   4:     using System;
   5:     using System.Collections.Generic;
   6:     using System.ComponentModel;
   7:     using System.ComponentModel.DataAnnotations;
   8:     using System.Data;
   9:     using System.Linq;
  10:     using System.ServiceModel.DomainServices.EntityFramework;
  11:     using System.ServiceModel.DomainServices.Hosting;
  12:     using System.ServiceModel.DomainServices.Server;
  13:     using BlogSLMVVM.Web.Models;
  14:  
  15:  
  16:     // Implements application logic using the NorthwindEntities context.
  17:     // TODO: Add your application logic to these methods or in additional methods.
  18:     // TODO: Wire up authentication (Windows/ASP.NET Forms) and uncomment the following to disable anonymous access
  19:     // Also consider adding roles to restrict access as appropriate.
  20:     // [RequiresAuthentication]
  21:     [EnableClientAccess()]
  22:     public class NorthwindService : LinqToEntitiesDomainService<NorthwindEntities>
  23:     {
  24:  
  25:         // TODO:
  26:         // Consider constraining the results of your query method.  If you need additional input you can
  27:         // add parameters to this method or create additional query methods with different names.
  28:         // To support paging you will need to add ordering to the 'Products' query.
  29:         public IQueryable<Product> GetProducts()
  30:         {
  31:             return this.ObjectContext.Products;
  32:         }
  33:  
  34:         public void InsertProduct(Product product)
  35:         {
  36:             if ((product.EntityState != EntityState.Detached))
  37:             {
  38:                 this.ObjectContext.ObjectStateManager.ChangeObjectState(product, EntityState.Added);
  39:             }
  40:             else
  41:             {
  42:                 this.ObjectContext.Products.AddObject(product);
  43:             }
  44:         }
  45:  
  46:         public void UpdateProduct(Product currentProduct)
  47:         {
  48:             this.ObjectContext.Products.AttachAsModified(currentProduct, this.ChangeSet.GetOriginal(currentProduct));
  49:         }
  50:  
  51:         public void DeleteProduct(Product product)
  52:         {
  53:             if ((product.EntityState == EntityState.Detached))
  54:             {
  55:                 this.ObjectContext.Products.Attach(product);
  56:             }
  57:             this.ObjectContext.Products.DeleteObject(product);
  58:         }
  59:     }
  60: }
  61:  
  62:  

MVVM RLZ – cRud - Retrieve

Agora vamos ao core do negócio, criar as VielModel e os Comandos.

Vamos começar pela interface.

No projeto Silverlight na pasta Views, adicone uma nova Silverlight Page chamada ProductsView.

Compile e abra o projeto no Expression Blend 4 e abra a ProductsView.xaml.

Adicione 3 botões (Novo,Editar e Excluir), 1 BusyIndicator e 1 DataGrid em sua page.

Adicione a seguinte expressão a propriedade ItemsSource da Datagrid : {Binding AllProducts}

Adicione a seguinte expressão a propriedade IsBusy do BusyIndicato : {Binding IsLoading}

image

Eis o XAML

   1: <navigation:Page 
   2:            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" 
   3:            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
   4:            xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   5:            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   6:            xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
   7:            xmlns:sdk="http://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk" xmlns:toolkit="http://schemas.microsoft.com/winfx/2006/xaml/presentation/toolkit" x:Class="BlogSLMVVM.Views.ProductsView"
   8:            mc:Ignorable="d"
   9:            d:DesignWidth="640" d:DesignHeight="480"
  10:            Title="ProductsView Page">
  11:     <Grid x:Name="LayoutRoot">
  12:  
  13:         <sdk:DataGrid x:Name="dtgDados" Margin="43,149,48,22" ItemsSource="{Binding AllProducts}"/>
  14:  
  15:         <toolkit:BusyIndicator x:Name="bsiCarregando" Content="BusyIndicator" Height="32" Margin="52,108,71,0" VerticalAlignment="Top" IsBusy="{Binding IsLoading}"/>
  16:         <Button x:Name="btnNovo" Content="Novo" HorizontalAlignment="Left" Height="27" Margin="52,64,0,0" VerticalAlignment="Top" Width="97"/>
  17:         <Button x:Name="btnEditar" Content="Editar" HorizontalAlignment="Left" Height="27" Margin="165,64,0,0" VerticalAlignment="Top" Width="97"/>
  18:         <Button x:Name="btnExcluir" Content="Excluir" Height="27" Margin="275,64,268,0" VerticalAlignment="Top"/>
  19:  
  20:     </Grid>
  21: </navigation:Page>

Vamos criar o link para nossa nova página.

Abra o arquivo MainPage.xaml, localize o StackPanel chamado LinksStackPanel e adicione um divisor e um link para nossa página:

   1: <Rectangle x:Name="Divider1_Copy" Style="{StaticResource DividerStyle}"/>
   2:             <HyperlinkButton x:Name="Link2_Copy" Style="{StaticResource LinkStyle}" 
   3:                 NavigateUri="/ProductsView" TargetName="ContentFrame" Content="Products"/>

Agora vamos criar nossa ViewModel.

No projeto Silverlight, na pasta ViewModel adicione uma nova classe com o nome ProductsViewModel

Abaixo segue o código final da classe, vou comentar os pontos principais mais abaixo.

   1: using System;
   2: using System.Net;
   3: using System.Windows;
   4: using System.Windows.Controls;
   5: using System.Windows.Documents;
   6: using System.Windows.Ink;
   7: using System.Windows.Input;
   8: using System.Windows.Media;
   9: using System.Windows.Media.Animation;
  10: using System.Windows.Shapes;
  11: using System.ComponentModel;
  12: using BlogSLMVVM.Web.Services;
  13: using BlogSLMVVM.Web.Models;
  14: using System.Linq;
  15: using System.ServiceModel.DomainServices.Client;
  16:  
  17: namespace BlogSLMVVM.ViewModel
  18: {
  19:     public class ProductsViewModel : INotifyPropertyChanged
  20:     {
  21:  
  22:         #region Propriedades Privadas
  23:         private NorthwindContext gctxNorthwind = null;
  24:         private bool gIsLoading = false;
  25:         #endregion
  26:  
  27:         #region Propriedades Públicas
  28:         /// <summary>
  29:         ///     Indica se o serviço está ocupado recuperando a listagem
  30:         /// </summary>
  31:         public bool IsLoading
  32:         {
  33:             get { return gIsLoading; }
  34:             set { gIsLoading = value; }
  35:         }
  36:  
  37:         /// <summary>
  38:         ///     Retorna a lista de todos os produtos
  39:         /// </summary>
  40:         public EntitySet<Product> AllProducts
  41:         {
  42:             get { return GetAllProducts(); }
  43:         }
  44:         #endregion
  45:  
  46:         #region Construtor
  47:         public ProductsViewModel()
  48:         {
  49:             gIsLoading = true;
  50:             this.gctxNorthwind = new NorthwindContext();
  51:             this.gctxNorthwind.Load<Product>(
  52:                                this.gctxNorthwind.GetProductsQuery(),
  53:                                delegate(LoadOperation<Product> loadOperation)
  54:                                {
  55:                                    this.IsLoading = false;
  56:                                    RaisePropertyChanged("IsLoading");
  57:                                }, null);
  58:         }
  59:         #endregion
  60:  
  61:         #region Chamadas do Serviço
  62:         /// <summary>
  63:         ///     Chamada para os produtos
  64:         /// </summary>
  65:         /// <returns></returns>
  66:         private EntitySet<Product> GetAllProducts()
  67:         {
  68:             return this.gctxNorthwind.Products;
  69:         }
  70:         #endregion
  71:  
  72:  
  73:         #region INotifyPropertyChanged Members
  74:  
  75:         public event PropertyChangedEventHandler PropertyChanged;
  76:  
  77:         private void RaisePropertyChanged(string propertyname)
  78:         {
  79:             if (PropertyChanged != null)
  80:             {
  81:                 PropertyChanged(this, new PropertyChangedEventArgs(propertyname));
  82:             }
  83:         }
  84:  
  85:         #endregion
  86:     }
  87: }

A primeira coisa é reparar que a classe está implementando a interface INotifyPropertyChanged. Estamos fazendo isso para que a view entenda que houve uma alteração e que ela deve se atualizar.

Em nosso construtor, estamos inicializando o serviço que criamos anteriormente e carregando os Produtos, note que estamos utilizando a propriedade IsLoading para manter a view informada de que o serviço está em execução.

Por fim temos a propriedade AllProducts que retorna a lista de produtos carregada pelo serviço.

Agora vamos ao código da nossa view:

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Net;
   5: using System.Windows;
   6: using System.Windows.Controls;
   7: using System.Windows.Documents;
   8: using System.Windows.Input;
   9: using System.Windows.Media;
  10: using System.Windows.Media.Animation;
  11: using System.Windows.Shapes;
  12: using System.Windows.Navigation;
  13: using BlogSLMVVM.ViewModel;
  14:  
  15: namespace BlogSLMVVM.Views
  16: {
  17:     public partial class ProductsView : Page
  18:     {
  19:  
  20:         private ProductsViewModel vmdProduto = null;
  21:  
  22:         public ProductsView()
  23:         {
  24:             InitializeComponent();
  25:             this.vmdProduto = new ProductsViewModel();
  26:             this.DataContext = this.vmdProduto;
  27:         }
  28:  
  29:         // Executes when the user navigates to this page.
  30:         protected override void OnNavigatedTo(NavigationEventArgs e)
  31:         {
  32:         }
  33:  
  34:     }
  35: }

Bem simples, apenas instanciamos a nossa ViewModel e a indicamos como contexto da View.

Pronto!!!

Já estamos com a nossa listagem :)

image 

Eis o fonte utilizado até aqui:

BlogSLMVVM - Parte 1.zip (4,77 mb)

Em breve o próximo post com introdução aos Commands, as outras letras do CRUD e o código fonte final :)

Silverlight 4 Dica do do Dia #8 Criando um CRUD com MVVM e Silverlight Parte 2 – Commands e Insert/Update/Delete

 

Toolkit para MVVM Com Silverlight.

By rdorta at outubro 05, 2009 11:47
Filed Under: Silverlight, Arquitetura

Muito bom!!!

Excelente toolkit para quem quer aprender MVVM e facilitar o desenvolvimento de aplicações WPF e Silverlight:

http://www.galasoft.ch/mvvm/getstarted/

Inclui código fonte.

[]´s

Até a próxima Arqinovação

Padrões de Arquitetura para Silverlight – MVVM (Model – View - ModelView)

By rdorta at setembro 17, 2009 12:11
Filed Under: Silverlight, Arquitetura

Neste post vou falar um pouco sobre o padrão MVVM e como utiliza-lo com Silverlight.

MVVM? É de comer?

rs. Não, não é nenhum xingamento e muito menos algo de comer.

MVVM ou Model View – ModelView é um design pattern que na minha opinião é o melhor patter para se desenhar aplicações em Silverlight.

Porque não utilizar outros Patterns mais conhecidos como MVC ou MVP?

No padrão MVP, o Presenter é responsável pela configuração e gerenciamento de estado de uma determinada view.

No padrão MVP o Controller tem um papel  parecido com o do Presenter.

Porém em Silverlight, a nossa camada de VIEW ou exibição contém um XAML que pode conter diversos Bindings, triggers, e gerenciamentos de estado, diferente das Views dos outros Patterns que são “burras” no Silverlight o XAML tem inteligência e vida própria.

Qual a solução?

O MVVM é um padrão derivado do MVP e do MVC, que foi disceminado pela comunidade de desenvolvimento WPF da Microsoft. A grande diferença é que apesar da ViewModel manipular e definir estados da View, a View pode declarativamente realizar bindings para propriedades da ModelView.

Separando melhor as camadas:

View = Composição de XAML e C# que manipula estados do XAML.
Model = Como as Models de MVC e MVP, define as chamadas de regras de negócio para exibição
ModelView = Camada que prepara os dados recebidos da Model para exibição no XAML.

Abaixo segue um gráfico para ilustrar o que disse acima:

fig01.gif

Hands On Mode ON

Para ilustrar melhor ainda o processo, vamos criar um MVVM Hello World em Silverlight:

No Visual Studio 2008 crie um novo projeto e permita que ele crie o projeto de testes:

image

Na sua Page.XAML, copie o código abaixo:

<UserControl x:Class="ExemploMVVM.Page"
    xmlns="
http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Width="400" Height="300">
    <Grid Background="White" ShowGridLines="True" x:Name="LayoutRoot">
        <Grid.ColumnDefinitions>
            <ColumnDefinition></ColumnDefinition>
            <ColumnDefinition></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <Grid.RowDefinitions>
            <RowDefinition></RowDefinition>
            <RowDefinition></RowDefinition>
        </Grid.RowDefinitions>
        <TextBlock x:Name="txb1" Text="{Binding Nome}"></TextBlock>
        <TextBlock x:Name="txb2" Text="{Binding Idade}" Grid.Row="1" Grid.Column="0"></TextBlock>
        <TextBlock x:Name="txb3" Text="{Binding Sobrenome}" Grid.Row="0" Grid.Column="1"></TextBlock>
        <TextBlock x:Name="txb4" Text="{Binding Curso}" Grid.Row="1" Grid.Column="1"></TextBlock>
    </Grid>
</UserControl>

 

Agora vamos definir uma Classe do Tipo Aluno, para ser nosso objeto de transporte.

namespace ExemploMVVM
{
    public class Aluno
    {

        public Aluno(string pstrNome, string pstrSobrenome, int pintIdade, string pstrCurso)
        {
            this.Nome = pstrNome;
            this.Sobrenome = pstrSobrenome;
            this.Idade = pintIdade;
            this.Curso = pstrCurso;
        }

        private string gstrNome;
        private string gstrSobrenome;
        private int gintIdade;
        private string gstrCurso;

        public string Nome
        {
            get { return gstrNome; }
            set { gstrNome = value; }
        }
        public string Sobrenome
        {
            get { return gstrSobrenome; }
            set { gstrSobrenome = value; }
        }
        public int Idade
        {
            get { return gintIdade; }
            set { gintIdade = value; }
        }
        public string Curso
        {
            get { return gstrCurso; }
            set { gstrCurso = value; }
        }

    }
}

Agora vamos criar nossa View Model. Reparem que ela recebe uma interface IAlunoView e um Serviço para ser Rodado. Isso expande as possibilidade de utilização, sendo que você pode definir uma interface para serviços também, e dessa forma você pode popular n views com n serviços.

namespace ExemploMVVM
{
    public class AlunoViewModel
    {
        private IAlunoView view;  
        public string Nome{ get; set;}  
        public string Sobrenome{ get; set;}  
        public int Idade { get; set; }  
        public string Curso { get; set; }  
        private IAlunoService svcAluno;

        public AlunoViewModel(IAlunoView view, IAlunoService svcAluno)  
        {  
            this.view = view;
            this.svcAluno = svcAluno;  
            Initialize();   
        }  

        private void Initialize()  
        {
            var p = svcAluno.ObterAlunoCurso("Arquitetura");
            Nome = p.Nome;
            Sobrenome = p.Sobrenome;
            Idade = p.Idade;
            Curso = p.Curso;  
        }  

    }
}

E  por fim, o C# da nossa View:

namespace ExemploMVVM
{
    public partial class Page : UserControl, IAlunoView
    {

        private readonly AlunoViewModel vmm; 

        public Page()
        {
            InitializeComponent();
            vmm = new AlunoViewModel(this, new AlunoService());
            this.DataContext = vmm;  
        }
    }
}

Resumo da ópera

Resumindo, a grande vantagem de utilizar MVVM com Silverlight é que você pode aproveitar todo o poder de utlização de Bindings do XAML e ainda assim, criar um modelo de arquitetura onde você pode reutilizar a mesma regra de negócio(ViewModel e Model) com diversos designs diferentes(Views); No nosso caso, poderiamos tanto utilizar essa visualização dos quatro quadrados quanto fazer um CARD FLOW com os nomes e fotos dos alunos, quanto também criar uma visualização de grid com os dados dos alunos expostos.

Abaixo segue o código fonte que utilizei para esse exemplo.

ExemploMVVM.zip (782,28 kb) 

Abraços a todos e espero que possam usufluir desse modelo de arquitetura muito recomendado para aplicações em Silverlight.

Até a próxima Arqinovação!!!

 

Sobre mim

Ricardo Dorta

Ricardo Dorta

Arquiteto de sistemas da Makesys, atua na área de desenvolvimento de aplicações .NET há 5 anos.
MCP, MCAD e MCSD.