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

 

Silverlight 4 Dica do Dia #7 – Pivot Viewer Basics

By rdorta at julho 01, 2010 18:08
Filed Under: Silverlight

Após mais uma excelente twitada e blogada do Rene de Paula – User Experience Evangelist da Microsoft, conferi o lançamento do Pivot Viewer.

O assunto é bem extenso, vou criar uma série de posts em breve para detalhar um pouco melhor o controle. Neste post vou apenas mostar o que consegui fazer em algumas horas com alguns conhecimentos que eu já tinha.

Inicialmente alguns testes e esse exemplo básico abaixo, buscando as pessoas que você segue em seu twitter :p Os code snippets estão abaixo assim como o fonte.

De início vocês irão ver que eu estou seguindo no Twitter, mas caso queiram, é só digitar o usuário de vocês e aguardar ele gerar o Deep Zoom(pode demorar um pouco dependendo de quantas pessoas você segue)

Get Microsoft Silverlight

 

O Pivot Viewer é um controle que permite ao desenvolvedor disponibilizar GRANDES (mas grandes MESMO) quantidades de informações de uma só vez e também visualizar a conexão entre elas.

O controle Pivot exibe “coleções” de informações, utilizando DEEP ZOOM em sua máxima performance.

Mais informações sobre o Pivot Viewer você encontra em:

http://www.getpivot.com/

E para baixar as assemblys vá em http://www.getpivot.com/download/

Vamos criar um exemplo de Pivot Viewer com essas assemblys.

Outro SDK que vamos utilizar neste exemplo é o http://linqtotwitter.codeplex.com/

Este é um excelente SDK para comunicação com o Twitter. Não pretendo me alongar demais sobre o Deep Zoom Dinamico e o LINQ to Twitter neste post. Talvez em outro post caso queriam, me peçam no Twitter

Enfim, vamos começar.

Após baixar e instalar o Pivot, as assemblys vão estar disponíveis no diretório de SDKS da Microsoft, no meu caso (Windows 7 x64) estão em:

C:\Program Files (x86)\Microsoft SDKs\Silverlight\v4.0\PivotViewer\Jun10\Bin

image

Vamos criar um projeto Silverlight com um Pivot Viewer que leia uma coleção básica. Nos próximos posts vamos melhorar um pouco a aplicação colocando alguns filtros mais legais. (Deem sugestões de filtros de Twitter para nossa aplicação no Twitter)

image

Adicione as referências do Pivot Viewer no seu projeto Silverlight:

image

Vamos adicionar o controle Pivot em nosso XAML. E também um textbox para obter o id do twitter de nosso usuário e um TextBlock para informar que o serviço de DeepZoom está sendo executado.

   1: <UserControl x:Class="SLPivotViewer.MainPage"
   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:     mc:Ignorable="d"
   7:     xmlns:local="clr-namespace:System.Windows.Pivot;assembly=System.Windows.Pivot"
   8:     d:DesignHeight="300" d:DesignWidth="400">
   9:  
  10:     <Grid x:Name="LayoutRoot" Background="White" >
r: black; font-size: 8pt; overflow: visible; border-style: none; padding: 0px;">  11:  
  12:         <Grid Grid.Row="0" Grid.Column="0">
  13:             <StackPanel Orientation="Horizontal"  Height="30" VerticalAlignment="Top">
  14:                 <TextBlock Text="Digite o seu usuário do twitter:"></TextBlock>
  15:                 <TextBox x:Name="txtUser" Width="200"></TextBox>
  16:                 <Button x:Name="btnBuscar" Content="Buscar" Click="btnBuscar_Click"></Button>
  17:             </StackPanel>
  18:             <TextBlock x:Name="txbAguarde" Text="Aguarde, o DeepZoom de amigos está sendo gerado" Height="18" Visibility="Collapsed" VerticalAlignment="Top" Margin="0,34,0,0"/>
  19:         </Grid>
  20:         <Grid Grid.Column="0" Margin="0,65,0,0" d:LayoutOverrides="GridBox">
  21:            <local:PivotViewer x:Name="pvtViewer"></local:PivotViewer>
  22:         </Grid>
  23:  
  24:     </Grid>
  25: </UserControl>

 

Antes de mais nada é interessante que você leia o post que fiz ano passado sobre como criar coleções DEEP ZOOM Dinamicamente:

Deep Zoom Dinamico

