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

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)

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.

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

Selecione a tabela de Produto e clique em Finalizar

Temos agora nossa Model de Produtos

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

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

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}

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 :)
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