Agora em nosso projeto Web, vamos adicionar um serviço Domain Service que irá gerar as imagens do DeepZoom caso as mesmas não existam, aqui vamos usar o Linq to Twitter e o Deep Zoom Dinamico

   1:  
   2: namespace SLPivotViewer.Web.Services
   3: {
   4:     using System;
   5:     using System.Collections.Generic;
   6:     using System.ComponentModel;
   7:     using System.ComponentModel.DataAnnotations;
   8:     using System.Linq;
   9:     using System.ServiceModel.DomainServices.Hosting;
  10:     using System.ServiceModel.DomainServices.Server;
  11:     using LinqToTwitter;
  12:     using System.IO;
  13:     using System.Web;
  14:     using System.Xml.Linq;
  15:     using Microsoft.DeepZoomTools;
  16:     using System.Security.Principal;
  17:     using System.Net;
  18:  
  19:     [EnableClientAccess()]
  20:     public class TwitterService : DomainService
  21:     {
  22:  
  23:         [Invoke]
  24:         public void GenerateUsersCollection(string pstrUserName)
  25:         {
  26:             //LOGANDO OS PASSOS
  27:             StreamWriter writer = new StreamWriter(HttpContext.Current.Server.MapPath("~/log.txt"));
  28:             try
  29:             {
  30:                 //GARANTINDO A EXISTENCIA DOS DIRETORIOS
  31:                 if (!File.Exists(HttpContext.Current.Server.MapPath("~/Collections/" + pstrUserName + ".cxml")))
  32:                 {
  33:                     writer.Write("INICIO - CRIAR DIRETORIOS");
  34:                     if (!Directory.Exists(HttpContext.Current.Server.MapPath("~/Collections/")))
  35:                     {
  36:                         Directory.CreateDirectory(HttpContext.Current.Server.MapPath("~/Collections/"));
  37:                     }
  38:  
  39:                     if (!Directory.Exists(HttpContext.Current.Server.MapPath("~/imgs/")))
  40:                     {
  41:                         Directory.CreateDirectory(HttpContext.Current.Server.MapPath("~/imgs/"));
  42:                     }
  43:  
  44:                     if (!Directory.Exists(HttpContext.Current.Server.MapPath("~/Collections/")))
  45:                     {
  46:                         Directory.CreateDirectory(HttpContext.Current.Server.MapPath("~/Collections/"));
  47:                     }
  48:  
  49:                     if (!Directory.Exists(HttpContext.Current.Server.MapPath("~/ClientBin/")))
  50:                     {
  51:                         Directory.CreateDirectory(HttpContext.Current.Server.MapPath("~/ClientBin/"));
  52:                     }
  53:  
  54:                     if (!Directory.Exists(HttpContext.Current.Server.MapPath("~/ClientBin/" + pstrUserName + "/")))
  55:                     {
  56:                         Directory.CreateDirectory(HttpContext.Current.Server.MapPath("~/ClientBin/" + pstrUserName + "/"));
  57:                     }
  58:  
  59:                     if (!Directory.Exists(HttpContext.Current.Server.MapPath("~/ClientBin/" + pstrUserName + "/GeneratedImages/")))
  60:                     {
  61:                         Directory.CreateDirectory(HttpContext.Current.Server.MapPath("~/ClientBin/" + pstrUserName + "/GeneratedImages/"));
  62:                     }
  63:  
  64:                     if (!Directory.Exists(HttpContext.Current.Server.MapPath("~/ClientBin/" + pstrUserName + "/GeneratedImages/output_images/")))
  65:                     {
  66:                         Directory.CreateDirectory(HttpContext.Current.Server.MapPath("~/ClientBin/" + pstrUserName + "/GeneratedImages/output_images/"));
  67:                     }
  68:  
  69:  
  70:                     writer.Write("FIM - CRIAR DIRETORIOS");
  71:  
  72:                     writer.Write("INICIO - CONEXAO TWITTER");
  73:  
  74:                     //INICIANDO A CONSULTA A API DO TWITTER
  75:                     TwitterContext twitterCtx = new TwitterContext();
  76:                     //CARREGANDO OS USUARIOS QUE ESTÃO SENDO SEGUIDOS PELO USUARIO DA APLICAÇÃO
  77:                     var users =
  78:                         from tweet in twitterCtx.User
  79:                         where tweet.Type == UserType.Friends &&
  80:                               tweet.ID == pstrUserName
  81:                         select tweet;
  82:  
  83:  
  84:                     writer.Write("FIM - CONEXAO TWITTER");
  85:                     //CRIAÇÃO DO DEEP ZOOM
  86:                     List<string> lstXMLs = new List<string>();
  87:  
  88:                     string strCaminhoImagensGeradas = HttpContext.Current.Server.MapPath("/Collections/" + pstrUserName + "/GeneratedImages/");
  89:  
  90:                     writer.Write("INICIO - CRIAR DEEP ZOOM");
  91:                     CollectionCreator cc = new CollectionCreator();
  92:  
  93:                     //CRIAÇÃO DO XML DA COLEÇÃO
  94:                     XDocument doc = new XDocument();
  95:                     XElement elCollection = new XElement("Collection");
  96:                     XElement elFacetCateogies = new XElement("FacetCategories");
  97:                     XElement facet = new XElement("FacetCategory");
  98:                     XElement elItems = new XElement("Items");
  99:                     XAttribute at = new XAttribute("Name", "Teste");
 100:                     XAttribute at2 = new XAttribute("Type", "String");
 101:                     facet.Add(at);
 102:                     facet.Add(at2);
 103:                     elFacetCateogies.Add(facet);
 104:                     elCollection.Add(elFacetCateogies);
 105:  
 106:                     //ADICIONANDO OS NÓS DAS IMAGENS
 107:                     int intContador = 0;
 108:                     foreach (User objUser in users)
 109:                     {
 110:                         XElement elItem = new XElement("Item");
 111:                         writer.Write("    INICIO - CRIAR IMAGEM");
 112:                         ImageCreator ic = new ImageCreator();
 113:                         ic.TileSize = 256;
 114:                         ic.TileFormat = ImageFormat.AutoSelect;
 115:                         ic.ImageQuality = 1;
 116:                         string target = strCaminhoImagensGeradas + "output_images\\" + intContador;
 117:  
 118:                         if (!File.Exists(HttpContext.Current.Server.MapPath("~/imgs/" + objUser.Identifier.ScreenName + ".jpg")))
 119:                         {
 120:                             WebClient client = new WebClient();
 121:                             client.DownloadFile(objUser.ProfileImageUrl, HttpContext.Current.Server.MapPath("~/imgs/" + objUser.Identifier.ScreenName + ".jpg"));
 122:                         }
 123:  
 124:                         ic.Create(HttpContext.Current.Server.MapPath("~/imgs/" + objUser.Identifier.ScreenName + ".jpg"), target);
 125:                         writer.Write("    FIM - CRIAR IMAGEM");
 126:                         lstXMLs.Add(Path.ChangeExtension(target, ".xml"));
 127:                         elItem.Add(new XAttribute("Img", "#" + intContador.ToString()));
 128:                         elItem.Add(new XAttribute("Id", intContador.ToString()));
 129:                         elItem.Add(new XAttribute("Href", "http://www.twitter.com/" + objUser.Identifier.ScreenName));
 130:                         elItem.Add(new XAttribute("Name", objUser.Name));
 131:                         elItems.Add(elItem);
 132:                         intContador++;
 133:                     }
 134:  
 135:                     cc.TileSize = 256;
 136:  
 137:                     cc.TileFormat = ImageFormat.Jpg;
 138:  
 139:                     cc.MaxLevel = 8;
 140:  
 141:                     cc.ImageQuality = 1;
 142:  
 143:                     cc.Create(lstXMLs, strCaminhoImagensGeradas + "output");
 144:                     writer.Write("FIM - CRIAR DEEPZOOM");
 145:                     elItems.Add(new XAttribute("ImgBase", pstrUserName + @"\GeneratedImages\output.xml"));
 146:                     elCollection.Add(elItems);
 147:                     doc.Add(elCollection);
 148:                     string strFinal = doc.ToString();
 149:                     strFinal = strFinal.Replace("<Collection>", "<Collection xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" SchemaVersion=\"1\" xmlns:d1p1=\"http://schemas.microsoft.com/livelabs/pivot/collection/2009\" xmlns=\"http://schemas.microsoft.com/collection/metadata/2009\">");
 150:                     StreamWriter final = new StreamWriter(HttpContext.Current.Server.MapPath("~/Collections/" + pstrUserName + ".cxml"));
 151:                     final.Write(strFinal);
 152:                     final.Close();
 153:                 }
 154:             }
 155:             catch (Exception ex)
 156:             {
 157:                 writer.Write(ex.Message);
 158:             }
 159:             finally
 160:             {
 161:                 writer.Close();
 162:             }
 163:         }
 164:  
 165:  
 166:     }
 167: }
 168:  
 169:  

 

 

O código está bem comentado, mas basicamente o que fiz foi utilizar o meu artigo de Deep Zoom Dinamico e os exemplos básicos do LINQ To Twitter.

 

Por fim, basta apenas chamar o serviço e passar a coleção para o Pivot Viewer

   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 SLPivotViewer.Web.Services;
  13: using System.ServiceModel.DomainServices.Client;
  14: using System.Windows.Pivot;
  15:  
  16: namespace SLPivotViewer
  17: {
  18:     public partial class MainPage : UserControl
  19:     {
  20:         public MainPage()
  21:         {
  22:             InitializeComponent();
  23:             //Carregando a coleção do Dorta
  24:             this.pvtViewer.LoadCollection("http://localhost:19227//Collections/dortaway.cxml", string.Empty);
  25:         }
  26:  
  27:         InvokeOperation opeListagem = null;
  28:         void btnBuscar_Click(object sender, RoutedEventArgs e)
  29:         {
  30:             //Instanciando o serviço que irá gerar o coleção
  31:             TwitterContext ctx = new TwitterContext();
  32:             //executando o método
  33:             opeListagem = ctx.GenerateUsersCollection(this.txtUser.Text);
  34:             //atribuindo o método que será disparado quando o serviço completar
  35:             opeListagem.Completed += opeListagem_Completed;
  36:             //avisando o usuário que o serviço está em execução
  37:             txbAguarde.Visibility = System.Windows.Visibility.Visible;
  38:         }
  39:  
  40:         void pvtViewer_CollectionLoadingFailed(object sender, CollectionErrorEventArgs e)
  41:         {
  42:             //em caso de erro vamos avisar o usuário
  43:             MessageBox.Show(e.Exception.Message);
  44:         }
  45:  
  46:         void opeListagem_Completed(object sender, EventArgs e)
  47:         {
  48:             if (opeListagem.HasError)
  49:             {
  50:                 //em caso de erro vamos avisar o usuário
  51:                 MessageBox.Show(opeListagem.Error.Message);
  52:                 opeListagem.MarkErrorAsHandled();
  53:             }
  54:             else
  55:             {
  56:                 //vamos carregar a coleção
  57:                 txbAguarde.Visibility = System.Windows.Visibility.Collapsed;
  58:                 this.pvtViewer.LoadCollection("http://localhost:19227/Collections/" + this.txtUser.Text + ".cxml", string.Empty);
  59:                 this.pvtViewer.CollectionLoadingFailed += new EventHandler<CollectionErrorEventArgs>(pvtViewer_CollectionLoadingFailed);
  60:             }
  61:             
  62:         }
  63:     }
  64: }

 

Abaixo segue o fonte que usei. Bom divertimento :)

SLPivotViewer.zip (13,83 mb)

[]´s

Silverlight 4 Dica do Dia #6 – Mapas do BING Maps no Silverlight

By rdorta at junho 25, 2010 15:46
Filed Under: Arquitetura, Silverlight

Olá,

Estive trabalhando em uma prova de conceito para um cliente da Makesys, que envolve a utilização de Bing Maps, e resolvi compartilhar com vocês como fazer para utilizar o controle de Mapas do Bing Maps.

Vamos criar uma aplicação que contenha o controle de mapas e que adicione Pins a medida que o usuário clica no mapa.

Para começar, acesse o site do Bing Maps destinado a Devs e de uma olhada em todas as opções, em nosso exemplo vamos utilizar o controle Silverlight e o SOAP Services:

http://www.microsoft.com/maps/developers/

Depois acesse o site portal do Bing Maps, para criar o seu token de desenvolvimento, você irá precisar do seu Windows Live Id:

https://www.bingmapsportal.com/

Baixe as assemblys do controle Silverlight aqui:

http://www.microsoft.com/downloads/details.aspx?displaylang=en&FamilyID=beb29d27-6f0c-494f-b028-1e0e3187e830

Crie um projeto do tipo Silverlight Application, Conforme mostra a figura abaixo:

image

Gere a solução e adicione as referências para as dlls do Bing Maps que no meu caso (Windows 7 64 bits) se encontram em:

C:\Program Files (x86)\Bing Maps Silverlight Control\V1\Libraries

image

Agora clique no botão Salvar Tudo. E vamos para o Expression Blend 4. Abra sua solução.

Abra a MainPage.xaml e clique no botão de procura de controles, navegue até a dll Microsoft.Maps.MapControl.dll e você verá os controles abaixo:

image

Vamos utilizar o controle map. Selecione o mesmo e adicione em sua MainPage

image

 

 

 

De um nome ao controle e adicione um handler ao evento MouseClick:

image

(IMPORTANTE!!!!)Agora, adicione a chave de registro que você obteve no primeiro passo, ficará algo assim:

   1: <Microsoft_Maps_MapControl:Map CredentialsProvider="SEUTOKENDEAPLICACAOAQUI" x:Name="mapMapa" d:LayoutOverrides="Width, Height" MouseClick="mapMapa_MouseClick"/>

De volta ao Visual Studio, eis o código final comentado:

   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 Microsoft.Maps.MapControl;
  13:  
  14: namespace BlogBingMaps
  15: {
  16:     public partial class MainPage : UserControl
  17:     {
  18:         public MainPage()
  19:         {
  20:             InitializeComponent();
  21:         }
  22:  
  23:         private void mapMapa_MouseClick(object sender, Microsoft.Maps.MapControl.MapMouseEventArgs e)
  24:         {
  25:             //Criando o pin
  26:             Pushpin pspPin = new Pushpin();
  27:             //Obtendo a localização com latitude e longitude do ponto em que o usuário clicou
  28:             pspPin.Location = mapMapa.ViewportPointToLocation(e.ViewportPoint);
  29:             //Adicionando o pin ao mapa
  30:             mapMapa.Children.Add(pspPin);
  31:         }
  32:     }
  33: }

Perfeito!!!

Já temos uma aplicação em Silverlight com integração ao Bing Maps.

Para deixar a coisa mais interessante, vamos mostrar o endereço da Microsoft em SP como visualização inicial, utilizando o SOAP Service para obter o código geográfico(latitude e longitude) através do endereço:

Adicione um Service Reference ao seu projeto para o endereço:

http://dev.virtualearth.net/webservices/v1/geocodeservice/geocodeservice.svc?wsdl

Vamos nomear o serviço como GeocodeService:

image

 

Adicone o import para o namespace do serviço:

   1: using BlogBingMaps.GeocodeService;

Abaixo segue o código completo comentado:

   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 Microsoft.Maps.MapControl;
  13: using Microsoft.Maps.MapControl.Core;
  14: using BlogBingMaps.GeocodeService;
  15:  
  16: namespace BlogBingMaps
  17: {
  18:     public partial class MainPage : UserControl
  19:     {
  20:         public MainPage()
  21:         {
  22:             InitializeComponent();
  23:  
  24:             //cria a instancia do serviço
  25:             GeocodeServiceClient cliServico = new GeocodeServiceClient();
  26:             //dimensiona a chave de desenvolvimento
  27:             string strChave = "SUA CHAVE AQUI";
  28:             //cria o objeto de request
  29:             GeocodeRequest reqGeoRequest = new GeocodeRequest();
  30:             // cria as credenciais
  31:             reqGeoRequest.Credentials = new Credentials();
  32:             reqGeoRequest.Credentials.ApplicationId = strChave;
  33:  
  34:             //endereço que queremos buscar
  35:             string strEndereco = "Av Das Nacoes Unidas, 12901, São Paulo";
  36:             reqGeoRequest.Query = strEndereco;
  37:  
  38:             //adicina handler para o completed
  39:             cliServico.GeocodeCompleted += new EventHandler<GeocodeCompletedEventArgs>(cliServico_GeocodeCompleted);
  40:             cliServico.GeocodeAsync(reqGeoRequest);
  41:         }
  42:  
  43:         void cliServico_GeocodeCompleted(object sender, GeocodeCompletedEventArgs e)
  44:         {
  45:             GeocodeResponse resResultado = e.Result;
  46:             //define a latitude, longitude e fator de zoom da visualização
  47:             mapMapa.SetView(new Location(resResultado.Results[0].Locations[0].Latitude, resResultado.Results[0].Locations[0].Longitude), 14);
  48:         }
  49:  
  50:         private void mapMapa_MouseClick(object sender, Microsoft.Maps.MapControl.MapMouseEventArgs e)
  51:         {
  52:             //Criando o pin
  53:             Pushpin pspPin = new Pushpin();
  54:             //Obtendo a localização com latitude e longitude do ponto em que o usuário clicou
  55:             pspPin.Location = mapMapa.ViewportPointToLocation(e.ViewportPoint);
  56:             //Adicionando o pin ao mapa
  57:             mapMapa.Children.Add(pspPin);
  58:         }
  59:     }
  60: }

Por fim, no arquivo ServiceReferences.config, remova o endpoint binaryhttp:

   1: <configuration>
   2:     <system.serviceModel>
   3:         <bindings>
   4:             <basicHttpBinding>
   5:                 <binding name="BasicHttpBinding_IGeocodeService" maxBufferSize="2147483647"
   6:                     maxReceivedMessageSize="2147483647">
   7:                     <security mode="None" />
   8:                 </binding>
   9:             </basicHttpBinding>
  10:             <customBinding>
  11:                 <binding name="CustomBinding_IGeocodeService">
  12:                     <binaryMessageEncoding />
  13:                     <httpTransport maxReceivedMessageSize="2147483647" maxBufferSize="2147483647" />
  14:                 </binding>
  15:             </customBinding>
  16:         </bindings>
  17:         <client>
  18:             <endpoint address="http://dev.virtualearth.net/webservices/v1/geocodeservice/GeocodeService.svc"
  19:                 binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IGeocodeService"
  20:                 contract="GeocodeService.IGeocodeService" name="BasicHttpBinding_IGeocodeService" />
  21:         </client>
  22:     </system.serviceModel>
  23: </configuration>

 

Espero que gostem, abaixo o silvelright final e o fonte :)

 

Até a próxima

BlogBingMaps.zip (661,48 kb)

Silverlight 4 Dica do Dia #5 - Realidade Aumentada – YES WE CAN!!!

By rdorta at junho 21, 2010 16:43
Filed Under: Arquitetura, Silverlight

Após meu último post, sobre integração com WebCam recebi muitas perguntas referentes a Realidade Aumentada.

Portanto, resolvi escrever este post sobre o assunto como forma de iniciar as pessoas que querem aprender mais sobre o assunto.

Sugiro que quem ainda não leu meu último post, que comece por ele, porque neste post vou assumir que você já sabe como capturar as imagens vindas da sua WebCam.

Depois de ler o post anterior, imprima a imagem abaixo:

image

Essa imagem é a que vamos identificar dentro da nossa aplicação.

Bom, partindo do programa gerado no último post, temos uma aplicação que captura os dados da webcam e exibe na tela.

Vamos utilizar o SLARToolkit, como nosso framework de identificação de padrão de imagem e substituição.

Adicione as referências das 2 dlls do toolkit no projeto.

Agora baixe o arquivo Pattern que representa a imagem acima: http://blogs.makesys.com.br/dorta/makesys.pat. Adicione ela ao projeto, clique em cima dela, vá até a aba propriedades e troque a sua Build Action para Resource.

Adicione o pattern no projeto.

Agora, a partir do projeto que criamos no meu último post, vamos adicionar um canvas após o Rectangle que serviu como ViewPort, dentro deste canvas, vou adicionar uma imagem, mas pode ser adicionado qualquer outro objeto Silverlight, eis o XAML completo:

   1: <UserControl x:Class="SLRealidadeAumentada.MainPage"
   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:     mc:Ignorable="d"
   7:     d:DesignHeight="550" d:DesignWidth="700"
   8:     Loaded="UserControl_Loaded">
   9:  
  10:     <Grid x:Name="LayoutRoot" Background="White">
  11:         <StackPanel>
  12:             <Grid Width="640" Height="480">
  13:                 <Rectangle Name="Viewport" Stroke="Black" StrokeThickness="2" />
  14:                 <Canvas>
  15:                     <Grid Background="White" x:Name="imgMakesys" Visibility="Collapsed"  >
  16:                         <Image Source="logo_makesys.png" Height="91" Width="261"/>
  17:                     </Grid>
  18:                 </Canvas>
  19:             </Grid>
  20:             <Button Content="IniciarCaptura" HorizontalAlignment="Center" Click="Button_Click" />
  21:         </StackPanel>
  22:     </Grid>
  23: </UserControl>

Abaixo o código final:

   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 SLARToolKit;
  13: using System.Windows.Media.Media3D;
  14:  
  15: namespace SLRealidadeAumentada
  16: {
  17:     public partial class MainPage : UserControl
  18:     {
  19:  
  20:         CaptureSource cpsVideo;
  21:         CaptureSourceMarkerDetector cmkDetectorPattern;
  22:  
  23:         public MainPage()
  24:         {
  25:             InitializeComponent();
  26:         }
  27:  
  28:         private void UserControl_Loaded(object sender, RoutedEventArgs e)
  29:         {
  30:             // Instancia o objeto que manipula a WebCam
  31:             cpsVideo = new CaptureSource();
  32:             // Define qual webcam será o alvo do objeto, no caso a câmera padrão do Sistema
  33:             cpsVideo.VideoCaptureDevice = CaptureDeviceConfiguration.GetDefaultVideoCaptureDevice();
  34:  
  35:             //Cria o objeto que recebe os dados do video
  36:             var vbsVideo = new VideoBrush();
  37:             vbsVideo.SetSource(cpsVideo);
  38:             // Preenche o retangulo com os dados capturados do video
  39:             Viewport.Fill = vbsVideo;
  40:  
  41:             //  Initialize the o detector
  42:             cmkDetectorPattern = new CaptureSourceMarkerDetector();
  43:             // Carrega o pattern que deve ser procurado, ele tem 64 segmentos e 80 milimetros de 
  44:             var patPattern = Marker.LoadFromResource("makesys.pat", 64, 64, 80);
  45:             // Cria a parspectiva com planos de 1 a 4000
  46:             cmkDetectorPattern.Initialize(cpsVideo, 1, 4000, patPattern);
  47:  
  48:             // Vincula o handler de captura do pattern
  49:             // O evento é disparado se pelo menos um pattern é identificado na imagem
  50:             cmkDetectorPattern.MarkersDetected += (senderDelegate, eDelegate) =>
  51:             {
  52:                 // Cria uma nova tread para manipular a projeção do objeto que está no canvas
  53:                 Dispatcher.BeginInvoke(() =>
  54:                 {
  55:                     // Calcula a matrix de projeção
  56:                     var dreResultado = eDelegate.DetectionResults;
  57:                     if (dreResultado.HasResults)
  58:                     {
  59:                         // Centro do Objeto
  60:                         Matrix3D mtdCentro = Matrix3DFactory.CreateTranslation(-imgMakesys.ActualWidth * 0.5, -imgMakesys.ActualHeight * 0.5, 0);
  61:                         // Troca o eixo Y e diminui a escala pela metade
  62:                         Matrix3D mtdEscala = Matrix3DFactory.CreateScale(0.5, -0.5, 0.5);
  63:                         // Calcula a matriz final de transformação baseada no ponto centro original
  64:                         Matrix3D mtdFinal = mtdCentro * mtdEscala * dreResultado[0].Transformation;
  65:  
  66:                         // Calcula a projeção utilizando o SLARToolkit
  67:                         Matrix3D mtdViewPort = Matrix3DFactory.CreateViewportTransformation(Viewport.ActualWidth, Viewport.ActualHeight);
  68:                         Matrix3D mtdProjecao = Matrix3DFactory.CreateViewportProjection(mtdFinal, Matrix3D.Identity, cmkDetectorPattern.Projection, mtdViewPort);
  69:  
  70:                         // Aplicaca a transformação 3D ao objeto final
  71:                         imgMakesys.Projection = new Matrix3DProjection { ProjectionMatrix = mtdProjecao };
  72:                         imgMakesys.Visibility = Visibility.Visible;
  73:                     }
  74:                 });
  75:             };
  76:         }
  77:  
  78:         private void Button_Click(object sender, RoutedEventArgs e)
  79:         {
  80:             // Efetua a requisição de acesso a WebCam
  81:             if (CaptureDeviceConfiguration.RequestDeviceAccess())
  82:             {
  83:                 cpsVideo.Start();
  84:             }
  85:         }
  86:     }
  87: }

Deixei o código bem comentado, note que estamos utilizando o objeto Matrix3D para adicionar uma projeção ao nosso grid que contém a imagem.

A grande mágica na realidade é realizada pelo SLARToolkit que identifica a imagem e dispara o delegate que criamos na linha 50.

Notem também que estamos utilizando Multi-Threading para renderizar a projeção, isso evita que a aplicação trave com o processamento.

Abaixo uma imagem minha(galã!!!) utilizando o código acima.

image

Agora o código final para você brincarem :)

 

Get Microsoft Silverlight

 

 

Segue o fonte: SLRealidadeAumentada.zip (618,51 kb)

Até a próxima

Silverlight 4 Dica do dia #3 – Validação de campos

By rdorta at junho 15, 2010 14:13
Filed Under: Silverlight

Olá,

Um dos grandes recursos do Silverlight 4 é a atualização de campos através de atualização de bindings.

Vamos pensar em um sistema em Silverlight que contém uma classe de transporte(DTO), com duas propriedades: Nome do Servidor e IP do Servidor:

   1: public class Servidor
   2:     {
   3:  
   4:  
   5:         [Required(ErrorMessage = "Preencha o nome do servidor")]
   6:         public string Nome { get; set; }
   7:         [Required(ErrorMessage = "Preencha o ip do servidor")]
   8:         [RegularExpression(@"^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$",ErrorMessage="Formato de IP inválido.")]
   9:         public string IP { get; set; }
  10: }

Notem que estou usando nessa classe alguns atributos para as propriedades que podem ser encontradas na assembly System.ComponentModel.DataAnnotations.

  • Required: Esse atributo indica que este campo é de preenchimento obrigatório
  • RegularExpression: Esse atributo indica que o conteúdo da propriedade deve seguir uma formação indica pela expressão regular.

Além dessa classe, temos em nosso XAML 2 campos textbox:

   1: <TextBox x:Name="txtNome" TextWrapping="Wrap" Text="{Binding Nome, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnExceptions=True}"/>
   2: <TextBox x:Name="txtIp" TextWrapping="Wrap" Text="{Binding IP, Mode=TwoWay, NotifyOnValidationError=True, ValidatesOnExceptions=True}"/>

O interessante aí está no valor que estamos atribuindo a propriedade Text dos Texbox, vou explicar elemento a elemento:

  • Binding: usando esta referencia estou indicando que o texto do textbox deve ser preenchido pela propriedade nome do objeto que estiver sendo usado como DataContext do textbox. Como o textbox em si não contém nenhum DataContext, a propria engine tenta encontrar essa propriedade no DataContext do controle acima e assim sucessivamente.
  • Mode: Esta propriedade indica em qual direção as atualizações devem ser executadas, nesse caso TwoWay indica que caso eu altere o texto do textbox eu devo automaticamente alterar o texto da propriedade Nome que está no DataContext e vice-versa.
  • NotifyOnValidationError: Este cara indica se, caso ocorra um erro de validação, o usuário deve ser avisado através da aplicação do estado de erro no campo, como estamos setando esse valor para TRUE, caso o formato de ip digitado esteja incorreto o campo ficará assim:
    image
    Note que a mensagem exibida é a que setamos no atributo RegularExpression – ErrorMessage em nossa classe de transporte(DTO):
    [RegularExpression(@"^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$",ErrorMessage="Formato de IP inválido.")]
  • ValidatesOnExceptions: Esta propriedade indica que quando um usuário efetua uma alteração na propriedade Text do campo, os procedimentos de validação com a classe que está preenchendo o DataContext devem ser executados.

Agora vamos apenas popular o DataContext do nosso controle com a uma instancia da nossa classe de transporte:

   1: public CRUDServidor()
   2: {
   3:     this.DataContext = new Model.Servidor();
   4: }

Pronto!!!

Agora em toda a digitação que o usuário fizer nas caixas de texto, os bindings serão validados.

Por último basta apenas revalidar esses bindings antes de executar alguma ação, utilizando a classe Validator que também pode ser encontrada no namespace System.ComponentModel.DataAnnotations:

   1: private void btnSalvar_Click(object sender, RoutedEventArgs e)
   2: {
   3:     try
   4:     {
   5:         Validator.ValidateObject(this.DataContext, new ValidationContext(this.DataContext, null, null));
   6:         //Salvar as alterações
   7:     }
   8:     catch
   9:     {
  10:         BindingExpression bneNome = txtNome.GetBindingExpression(TextBox.TextProperty);
  11:         bneNome.UpdateSource();
  12:         BindingExpression bneIP = txtIpRoteador.GetBindingExpression(TextBox.TextProperty);
  13:         bneIP.UpdateSource();
  14:     }
  15: }

Grande novidade do PDC - Silverlight 4

By rdorta at novembro 19, 2009 13:59
Filed Under: Arquitetura, Silverlight

Olá amigos,

Mais uma vez a Microsoft se supera no PDC 2009. Quem quiser um acompanhamento técnico legal do PDC 2009, sugiro o blog do Waldemir Cambiucci (ver no meu blogroll).

Agora falando sobre o que mais me interessa, Silverlight 4 vem aí e com features que eu sempre dizia que faltavam ao Silvelright quando comparado ao Flash. Ou seja… Flash Down…

Vamos direto ao link e depois vou comentar

http://silverlight.net/getstarted/silverlight-4-beta/

Primeiro ponto legal, a utilização do Visual Studio 2010.

Vamos aos Tools:

Silverlight 4 Beta Tools for Visual Studio 2010

Segundo ponto legal:

Blend for .Net 4

http://www.microsoft.com/downloads/details.aspx?FamilyID=6806e466-dd25-482b-a9b3-3f93d2599699&displaylang=en

A Documentação:

Online Silverlight 4 Beta Documentation

E agora minha visão sobre as novas features:

  • Impressão direta do Silverlight – YES WE CAN!!! muito bom!!!
  • Inclusão de um RichTextbox para exibição de HTML – Outra feature que fazia falta… Muito bom
  • DataGrid com Sort e Resize e Copy e Paste de linhas.
  • RIA Services
  • Melhorias no sistema de Out-of Browser.
  • Integração com periféricos: leitor de códigos de barra, webcam e outros..

Vou realziar alguns testes com a ferramenta em breve e posto mais comentários´

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.