BrSilverlight

Tutoriais e dicas sobre Silverlight

Sobre o autor

Sou Breno Ferreira, desenvolvedor em um Centro de Tecnologia em Petrópolis especializado em Silverlight. Criei este site com o objetivo de postar dicas, tutoriais e artigos sobre Silverlight, WPF e tecnologias Microsoft.

 

Tratando exceções de serviços WCF no Silverlight

Quem já trabalhou com acesso a serviços criados com WCF (Windows Communication Foundation) no Silverlight com certeza já deve ter percebido que quando alguma exceção é lançada no servidor, no lado do cliente (a aplicação Silverlight nesse caso), recebia a seguinte mensagem:

error

Isso sempre gerava uma enorme dor de cabeça para os desenvolvedores, pois não era trivial saber o que houve de errado.

Mas a partir da versão 3 do Silverlight esse problema pode ser evitado, bastando-se usar o Client HTTP Stack ao invés do Browser HTTP Stack. O Client HTTP Stack usa os serviços HTTP nativos do Sistema Operacional, ao invés da pilha HTTP do browser. Isso irá permitir acessar as informações sobre as SOAP Faults geradas no servidor.

Para utilizar o Client HTTP Stack no Silverlight, basta voce chamar o método RegisterPrefix() da classe WebRequest conforme o código abaixo:

Code Snippet
  1. public MainPage()
  2. {
  3.     Boolean registerResult = WebRequest.RegisterPrefix("http://", WebRequestCreator.ClientHttp);
  4. }

 

Snippet 1: utilizando o Client Http Stack no Silverlight

No código acima, no construtor da nossa MainPage, passamos a utilizar a pilha de rede do SO para requisições HTTP feitas a qualquer serviço cuja URL comece com “http://”.

Agora, na definição dos contratos do nosso serviço, podemos utilizar Fault Contracts e definir os tipos dos erros que serão enviados para o cliente.

Para isso, basta definirmos uma classe que irá guardar as informações dos erros:

Code Snippet
  1. public class MyServiceFault
  2. {
  3.     public String ErrorMessage { get; set; }
  4. }

Snippet 2: Definição do nosso Fault Contract

Em seguida, quando declaramos um método no nosso ServiceContract, basta utilizarmos o atributo FaultContract, passando a classe que irá ser passada ao cliente com as informações dos erros:

Code Snippet
  1. [OperationContract]
  2. [FaultContract(typeof(MyServiceFault))]
  3. public User[] GetUsers()
  4. {
  5.     //...
  6. }

Snippet 3: Definição do OperationContract

Agora na aplicação Silverlight conseguimos acessar as informações de possíveis erros que venham a ocorrer no servidor. Basta checarmos o valor da propriedade Error da chamada assíncrona do método do serviço:

Code Snippet
  1. void OnGetUsersCompleted(object sender, GetUsersCompletedEventArgs e)
  2. {
  3.     if (e.Error == null)
  4.     {
  5.         // Success
  6.     }
  7.     else if (e.Error is FaultException<MyServiceFault>)
  8.     {
  9.         FaultException<MyServiceFault> fault = e.Error as FaultException<MyServiceFault>;
  10.  
  11.         var errorMessage = fault.ErrorMessage;
  12.  
  13.         MessageBox.Show(errorMessage);
  14.  
  15.     }
  16. }

Snippet 4: Trantando Faults no Silverilght

Com isso conseguimos ter um acesso melhor as Exceptions que são lançadas no servidor e depurá-las em runtime de maneira mais fácil, sem receber uma mensagem de “Not Found” que não diz absolutamente nada.


Categories: Silverlight
Permalink | Comentários (2) | Post RSSRSS comment feed

Acessando Azure Tables no Silverlight

Introdução

image_thumb

No ano passado, durante o PDC09, Ray Ozzie, Chief Software Architect da Microsoft, apresentou ao público a estratégia da Microsoft em relação a construção de Software de hoje em diante. Essa estratégia consiste do “lema” “3 Screens and the Cloud”, ou seja, aplicações que são acessíveis e integram-se através de tres dispositivos (TV, PC e Celular). E essas aplicações terão interfaces ricas, e com a centralização dos dados na nuvem, assim provendo uma grande User-Experience.

E para nós desenvolvedores que temos que começar a pensar em como construir essas aplicações, devemos começar a aprender como integrar nossas Rich Internet Applications com serviços hospedados no Azure Services Plataform, e assim centralizar nossos dados e torná-los acessíveis de qualquer lugar, através da internet.

E é sobre essa integração entre o Azure e Silverlight que este post irá tratar. Irei explicar como utilizar o Table Storage do Azure e disponibiliar esses dados através de serviços RESTful utilizando WCF Data Services e acessá-los em uma aplicação Silverlight.

Pré-Requisitos

O código-fonte deste tutorial pode ser baixado aqui:

http://cid-1498c467c14dc20b.skydrive.live.com/self.aspx/BrSilverlight/Tutoriais/CloudGameStorage.Silverlight.zip

Definindo os serviços e acessando o Table Storage

Para começar crie um novo projeto Silverlight no Visual Studio. Agora, crie um projeto do tipo “WCF Service Application”.

image Voce pode deletar o serviço criado automaticamente pelo template (IService1.cs e IService1.svc). Agora, voce irá adicionar duas classes a esse projeto, uma chamada Game e outra chamada GameDataContext.

Game.cs
  1. public class Game : TableServiceEntity
  2. {
  3.     public String Name { get; set; }
  4.     public String Website { get; set; }
  5.     public Int32 NumberOfPlayers { get; set; }
  6.     public Double Rating { get; set; }
  7.     public String Category { get; set; }
  8. }

Snippet 1

Essa classe define o tipo de dados que será salvo no Table Storage do Azure. Note que a classe herda de TableServiceEntity. Assim, o runtime do Azure saberá qual o nome da Table a ser criada e quais os tipos dos dados a ser guardados nessa Table.

Quando herdamos da classe TableServiceEntity, herdamos tres propriedade muito importantes:

PartitionKey: utilizado pelo Load Balancer do Azure para particionar e segmentar as tables no Storage. Assim, cada item das Tables com a mesma PartitionKey será salva no mesmo local e de maneira sequencial, garantindo assim um acesso mais eficiente aos dados.

RowKey: a RowKey é utilizada para identificar uma entidade específica no Table Storage.

Timestamp. essa propriedade é gerenciada internamente pelo Azure e não deve ser modificada. Ela é um valor DateTime que guarda a última modificação a ser feita na entidade.

Obs: o desenvolvedor deverá setar os valores das propriedade PartitionKey e RowKey, e esses valores deverão ser uma String de até no máximo 1K de tamanho.

Note que apesar do nome ser Tables, e cada Table possuir linhas (cada objeto do tipo Game salvo seria uma linha na Table) e colunas (cada propriedade da nossa classe), isso não possui nenhuma ligação com tabelas de banco de dados. Não é possível fazer Joins entre Tables nem ter Primary Keys ou Foreign Keys. Se voce deseja um banco de dados na nuvem, utilize o SQL Azure.

Na classe GameDataContext, adicione o código abaixo:

GameDataContext.cs
  1. public class GameDataContext : TableServiceContext, IUpdatable
  2. {
  3.     private static CloudStorageAccount account = CloudStorageAccount.DevelopmentStorageAccount;
  4.  
  5.     public GameDataContext(String baseAddress, StorageCredentials credentials)
  6.         : base(baseAddress, credentials)
  7.     {
  8.         CloudTableClient.CreateTablesFromModel(typeof(GameDataContext),
  9.                                     baseAddress,
  10.                                     credentials);
  11.     }
  12.  
  13.     public IQueryable<Game> Games
  14.     {
  15.         get { return this.CreateQuery<Game>("Games"); }
  16.     }
  17.  
  18.     #region IUpdatable Members
  19.     //thanks to Aleksey Savateyev
  20.     //from his blob post at http://blogs.msdn.com/ales/archive/2009/12/17/update-porting-silverlight-ria-to-windows-azure-data-access.aspx
  21.     
  22.     public void AddReferenceToCollection(object targetResource, string propertyName, object resourceToBeAdded)
  23.     {
  24.         AddLink(targetResource, propertyName, resourceToBeAdded);
  25.     }
  26.  
  27.     public void ClearChanges()
  28.     {
  29.         // clear out links
  30.         foreach (LinkDescriptor link in Links)
  31.             DetachLink(link.Source, link.SourceProperty, link.Target);
  32.  
  33.         // clear out entities
  34.         foreach (EntityDescriptor entity in Entities)
  35.             Detach(entity.Entity);
  36.     }
  37.  
  38.     public object CreateResource(string containerName, string fullTypeName)
  39.     {
  40.         object obj = Activator.CreateInstance(GetType().Assembly.GetType(fullTypeName));
  41.  
  42.         base.AddObject(containerName, obj);
  43.  
  44.         return obj;
  45.     }
  46.  
  47.     public void DeleteResource(object targetResource)
  48.     {
  49.         base.DeleteObject(targetResource);
  50.     }
  51.  
  52.     public object GetResource(IQueryable query, string fullTypeName)
  53.     {
  54.         object resource = null;
  55.  
  56.         // fullTypeName can be null for deletes
  57.         if (fullTypeName == null)
  58.         {
  59.               resource = query.Cast<Game>().SingleOrDefault();
  60.         }
  61.         else
  62.         {
  63.               // Check for types that will be updated or deleted
  64.               if (fullTypeName == typeof(Game).FullName)
  65.               {
  66.                     resource = query.Cast<Game>().SingleOrDefault();
  67.               }
  68.  
  69.               UpdateObject(resource);
  70.         }
  71.  
  72.         return resource;
  73.     }
  74.  
  75.     public object GetValue(object targetResource, string propertyName)
  76.     {
  77.         Type type = targetResource.GetType();
  78.  
  79.         PropertyInfo propInfo = type.GetProperty(propertyName);
  80.  
  81.         if (propInfo == null)
  82.         {
  83.             throw new Exception(string.Format("Can't find given property '{0}'.", propertyName ?? "NULL"));
  84.         }
  85.  
  86.         object val = propInfo.GetValue(targetResource, null);
  87.  
  88.         return val;
  89.     }
  90.  
  91.     public void RemoveReferenceFromCollection(object targetResource, string propertyName, object resourceToBeRemoved)
  92.     {
  93.         DeleteLink(targetResource, propertyName, resourceToBeRemoved);
  94.     }
  95.  
  96.     public object ResetResource(object resource)
  97.     {
  98.         Detach(resource);
  99.  
  100.         return resource;
  101.     }
  102.  
  103.     public object ResolveResource(object resource)
  104.     {
  105.         // Already gave object in CreateResource
  106.         return resource;
  107.     }
  108.  
  109.     public new void SaveChanges()
  110.     {
  111.         try
  112.         {
  113.             base.SaveChanges();
  114.         }
  115.         catch (KeyNotFoundException e)
  116.         {
  117.             var exception = e;
  118.         }
  119.     }
  120.  
  121.     public void SetReference(object targetResource, string propertyName, object propertyValue)
  122.     {
  123.         SetLink(targetResource, propertyName, propertyValue);
  124.     }
  125.  
  126.     public void SetValue(object targetResource, string propertyName, object propertyValue)
  127.     {
  128.         PropertyInfo propInfo = targetResource.GetType().GetProperty(propertyName);
  129.  
  130.         if (propInfo == null)
  131.         {
  132.             throw new Exception(string.Format("Can't find property '{0}'", propertyName ?? String.Empty));
  133.         }
  134.  
  135.         propInfo.SetValue(targetResource, propertyValue, null);
  136.     }
  137.     #endregion
  138. }
Snippet 2

Esta classe será responsável por fazer toda a comunicação com os serviços REST do Azure Table Storage. Por isso, herdamos nossa classe de TableServiceContext, que por sua vez, herda de DataServiceContext. O que isso quer dizer é que estamos utilizando o WCF Data Services para fazer as requisições HTTP com os serviços do Azure Table Storage.

Mas somente herdar de TableServiceContext não resolve todos os problemas. Precisamos implementar a Interface IUpdatable. Assim poderemos executar operações além de somente leitura, como inserts, deletes e updates nos nossos dados.

Agora precisamos colocar os dados de forma acessível aos clientes. Para isso, iremos criar um novo WCF Data Service.

image

Adicione o seguinte código ao serviço:

GameDataService.svc.cs
  1. public class GameDataService : DataService< GameDataContext >
  2. {
  3.     // This method is called only once to initialize service-wide policies.
  4.     public static void InitializeService(DataServiceConfiguration config)
  5.     {            
  6.         config.SetEntitySetAccessRule("*", EntitySetRights.All);
  7.         config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
  8.     }
  9.  
  10.     protected override GameDataContext CreateDataSource()
  11.     {
  12.         var account = CloudStorageAccount.DevelopmentStorageAccount;
  13.  
  14.         var address = account.TableEndpoint.AbsoluteUri;
  15.         var credentials = account.Credentials;
  16.  
  17.         GameDataContext context = new GameDataContext(address, credentials);
  18.         return context;
  19.     }
  20. }

Snippet 3

Note que aqui nós criamos um serviço que irá expor os dados obtidos com o nosso DataContext criado anteriormente que acessa o TableStorage do Azure.

Importante: o método CreateDataSource é chamado quando o serviço é iniciado. Nesse método passamos as credenciais necessárias para acessar-se o TableStorage. No nosso caso, estamos utilizando as credenciais padrões usadas no ambiente de desenvolvimento provido pelo SDK do Azure. Em ambiente de produção, será necessário passar as suas credenciais para acessar os serviços do Azure.

Acessando os dados no Silverlight

Agora iremos criar nossa camada de apresentação em Silverlight. Para isso crie um novo projeto Silverlight no Visual Studio.

Agora, precisamos adicionar a referência ao nosso serviço ao projeto Silverlight. Para isso, clique com o botão direito no projeto e vá em “Add Service Reference…”.

image 

Na janela que abrir, clique em “Discovery” e o Visual Studio irá achar nosso serviço. Dê um nome a referencia do serviço e clique em OK.

image

Nosso xaml será composto do controle DataForm (caso voce não conheça ou não sabe como usá-lo, veja meu tutorial explicando como ele funciona, que irá mostrar e coletar os dados do usuário, e um botão que irá salvar os dados no Table Storage. Para isso, adicione o seguinte código ao MainPage.xaml:

MainPage.xaml
  1. <UserControl x:Class="CloudGameStorage.Silverlight.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.     xmlns:df="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm.Toolkit"
  7.     xmlns:in="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Input.Toolkit"
  8.     mc:Ignorable="d"
  9.     Loaded="UserControl_Loaded"
  10.     d:DesignHeight="400" d:DesignWidth="572">
  11.  
  12.     <Grid x:Name="LayoutRoot" Background="White">
  13.         <df:DataForm x:Name="gameForm" AutoEdit="False" AutoGenerateFields="False" EditEnding="gameForm_EditEnding" DeletingItem="gameForm_DeletingItem">
  14.             <df:DataForm.EditTemplate>
  15.                 <DataTemplate>
  16.                     <StackPanel df:DataField.IsFieldGroup="True">
  17.                         <df:DataField Label="Name:" Description="Name of the game">
  18.                             <TextBox Text="{Binding Path=Name, Mode=TwoWay}" />
  19.                         </df:DataField>
  20.                         <df:DataField Label="Website:" Description="Website of the game">
  21.                             <TextBox Text="{Binding Path=Website, Mode=TwoWay}" />
  22.                         </df:DataField>
  23.                         <df:DataField Label="Number of Players" Description="Number of players supported by the game" >
  24.                             <in:NumericUpDown Minimum="1" Value="{Binding Path=NumberOfPlayers, Mode=TwoWay}" Width="70" HorizontalAlignment="Left" />
  25.                         </df:DataField>
  26.                         <df:DataField Label="Rating" Description="How good is this game?">
  27.                             <in:Rating ItemCount="5" Value="{Binding Path=Rating, Mode=TwoWay}" />
  28.                         </df:DataField>
  29.                         <df:DataField Label="Category" Description="Game category (Shooter, Sports, Action, ...)">
  30.                             <TextBox Text="{Binding Path=Category, Mode=TwoWay}" />
  31.                         </df:DataField>
  32.                         <!--<df:DataField Label="Image" Description="Game box cover">
  33.                             <Image Cursor="Hand" mouselef />
  34.                         </df:DataField>-->
  35.                     </StackPanel>
  36.                 </DataTemplate>
  37.             </df:DataForm.EditTemplate>
  38.         </df:DataForm>
  39.         <Button Width="50" Height="20" HorizontalAlignment="Left" VerticalAlignment="Top" Content="Save" Click="Button_Click" />
  40.     </Grid>
  41. </UserControl>

Snippet 4

Aqui criamos um DataForm fazendo DataBinding com as propriedades do nosso modelo de dados. Note que tratamos os eventos EditEnding e DeletingItem. Esses  eventos irão adicionar novos items e atualizar seus dados e deletá-los da fonte de dados. O botão será responsável por sincronizar as mudanças com o serviço que acessa o TableStorage.

Agora adicione o seguinte código ao code-behind da MainPage:

MainPage.xaml.cs
  1. public partial class MainPage : UserControl
  2.     {
  3.         private GameDataContext dataContext;
  4.         private Uri serviceUri;
  5.  
  6.         public MainPage()
  7.         {
  8.             InitializeComponent();
  9.  
  10.             //creates the data service context
  11.             this.serviceUri = new Uri("http://localhost:3213/GameDataService.svc");
  12.             this.dataContext = new GameDataContext(this.serviceUri);
  13.         }
  14.  
  15.         /// <summary>
  16.         /// Gets or sets the list of games currently registered
  17.         /// </summary>
  18.         public List<Game> Games { get; set; }
  19.  
  20.         private void UserControl_Loaded(object sender, RoutedEventArgs e)
  21.         {
  22.             //creates a query to get all games saved.
  23.             var query = this.dataContext.CreateQuery<Game>("Games");
  24.  
  25.             //asynchronously executes the query, passing a lambda exp. to get the results.
  26.             query.BeginExecute((result) =>
  27.                 {
  28.                     var games = query.EndExecute(result);
  29.                     
  30.                     //gets the list of games returned by the data service and
  31.                     //set the ItemsSource of the DataForm to use DataBinding
  32.                     this.Games = games.ToList();
  33.                     this.gameForm.ItemsSource = this.Games;                    
  34.                 }, null);
  35.         }
  36.  
  37.         private void Button_Click(object sender, RoutedEventArgs e)
  38.         {
  39.             //Saves the changes made to the games (inserts, updates, deletes).
  40.             this.dataContext.BeginSaveChanges((result) =>
  41.             {
  42.                 this.dataContext.EndSaveChanges(result);
  43.             }, null);
  44.         }
  45.  
  46.         private void gameForm_DeletingItem(object sender, System.ComponentModel.CancelEventArgs e)
  47.         {
  48.             //deletes a game.
  49.             var game = this.gameForm.CurrentItem as Game;
  50.  
  51.             this.dataContext.DeleteObject(game);
  52.         }
  53.  
  54.         private void gameForm_EditEnding(object sender, DataFormEditEndingEventArgs e)
  55.         {
  56.             if (e.EditAction == DataFormEditAction.Commit)
  57.             {
  58.                 //gets the game being edited.
  59.                 var currentGame = this.gameForm.CurrentItem as Game;
  60.  
  61.                 //if the PartitionKey and RowKey is null, then it's a new game.
  62.                 if (currentGame.PartitionKey == null && currentGame.RowKey == null)
  63.                 {
  64.                     currentGame.PartitionKey = DateTime.Now.ToString("MMddyyy");
  65.  
  66.                     // Row key allows sorting, so we make sure the rows come back in time order.
  67.                     currentGame.RowKey = String.Format("{0:10}_{1}", DateTime.MaxValue.Ticks - DateTime.Now.Ticks, Guid.NewGuid());
  68.  
  69.                     //adds a new game.
  70.                     this.dataContext.AddToGames(currentGame);
  71.                 }
  72.                 else
  73.                     //updates the game data.
  74.                     this.dataContext.UpdateObject(currentGame);
  75.             }
  76.         }
  77.     }
Snippet 5

No construtor, criamos o nosso DataContext, que será uma espécie de cliente do nosso serviço, e irá fazer toda a comunicação com o serviço, fazer a serialização dos objetos por debaixo dos panos para nós. Isso ajuda bastante, pois senão teríamos que fazer toda a comunicação e a leitura do ATOM retornado pelo serviço (o que pode ser bem trabalhoso!). Note que precisamos passar a URI do serviço no construtor do DataContext. No meu ambiente de desenvolvimento, o serviço estava rodando na porta 3213, no seu caso, ela pode mudar, então preste atenção nisso e faça as alterações necessárias.

No evento Loaded do UserControl, chamamos o nosso serviço e retornamos os games já cadastrados. Para isso, criamos uma query passando o nome da entidade que desejamos, nesse caso “Games”.

Para receber os dados, precisamos fazer a requisição de maneira assíncrona, então chamamos o método BeginExecute do nossa query, e passamos uma lambda expression que será chamada quando a requisição terminar trazendo os resultados. Esses resultados então são guardados na nossa propriedade Games (do tipo List<Game>) e usado como fonte de dados (ItemsSource) do nosso DataForm para fazermos o DataBinding.

Nos eventos DeletingItem e EditEnding do DataForm, criamos, editamos e deletamos um item do TableStorage. Isso é feito chamando-se o método AddToGames, UpdateObejct e DeleteObject respectivamente. Note que ao chamar esses métodos, a requisição salvar as alterações não é realmente feita. Os dados só são sincronizados quando chamamos o método SaveChanges no evento do click do botão.

Importante: no evento EditEnding definimos as duas propriedades necessárias para todos os items salvos no TableStorage do Azure. Então checamos se esses valores já foram criados para o item que foi editado. Caso esses valores sejam null, setamos a PartitionKey para a data de hoje (assim os objetos criados em um determinado dia serão armazenados sequencialmente e no mesmo lugar no storage do Azure) e a RowKey para um valor que possa ser ordenado pela hora em que eles foram criados, adicionando-se uma GUID que irá identificá-los unicamente.

Conclusão

Neste tutorial, vimos como expor e acessar dados salvos no Table Storage do Windows Azure em uma aplicação Silverlight. Vimos que não é uma tarefa tão complicada e que pode agregar grande valor as aplicações. Principalmente agora, que podemos criar aplicações Silverlight que rodam tanto no PC e Windows Phone. Ou seja, podemos ter nossa aplicação rodando em dois dispositivos acessando os mesmos dados, que estão salvos na núvem, assim acessíveis de qualquer lugar.


Permalink | Comentários (0) | Post RSSRSS comment feed

Formulários em Silverlight com o controle DataForm

Introdução

O Silverlight é uma plataforma muito boa para construção de aplicações Line-of-Business, que oferecem uma interface rica e que responde rapidamente ao usuário.

Neste tutorial, irei explicar como criar um formulário de cadastro em Silverlight, usando o controle DataForm, e aplicando o Pattern MVVM para expor os dados ao UserControl do Silverlight. Caso voce não conheça o pattern, leia um tutorial postado anteriormente neste site que explica como o pattern funciona.

O código deste tutorial está disponível no meu Skydrive:

http://cid-1498c467c14dc20b.skydrive.live.com/self.aspx/BrSilverlight/Tutoriais/GameStore.zip

Criando o projeto

Como nossa aplicação será um esboço de uma aplicação LOB, criaremos uma NavigationApplication, que já inclui por padrão um sistema de navegação, parecido com páginas Web. Apesar de termos somente uma “página” nesta aplicação, acho que esse padrão é interessante para aplicações LOB.

Para criar um projeto do tipo NavigationApplication, vá em New Project no Visual Studio, e escolha a opção Silverlight Navigation Application.

image

Model

Neste tutorial, iremos criar um formulário para cadastro de jogos. Iremos guardar dados como nome, numero de jogadores, categoria, avaliação e a URL do site.

Crie uma nova classe chamada Game e adicione as seguintes propriedades.

Game.cs
  1. public class Game
  2. {
  3.     [Required(ErrorMessage="Game name is required")]
  4.     public String Name { get; set; }
  5.  
  6.     [RegularExpression(@"((https?|ftp|gopher|telnet|file|notes|ms-help):((//)|(\\\\))+[\w\d:#@%/;$()~_?\+-=\\\.&]*)", ErrorMessage="Invalid Website ULR")]
  7.     public String Website { get; set; }
  8.  
  9.     [Required(ErrorMessage="Number of Players is required")]
  10.     public int NumberOfPlayers { get; set; }
  11.  
  12.     [Range(0, 1)]
  13.     public Double Rating { get; set; }
  14.  
  15.     [Required(ErrorMessage="Category is required")]
  16.     public String Category { get; set; }
  17. }

 

Snippet 1

Essa classe será o Model na nossa aplicação, guardando as informações dos games.

Obs: Note que as propriedades estão marcadas com alguns Attributes. Ignore

ViewModel

O ViewModel neste exemplo será bastante simples. Será utilizado somente para expor os dados para a nossa View. Adicione a classe GameViewModel ao projeto e o seguinte código:

GameViewModel.cs
  1. public class GameViewModel : INotifyPropertyChanged
  2. {
  3.     public GameViewModel()
  4.     {
  5.         this.CreateGames();
  6.     }
  7.  
  8.     private void CreateGames()
  9.     {
  10.         List<Game> games = new List<Game>();
  11.  
  12.         Game game1 = new Game();
  13.         game1.Name = "Halo 3";
  14.         game1.Rating = 1;
  15.         game1.NumberOfPlayers = 4;
  16.         game1.Website = "http://halo.xbox.com/halo3/";
  17.         var game1Category = "Shooter";
  18.         game1.Category = game1Category;
  19.  
  20.         games.Add(game1);
  21.  
  22.         Game game2 = new Game();
  23.         game2.Name = "Gears of War 2";
  24.         game2.Rating =0.8;
  25.         game2.NumberOfPlayers = 10;
  26.         game2.Website = "http://gearsofwar.xbox.com/";
  27.         game2.Category = game1Category;
  28.  
  29.         games.Add(game2);
  30.  
  31.         Game game3 = new Game();
  32.         game3.Name = "Fifa Soccer 10";
  33.         game3.Rating = 0.7;
  34.         game3.NumberOfPlayers = 4;
  35.         game3.Website = "http://fifa.easports.com/";
  36.  
  37.         var game3Category = "Sports";
  38.         game3.Category = game3Category;
  39.  
  40.         games.Add(game3);
  41.  
  42.         this.Games = games;
  43.     }
  44.  
  45.     private List<Game> games;
  46.     public List<Game> Games
  47.     {
  48.         get { return games; }
  49.         private set { games = value; OnPropertyChanged("Games"); }
  50.     }
  51.  
  52.     public event PropertyChangedEventHandler PropertyChanged;
  53.  
  54.     private void OnPropertyChanged(String propertyName)
  55.     {
  56.         if (this.PropertyChanged != null)
  57.             this.PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
  58.     }
  59. }

 

 

 

Snippet 2

No nosso ViewModel, criamos uma propriedade chamada Games do tipo List<Game>. Essa propriedade será utilizada pela nossa View para mostrar os dados dos games cadastrados. O método CreateGames cria alguns objetos iniciais, somente para o objetivo de demonstração deste exemplo.

Obs: Note que esta classe implementa a interface INotifyPropertyChanged, que serve para notificar a View de que a propriedade do nosso ViewModel foi modificada, atualizando as informações na tela.

View

Como no nosso projeto iremos utilizar alguns controles do Silverlight Tookit, iremos adicionar as seguintes DLLs ao nosso projeto:

  • System.Windows.Controls.Data.DataForm.Toolkit.dll
  • System.Windows.Controls.Input.Toolkit.dll

Como na versão Beta 2 do Visual Studio 2010 há um pequeno bug na hora de adicionar referências a DLLs, voce terá que navegar até a pasta onde está instalado o Silverlight Control Toolkit e adicionar as DLLs. Geralmente, o Toolkit está instalado em:

C:\Program Files\Microsoft SDKs\Silverlight\v4.0\Toolkit\Bin

Nossa View, que irá conter o controle DataForm, será o Home.xaml, na pasta Views do projeto. Adicione o Xaml abaixo no arquivo Home.xaml.

Code Snippet
  1. <navigation:Page x:Class="GameStore.Home"
  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" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
  5.     xmlns:navigation="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Navigation"
  6.     xmlns:dt="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data.DataForm.Toolkit"
  7.     xmlns:in="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Input.Toolkit"
  8.     xmlns:vm="clr-namespace:GameStore.ViewModel"
  9.     mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480"
  10.     Title="Home"
  11.     Style="{StaticResource PageStyle}">
  12.     
  13.     <UserControl.Resources>
  14.         <vm:GameViewModel x:Key="ViewModel" />
  15.     </UserControl.Resources>
  16.  
  17.     <Grid x:Name="LayoutRoot" DataContext="{StaticResource ViewModel}">
  18.         <ScrollViewer x:Name="PageScrollViewer" Style="{StaticResource PageScrollViewerStyle}">
  19.  
  20.             <StackPanel x:Name="ContentStackPanel">
  21.  
  22.                 <TextBlock x:Name="HeaderText" Style="{StaticResource HeaderTextStyle}"
  23.                            Text="Welcome to the Game Store"/>
  24.  
  25.                 <dt:DataForm AutoGenerateFields="False" AutoEdit="False" ItemsSource="{Binding ElementName=LayoutRoot, Path=DataContext.Games}">
  26.                     <dt:DataForm.EditTemplate>
  27.                         <DataTemplate>
  28.                             <StackPanel dt:DataField.IsFieldGroup="True">
  29.                                 <dt:DataField Label="Name:" Description="Name of the game">
  30.                                     <TextBox Text="{Binding Path=Name, Mode=TwoWay}" />
  31.                                 </dt:DataField>
  32.                                 <dt:DataField Label="Website:" Description="Website of the game">
  33.                                     <TextBox Text="{Binding Path=Website, Mode=TwoWay}" />
  34.                                 </dt:DataField>
  35.                                 <dt:DataField Label="Number of Players" Description="Number of players supported by the game" >
  36.                                     <in:NumericUpDown Minimum="1" Value="{Binding Path=NumberOfPlayers, Mode=TwoWay}" Width="70" HorizontalAlignment="Left" />
  37.                                 </dt:DataField>
  38.                                 <dt:DataField Label="Rating" Description="How good is this game?">
  39.                                     <in:Rating ItemCount="5" Value="{Binding Path=Rating, Mode=TwoWay}" />
  40.                                 </dt:DataField>
  41.                                 <dt:DataField Label="Category" Description="Game category (Shooter, Sports, Action, ...)">
  42.                                     <TextBox Text="{Binding Path=Category, Mode=TwoWay}" />
  43.                                 </dt:DataField>
  44.                             </StackPanel>
  45.                         </DataTemplate>
  46.                     </dt:DataForm.EditTemplate>
  47.                 </dt:DataForm>
  48.  
  49.             </StackPanel>
  50.  
  51.         </ScrollViewer>
  52.     </Grid>
  53.  
  54. </navigation:Page>

Snippet 3

O controle DataForm

No Silverlight Tookit, existe um controle muito bom para a construção de formulários para receber dados do usuário. É um controle muito fácil de usar, e também muito flexível. Voce pode optar por gerar os campos automaticamente (setando a propriedade AutoGenerateFields para True), ou então especificar explicitamente quais controles serão utilizados para mostrar os dados. No nosso caso, setamos a propriedade AutoGenerateFields para false e customizamos a aparência do Form, definindo o EditTemplate do DataForm.

A maneira mais fácil de definir os controles utilizados no Form é criando um StackPanel que irá conter vários DataFields, que irão conter os Labels e terão como Content o controle que irá apresentar/receber a informação do usuário.

Nesse exemplo, temos vários DataFields, cada um com um controle Databound que irá apresentar/receber os dados do usuário. Por exemplo, utilizamos uma TextBox para o nome do game, um NumericUpDown para o número de jogadores, um Rating para a avaliação do jogo, etc… Note que em cada DataField setamos as propriedades Label (precisa explicar?) e Description (uma informação extra que é mostrada ao usuário sobre uma propriedade em particular.

Obs: usamos TwoWay Binding nos controles, assim quando o usuário editar as informações de um game, elas serão automaticamente atualizadas no noss Model.

Quando rodamos a aplicação, veremos o seguinte resultado:

image

 

 

 

 

Model Revisited

Lembra-se daqueles Attributes que definimos nas propriedades do nosso Model? Eles servem para fazermos validação dos dados digitados pelo usuário. Por exemplo:

O Attribute Required indica que o usuário precisa preencher o campo respectivo a propriedade marcada com esse Attribute. No nosso exemplo, as propriedades Name, NumberOfPlayers e Category. Esse Attribute também permite definir uma mensagem de erro que é apresentada ao usuário caso o usuário não preencha o campo.

Outro attribute interessante é o o RegularExpression, que permite que a validação de um campo através de uma Regular Expression. Usamos esse Attribute na propriedade Website, e definimos também uma ErrorMessage caso a Regex falhe.

Outro Attribute que também usamos é o Range, que define entre quais valores uma propriedade numérica pode estar.

Para saber mais sobre outros Attributes de validação, acesse a documentação do Namespace System.ComponentModel.DataAnnotations em http://msdn.microsoft.com/en-us/library/system.componentmodel.dataannotations.aspx.

Quando inserimos alguma entrada inválida no DataForm, e clicamos em OK (quando editando ou adicionando um item novo), as mensagens de erro são automaticamente mostradas, conforme a imagem abaixo:

image 

 

Quando corrigimos os erros, e clicamos em OK, as mensagens somem e o novo game é adicionado a Collection no nosso ViewModel.

image


Categories: Silverlight
Permalink | Comentários (0) | Post RSSRSS comment feed

Novidades do Silverlight 4 – Parte IV: o Pattern MVVM (Model-View-ViewModel)

Introdução

Nos posts anteriores, iniciei uma série de tutoriais que cobre algumas das novas features do Silverlight 4, e escrevi especificamente sobre Elevated Trust em aplicações Out-of-Browser, como ler o feed RSS dos webcasts no MSDN, e utilizar a interface COM do Outlook para adicionar um item ao calendário.

Hoje, irei explicar como criar a UI da aplicação e utilizar o Pattern MVVM para fazer a separação do XAML (interface) e do código utilizando Bindings e a nova Interface do Silverlight 4, ICommand.

Voce pode baixar o código deste tutorial no link abaixo:

http://cid-1498c467c14dc20b.skydrive.live.com/self.aspx/BrSilverlight/Tutoriais/MsdnWebcastsCalendar.zip

Como funciona o MVVM?

MVVM é um Pattern Arquitetural que tem como objetivo promover uma separação de responsabilidades. A camada de apresentação (View) fica responsável somente por receber os dados (Model), e apresentá-los ao usuário, enquanto há uma camada intermediária (ViewModel) que fica responsável por toda a lógica de negócio, e expor os dados para a respectiva View. Resumindo:

  • Model: é a estrutura de dados que expõe as informações sobre o que desejamos apresentar ao usuário
  • View: define a interface de usuário (XAML)
  • ViewModel: trata as ações do usuário, como por exemplo o click de um botão, e expõe os Models a View

Quando se aplica o Pattern a aplicações Silverlight ou WPF, a ligação dos Models é feita com a utilização de DataBinding e os eventos podem ser tratados utilizando-se Commands ou a API de Triggers do Expression Blend.

Analise a figura abaixo para visualizar melhor os tres elementos do Pattern:

image

Figura 1

 

MVVM na prática

Até aqui, já criamos algumas classes que possuem algumas funcionalidades como:

RssReader: lê os feeds RSS dos webcasts MSDN;

OutlookApplication: chama a API COM do Outlook para criar um item no calendário.

Essas classes farão parte da nossa lógica de negócio.

Agora, iremos criar os tres elementos que compoem o Pattern MVVM.

Model

Nós já criamos o Model da nossa aplicação na parte II. O Model da nossa aplicação será a classe FeedItemModel. Ela irá guardar as informações lidas do XML do RSS dos webcasts. Abaixo, o código da classe FeedItemModel:

Code Snippet
  1. public class FeedItemModel
  2. {
  3.     public String Title { get; set; }
  4.     public Uri Link { get; set; }
  5.     public DateTime Date { get; set; }
  6.     public Int32 Duration { get; set; }
  7.     public String Creator { get; set; }
  8.     public String Description { get; set; }
  9. }

Snippet 1

ViewModel

Code Snippet
  1. public class FeedViewModel : INotifyPropertyChanged
  2. {
  3.     public FeedViewModel ( )
  4.     {
  5.         this.ReadRssFeeds ( );
  6.     }
  7.  
  8.     public ObservableCollection<FeedItemModel> Feeds { get; private set; }
  9.     public ICommand OutlookApplicationCommand { get { return new OutlookCommand ( ); } }
  10.  
  11.     private void ReadRssFeeds ( )
  12.     {
  13.         RssReader reader = new RssReader ( );
  14.         reader.RssFeedReadCompleted += new EventHandler<RssFeedReadCompletedEventArgs> ( reader_RssFeedReadCompleted );
  15.         reader.ReadRssFeedAsync ( new Uri ( "http://www.msdnbrasil.com.br/Microsoft.NewRSS/RssItems.aspx?segment=Arquiteto%20de%20Solu%C3%A7%C3%B5es--and--Desenvolvedores&type=WebCasts%20Online&futureitems=S&auth=747dab31-42e3-69f3-7765-618e61722d9a" ) );
  16.     }
  17.  
  18.     void reader_RssFeedReadCompleted ( object sender, RssFeedReadCompletedEventArgs e )
  19.     {
  20.         if ( this.Feeds == null )
  21.             this.Feeds = new ObservableCollection<FeedItemModel> ( );
  22.  
  23.         Array.ForEach ( e.Feeds.ToArray ( ), ( feed ) => this.Feeds.Add ( feed ) );
  24.         OnPropertyChanged ( "Feeds" );
  25.     }
  26.  
  27.     #region INotifyPropertyChanged Members
  28.  
  29.     public event PropertyChangedEventHandler PropertyChanged;
  30.  
  31.     private void OnPropertyChanged ( string propertyName )
  32.     {
  33.         if ( PropertyChanged != null )
  34.         {
  35.             PropertyChanged ( this, new PropertyChangedEventArgs ( propertyName ) );
  36.         }
  37.     }
  38.  
  39.     #endregion

Snippet 2

Como um dos objetivos do ViewModel é expor os dados do Model para a View, temos a propriedade Feeds, que é uma ObservableCollection de FeedItemModels.

No construtor da classe, chamamos o método ReadRssFeeds, que por sua vez irá utilizar um objeto do tipo RssReader, que irá ler os feeds RSS. Quando a leitura dos feeds RSS terminar, o método reader_RssFeedReadCompleted será chamado, onde iremos adicionar todos os itens lidos a nossa coleção de FeeditemModels (Feeds).

Importante!

Nosso ViewModel implementa a Interface INotifyPropertyChanged. Temos implementar essa interface para notificar a View que nossa propriedade Feeds sofreu modificações (quando adicionamos os feeds a ela). Assim a engine de Databinding poderá atualizar os valores dos controles na nossa View. Internamente, a engine de Databinding irá associar o evento PropertyChanged, que irá atualizar os controles que fazem Binding com as propriedades dos objetos da classe FeedItemModel. Experimente comentar a parte onde chamamos o método OnPropertyChanged (linha 24) e voce verá que não irá aparecer nada na tela.

No nosso ViewModel, temos uma propriedade chamada OutlookApplicationCommand do tipo ICommand. Commands são uma nova feature do Silverlight 4 (que já existia em WPF), que facilita a implementação do Pattern MVVM.

Crie uma classe chamada OutlookCommand e adicione o código abaixo:

Code Snippet
  1. public class OutlookCommand : ICommand
  2. {
  3.     #region ICommand Members
  4.  
  5.     public event EventHandler CanExecuteChanged;
  6.  
  7.     public bool CanExecute ( object parameter )
  8.     {
  9.         return OutlookApplication.IsAvailable;
  10.     }
  11.  
  12.     public void Execute ( object parameter )
  13.     {
  14.         FeedItemModel model;
  15.         if(parameter is FeedItemModel)
  16.             model = parameter as FeedItemModel;
  17.         else
  18.             throw new ArgumentException("Invalid argument", "parameter");
  19.  
  20.         try
  21.         {
  22.             OutlookApplication.CreateCalendarItem ( model );
  23.             MessageBox.Show("Webcast added to your Outlook Calendar");
  24.         }
  25.         catch ( Exception )
  26.         {
  27.             MessageBox.Show ( "An error ocurred when trying to open Outlook resources.\nMake sure you have Microsoft Outlook for Windows installed and properly configured." );
  28.         }
  29.     }
  30.  
  31.     #endregion
  32. }

Snippet 3

A classe OutlookCommand implementa a interface ICommand, que possui os seguintes métodos:

CanExecute: esse método é responsável por dizer se o Command pode ou não ser executado. Assim, quando associarmos esse Command com um botão na nossa View, caso este método retorne false, o botão ficará desabilitado. No nosso caso, retornamos o valor da propriedade “IsAvailable” da classe OutlookApplication (ver a Parte III desta série de tutoriais).

Execute: esse método executa a ação (neste caso, inserir o item no calendário do Outlook com as informações do Webcast). Note que esse método, assim como o método CanExecute, recebe um parâmetro. Veremos abaixo como passar esse parâmetro pelo XAML. Esse parâmetro deve ser do tipo do nosso Model (FeedItemModel). Caso não seja, lançamos uma Exception.

View

Nossa View será representada pelo UserControl MainPage, que terá o seguinte XAML:

Code Snippet
  1. <UserControl x:Class="MsdnWebcastsCalendar.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.     xmlns:vm="clr-namespace:MsdnWebcastsCalendar.ViewModel"
  7.     mc:Ignorable="d"
  8.     Width="800" Height="600">
  9.  
  10.     <UserControl.Resources>
  11.         <vm:FeedViewModel x:Key="FeedViewModelDataContext" />
  12.         <Style x:Key="ListBoxItemTemplate" TargetType="ListBox">
  13.             <Setter Property="ItemTemplate">
  14.                 <Setter.Value>
  15.                     <DataTemplate>
  16.                         <StackPanel Width="{Binding ElementName=LayoutRoot, Path=ActualWidth}">
  17.                             <StackPanel.Background>
  18.                                 <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
  19.                                     <GradientStop Color="#FF36295E" Offset="0"/>
  20.                                     <GradientStop Color="#FFA2DCF3" Offset="1"/>
  21.                                 </LinearGradientBrush>
  22.                             </StackPanel.Background>
  23.                             <StackPanel Orientation="Horizontal">
  24.                                 <Button Command="{Binding ElementName=LayoutRoot, Path=DataContext.OutlookApplicationCommand}"
  25.                                         CommandParameter="{Binding}"
  26.                                         ToolTipService.ToolTip="Adicionar webcast ao calendário do Outlook">
  27.                                     <Image Source="/MsdnWebcastsCalendar;component/Microsoft_Outlook.png" />
  28.                                 </Button>
  29.                                 <HyperlinkButton Margin="0, 11, 0, 0" Content="{Binding Path=Title}" NavigateUri="{Binding Path=Link}" />
  30.                             </StackPanel>
  31.                             <TextBox Text="{Binding Path=Description}" IsReadOnly="True" TextWrapping="Wrap" />
  32.                             <StackPanel Orientation="Horizontal">
  33.                                 <TextBlock Text="Data: " FontStyle="Italic" />
  34.                                 <TextBlock Text="{Binding Path=Date}" FontStyle="Italic" />
  35.                                 <TextBlock Text="-" />
  36.                                 <TextBlock Text="{Binding Path=Creator}" FontWeight="Bold" />
  37.                             </StackPanel>
  38.                         </StackPanel>
  39.                     </DataTemplate>
  40.                 </Setter.Value>
  41.             </Setter>
  42.         </Style>
  43.     </UserControl.Resources>
  44.  
  45.     <Grid x:Name="LayoutRoot" Background="White" DataContext="{Binding Source={StaticResource FeedViewModelDataContext}}">
  46.         <ListBox x:Name="feedsListBox" Style="{StaticResource ListBoxItemTemplate}" ItemsSource="{Binding Path=Feeds}" />
  47.     </Grid>
  48. </UserControl>

Snippet 4

Aqui, nós temos uma ListBox que irá mostrar as informações dos feeds RSS dos Webcasts MSDN. Para explicar os detalhes do XAML acima, decidi separar em alguns tópicos.

Referenciando o ViewModel na View

Para unir o ViewModel a View da aplicação, iremos criar um objeto do tipo do nosso ViewModel (FeedViewModel) no XAML. Isso pode ser feito adicionando o XML Namespace com referencia ao namespace do nosso ViewModel (veja linha 6), e guardando o ViewModel como uma resource do UserControl, criando-se uma Key, para que possamos referenciá-lo mais tarde (linha 11).

Configurando os Bindings

No Grid LayoutRoot, definimos a propriedade DataContext, que irá guardar a referência para o ViewModel. Assim poderemos usar DataBinding para mostrar os valores dos Feeds RSS nos controles.

Logo abaixo, na Listbox (linha 46), a propriedade ItemsSource recebe o valor da propriedade Feeds do ViewModel (a propriedade Path do Binding recebe o nome da propriedade do objeto que é usado como fonte de dados no DataBinding, no nosso caso, o ViewModel).

No style dos Items da ListBox, também utilizamos DataBinding para setar os valores dos controles que irão mostrar os dados do Webcast. Por exemplo, na linha 31, temos uma TextBox que recebe o valor da propriedade Description, um HyperLinkButton que recebe o valor da propriedade Link, etc…

Importante!

No estilo definido para nossa ListBox, o objeto que é a fonte de DataBinding é um dos items da ObservableCollection Feeds do FeedViewModel. Isso por que a propriedade ItemsSource da ListBox, recebe a referência da propriedade Feeds, e os controles definidos no estilo são usados para cada ListBoxItem criada.

Resumindo: a propriedade ItemsSource recebe o valor da propriedade Feeds do nosso ViewModel. Isso irá criar vários ListBoxItems. E cada ListBoxItem terá como fonte de dados para o DataBinding, um elemento da ObservableCollection Feeds.

Configurando o Command

Cada ListBoxItem terá um botão que terá como Command, o Command definido no nosso ViewModel (OutlookApplicationCommand). Para definirmos isso no XAML, utilizamos Binding de elemento para elemento. Na linha 24, onde criamos o botão, definimos a propriedade Command utilizando Binding, e definimos a fonte como a propriedade OutlookApplicationCommand do DataContext (FeedViewModel) do controle LayoutRoot. Também passamos como parâmetro (que é passado para o método Execute do Command), a fonte de Binding do ListBoxItem (FeedItemModel).

Quando rodarmos nossa aplicação, teremos o seguinte resultado (lembre-se que para utilizarmos a API do Outlook, temos que rodar Out-of-Browser com Elevated Permissions):

image

Quando clicarmos no botão com o ícone do Outlook, ele irá chamar o método Execute do OutlookCommand, adicionando um item no calendário do Outlook com as informações do Webcast, conforme as imagens abaixo.

image

image

Bem, chegamos ao final dessa série de tutoriais. Espero que o conteúdo tenha ficado claro para todos. Caso alguma coisa não tenha ficado clara, ou alguma dúvida não foi respondida, não exite em deixar um comentário ou entre em contato comigo (http://brsilverlight.com/contact.aspx) ou mande uma mensagem para mim no Twitter (http://twitter.com/breno_ferreira).


Permalink | Comentários (0) | Post RSSRSS comment feed

Novidades do Silverlight 4 – Parte III: COM Interop

Introdução

Nos posts anteriores, iniciei uma série de tutoriais que cobre algumas das novas features do Silverlight 4, e escrevi especificamente sobre Elevated Trust em aplicações Out-of-Browser e como ler o feed RSS dos webcasts no MSDN.

Hoje, irei explicar como iremos podemos acessar recursos disponíveis na máquina do usuário através de interfaces COM.

Voce pode baixar o código deste tutorial no link abaixo:

http://cid-1498c467c14dc20b.skydrive.live.com/self.aspx/BrSilverlight/Tutoriais/MsdnWebcastsCalendar.zip

O que é COM?

COM, ou Component Object Model, é uma tecnologia desenvolvida pela Microsoft na década de 90 para facilitar o desenvolvimento de componentes (bibliotecas). Esses componentes podem ser usados por vários processos independentes, através de interfaces que abstraem a funcionalidade por debaixo dos panos. Assim, chamadas a objetos COM são feitas sem necessariamente saber-se da implementação, bastando chamar os métodos especificados na interface.

Voce pode pensar em COM como serviços que podem ser chamados (através dessas interfaces) por processos rodando na máquina do usuário. Um método é chamado, que irá fazer alguma tarefa, e devolver, se necessário, alguma resposta.

Obs: note que COM é uma tecnologia presente somente em ambientes Windows. Por isso, quando tentarmos acessar bibliotecas COM em nossas aplicações Silverlight, devemos checar, primeiramente, se estamos rodando fora do browser com Elevated Trust e também se a aplicação está rodando em um ambiente Windows. Para saber mais sobre COM, acesse o artigo na Wikipedia e a documentação na MSDN 

Usando COM no Silverlight 4

Para acessar as interfaces COM disponíveis na máquina do usuário, usamos a classe ComAutomationFactory, que possui métodos para acessar e retornar referências de interfaces COM.

Primeiramente, antes de tentarmos acessar qualquer objeto COM, devemos saber se é possível acessá-la (somente é possível acessar bibliotecas COM se estivermos rodando Out-of-Browser com Elevated Trust e em ambiente Windows). Para isso, a classe ComAutomationFactory possui uma propriedade chamada IsAvailable, que já verifica isso para nós.

Para acessarmos uma referência a uma interface COM, devemos chamar o método CreateObject, passando o ProgrammaticID da biblioteca COM que desejamos acessar. Esse método nos retorna um IDisposable, pois depois de feitas todas as operações necessárias, devemos liberar as referências ao objeto COM, chamando o método Dispose().

Como o Silverlight não tem como saber quais são os métodos e propriedades da interface, não é possível ter um objeto estaticamente tipado, e assim termos o adorado Intellisense no Visual Studio. As variáveis que guardam as referências das interfaces COM deverão ser do tipo Object em VB ou dynamic em C#. Assim, os tipos serão determinados em tempo de execução.

Utilizando a API do Outlook

Sem mais teoria, vamos a prática. No projeto Silverlight, adicione uma classe chamada OutlookApplication. Essa classe irá ter um método, que irá criar um item no calendário do Outlook, e uma propriedade que irá determinar se a aplicação tem as permissões necessárias para fazer COM Interop.

Adicione o seguinte código a classe OutlookApplication:

Code Snippet
  1. public static Boolean IsAvailable { get { return ComAutomationFactory.IsAvailable; } }
  2.  
  3. public static void CreateCalendarItem ( FeedItemModel feedItem )
  4. {
  5.     try
  6.     {
  7.         using ( dynamic outlook = ComAutomationFactory.CreateObject ( ( "Outlook.Application" ) ) )
  8.         {
  9.             dynamic appointment = outlook.CreateItem ( 1 ); //1 - AppointmentItem on calendar. More info: http://msdn.microsoft.com/en-us/library/bb208104.aspx
  10.             appointment.Subject = feedItem.Title;
  11.             appointment.Body = feedItem.Description;
  12.             appointment.Duration = feedItem.Duration;
  13.             appointment.Start = feedItem.Date;
  14.             appointment.End = feedItem.Date.AddMinutes ( feedItem.Duration );
  15.             appointment.Location = "Live Meeting";
  16.             appointment.ReminderSet = true;
  17.             appointment.ReminderMinutesBeforeStart = 15;
  18.             appointment.Save ( );
  19.             appointment.Send ( );
  20.         }
  21.     }
  22.     catch ( Exception )
  23.     {
  24.         throw;
  25.     }
  26. }

Snippet 1

Primeiramente, definimos a propriedade IsAvailable, que verifica se a aplicação possui privilégios para chamar objetos COM.

O método CreateCalendarItem, recebe um objeto que guarda um item do RSS dos Webcasts com as informações sobre título, data, duração, etc…

O objeto outlook, guarda a referência da interface COM. Com esse objeto, podemos chamar os métodos e propriedades expostos pela interface. Note que usamos um bloco using. Com isso garantimos que no final da operação, todos os recursos e memória serão liberados.

Em seguida, chamamos o método CreateItem, que irá criar um objeto referente a um item no calendário do Outlook. Setamos algumas propriedades, como

  • Subject (assunto)
  • Body(descrição)
  • Duration(duração)
  • Start e End (data de início e fim, respectivamente)
  • Location (local)
  • ReminderSet (se um aviso é mostrado ao usuário antes do evento começar)
  • ReminderMinutesBeforeStart (quanto tempo antes do evento começar, o aviso é mostrado ao usuário)

Finalmente, chamamos os métodos Save e Send, que salvam o item no calendário do Outlook.

No próximo post, iremos juntar tudo o que fizemos até agora e aplicar o pattern MVVM utilizando a interface ICommand. Até lá

Breno Ferreira


Permalink | Comentários (0) | Post RSSRSS comment feed

Novidades do Silverlight 4 – Parte II: Ler Feeds RSS

Introdução

No post anterior, iniciei uma série de tutoriais que cobre algumas das novas features do Silverlight 4, e escrevi especificamente sobre Elevated Trust em aplicações Out-of-Browser.

Hoje, irei explicar como fazemos para ler um feed RSS utilizando Linq To XML para lermos o XML retornado pelo feed.

Voce pode baixar o código deste tutorial no link abaixo:

http://cid-1498c467c14dc20b.skydrive.live.com/self.aspx/BrSilverlight/Tutoriais/MsdnWebcastsCalendar.zip

Lendo Feeds RSS no Silverlight

Feed RSS nada mais é do que conteúdo XML usado para publicar updates em sites, blogs, etc… Todo feed RSS possui uma URL acessível globalmente, que pode ser acessada por algum cliente e que irá dar como resposta esse XML.

Para lermos o RSS, utilizamos a classe WebClient, que possibilita receber a resposta de algum recurso na web, exposto através de uma URL. Com a resposta deste recurso (XML do RSS), utilizamos o Linq To XML para analisar o XML e ler os dados contidos nele.

Para começarmos, adicione uma classe ao projeto Silverlight chamado FeedItemModel. Essa classe irá ser o Model (usando o Pattern MVVM, mas por enquando ignore isso, veremos isso em um próximo post) que irá guardar as informações do Feed.

Como estaremos lendo o feed dos webcasts no MSDN, temos que guardar algumas informações como:

  • Título do Webcast
  • Link para cadastro
  • Data
  • Duração
  • Criador (quem são os palestrantes)
  • Descrição

Na classe FeedItemModel, adiciona as seguintes propriedades, que irão guardar as informações acima:

Code Snippet
  1. public class FeedItemModel
  2. {
  3.     public String Title { get; set; }
  4.     public Uri Link { get; set; }
  5.     public DateTime Date { get; set; }
  6.     public Int32 Duration { get; set; }
  7.     public String Creator { get; set; }
  8.     public String Description { get; set; }
  9. }

Snippet 1

Agora, temos que ler o conteúdo do feed.

Adicione uma classe chamada RssReader. Essa classe irá possuir um método que faz o download do XML do feed.

Adicione a classe RssReader o seguinte trecho de código:

Code Snippet
  1. public void ReadRssFeedAsync ( Uri feedUri )
  2.         {
  3.             WebClient client = new WebClient ( );
  4.             client.DownloadStringCompleted += new DownloadStringCompletedEventHandler ( client_DownloadStringCompleted );
  5.             client.DownloadStringAsync ( feedUri );
  6.         }
  7.  
  8.         private void client_DownloadStringCompleted ( object sender, DownloadStringCompletedEventArgs e )
  9.         {
  10.             if ( e.Error == null )
  11.             {
  12.                 XDocument doc = XDocument.Parse ( e.Result );
  13.  
  14.                 var items = from item in doc.Descendants ( "item" )
  15.                             select new FeedItemModel ( )
  16.                             {
  17.                                 Title = item.Element ( "title" ).Value,
  18.                                 Link = new Uri ( item.Element ( "link" ).Value.ToString ( ) ),
  19.                                 Date = ConvertToDateTime( item.Element ( "pubDate" ).Value ),
  20.                                 Creator = item.Element ( XName.Get("creator", "http://purl.org/dc/elements/1.1/") ).Value,
  21.                                 Description = ReplaceHTMLChars(item.Element ( "description" ).Value),
  22.                                 Duration = GetDurationFromDescriptionn(item.Element("description").Value),
  23.                             };
  24.  
  25.                 if ( this.RssFeedReadCompleted != null )
  26.                     RssFeedReadCompleted ( this, new RssFeedReadCompletedEventArgs ( ) { Feeds = items } );
  27.             }
  28.         }

Snippet 2

No método ReadRssFeedAsync, recebemos a URI do feed como parâmetro (no caso do feed dos webcasts do MSDN, a URL é: http://www.msdnbrasil.com.br/Microsoft.NewRSS/RssItems.aspx?segment=Arquiteto%20de%20Solu%C3%A7%C3%B5es--and--Desenvolvedores&type=WebCasts%20Online&futureitems=S&auth=747dab31-42e3-69f3-7765-618e61722d9a).

Criamos um objeto do tipo WebClient e chamamos o método DownloadStringAsync, que irá baixar o XML do feed. É importante saber que no Silverlight, qualquer chamada a recursos remotos via HTTP é feita de forma assíncrona, por isso, antes de chamarmos o método DownloadStringAsync, temos que associar o evento DownloadStringCompleted a um método (nesse caso, client_DownloadStringCompleted), que será chamado quando o download terminar.

No método client_DownloadStringCompleted, checamos se houve algum erro no download (if e.Error == null). Se não houve erro, então criamos um XDocument com o conteúdo baixado (e.Result). Então, utilizamos uma Linq query para pegarmos todos os elementos com o nome “item” (doc.Descendants(“item”)) e pegarmos seus elementos filhos, que trazem as informações sobre o Webcast (esses elementos são: title, link, pubDate, …).

Na linha 20, temos que usar o método XName.Get, pois no XML do feed, o elemento “creator” faz parte do XML Namespace “http://purl.org/dc/elements/1.1/” (se voce olhar no conteúdo XML, voce verá que o elemento creator estará assim “dc:creator” e no início do XML, é adicionado o XML namespace dc que aponta para “http://purl.org/dc/elements/1.1/”).

Obs: os métodos “ConvertToDateTime”, “ReplaceHTMLChars” e “GetDurationFromDescriptionn” somente fazem algumas operações auxiliares como limpar caracteres inválidos e fazer algumas operações no texto para pegar a duração do Webcast. Voce pode ver o que essas funções fazem se voce baixar o código da aplicação, no início deste post.

Essa Linq query irá retornar um IEnumerable de FeedItemModel. Mas, como a operação é feita de maneira assíncrona, temos que informar a quem chamou a função ReadRssFeedAsync, que a operação terminou e os resultados foram obtidos. Para isso, criamos um evento que será chamado quando a operação completar (linha 26), passando a coleção de FeedItemModel.

Neste post vimos como ler o Feed RSS com os Webcasts do MSDN. Nos próximos posts, iremos ver como criar um item no calendário do Outlook com os dados do Webcast e juntar todas as funcionalidades na aplicação usando o Pattern MVVM (Model-View-ViewModel)


Permalink | Comentários (0) | Post RSSRSS comment feed

Novidades do Silverlight 4 – Parte I: Rodando Out-of-Browser com Elevated Permissions

Introdução

Essa é a primeira parte de uma série de tutoriais que irá cobrir alguns dos novos recursos do Silverligtht 4, como Elevated Permisions em aplicações Out-of-Browser, MVVM usando a interface ICommand e COM Interop, a única feature nova que, ainda, não é multi-plataforma, ou seja, somente funciona em Windows.

Neste tutorial, iremos criar uma aplicação que irá ler um feed RSS dos próximos Webcasts do MSDN Brasil, e que também deixará o usuário salvar a data do webcast no seu calendário no Outlook usando COM Interop, caso a aplicação esteja rodando fora do browser, com privilégios elevados.

Voce pode baixar o código deste tutorial no link abaixo:

http://cid-1498c467c14dc20b.skydrive.live.com/self.aspx/BrSilverlight/Tutoriais/MsdnWebcastsCalendar.zip

Elevated Permissions no Silverlight 4

No Silverlight 4, a pedido de vários clientes e membros da comunidade, o time de Silverlight da Microsoft introduziu um novo recurso, que permite aplicações Silverlight rodando fora do browser usufruam de alguns recursos a mais que eram restringidos pela Sandbox de segurança. Alguns desses recursos são:

  • Acesso a arquivos locais: com isso é possível acessar as pastas My* (MyDocuments, MyMusic, MyVideos, …) do usuário sem precisar utilizar as APIs OpenFileDialog e SaveFileDialog. Basta utilizar-se as classes do namespace System.IO.
  • Chamadas a recursos Cross-Domain: com isso é possível acessar recursos que se encontram fora do domínio da aplicação Silverlight sem a necessidade de haver arquivos de configuração cross-domain (clientacesspolicy.xml ou crossdomain.xml).
  • COM Interop: agora é possível acessar recursos disponíveis através de interfaces COM. Note que esta feature está disponível somente em ambientes Microsoft Windows, pois no MacOS não existe COM.
  • e vários outros

Para começarmos, crie uma nova aplicação Silverlight no Visual Studio e dê o nome MsdnWebcastsCalendar (ou o nome que voce preferir). Com a aplicação criada, clique com o botão direito no projeto e vá em “Propriedades”. Agora execute os seguintes passos:

  1. na guia “Silverlight”, marque a opção “Enable running application out of browser”
  2. clique no botão “Out-of-Browser Settings …”
  3. marque a check-box “Require elevated trust when running outside of the browser”.

image

Agora, precisamos instalar a aplicação em nossa máquina. Para isso, devemos executar a aplicação, clicar com o botão direito e clicar em “Intall MsdnWebcastsCalendarApplication onto this computer …”. Uma caixa de diálogo irá abrir, pedindo ao usuário que instale a aplicação em sua máquina. Note que há uma aviso de segurança, notificando o usuário de que a aplicação poderá acessar recursos locais da máquina. Isso é um recurso de segurança que irá previnir os usuários de instalarem aplicações de origens não confiáveis. Clique em “Install”.

image

Agora a aplicação está instalada na máquina do usuário. Como ainda estamos desenvolvendo a aplicação, iremos configurar o Visual Studio para debugar a aplicação fora do browser.

Vá de novo nas propriedades do projeto Silverlight, e desta vez, vá em “Debug”, e marque a terceira opção “Installed out-of-browser application”.

image

Agora temos nossa aplicação instalada e configurada com elevated trust. Nos próximos posts, irei explicar como iremos ler o feed RSS, aplicar o Pattern MVVM e utilizar a API do Outlook via COM para criarmos o item no calendário. Até lá.

Breno Ferreira


Permalink | Comentários (1) | Post RSSRSS comment feed

Usando o Silverlight WebBrowser Control

Introdução

No Silverlight 4, foi incluído um controle bem interessante chamado WebBrowser. Esse controle é usado para apresentar conteúdo HTML. Note que esse controle funciona somente em aplicações rodando fora do browser. Isso por que não faria muito sentido usar este controle em uma aplicação rodando dentro do Browser, já que é possível interagir com o navegador (para saber mais, leia sobre HtmlPage e HtmlDocument).

Voce pode baixar o código da aplicação desenvolvida para este tutorial no link abaixo:

http://cid-1498c467c14dc20b.skydrive.live.com/self.aspx/BrSilverlight/Tutoriais/SilverlightBrowser.zip

Configurando Out-of-Browser

Primeiramente, crie uma nova aplicação Silverlight no Visual Studio. Como o controle WebBrowser só funciona para aplicações Out-of-Browser, temos que configurar nossa aplicação para funcionar fora do navegador. Para fazer isso, clique com o botão direito no projeto Silverlight e vá em Propriedades.

image

Feito isso, na guia “Silverlight” marque a opção “Enable running application out of the browser”.

image 

Agora, temos que ter certeza que a aplicação irá rodar fora do browser. Para fazermos isso, temos que ter duas “telas” distintas. Uma para quando a aplicação está rodando no browser, que irá pedir para o usuário instala-la em sua máquina, e outra, que irá ter o controle WebBrowser, quando a aplicação estiver fora do browser.

Clique com o botão direito no projeto Silverlight, vá em “Add” > “New Item” e escolha “Silverlight Child Window”.

image

Dê o nome de InstalaçãoWindow e clique em “Add”. Essa janela irá pedir ao usuário que instale a aplicação em sua máquina.

Adicione o seguinte XAML ao arquivo InstalacaoWindow.xaml:

Code Snippet
  1. <controls:ChildWindow x:Class="SilverlightBrowser.InstalacaoWindow"
  2.            xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  3.            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  4.            xmlns:controls="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls"
  5.            Width="400" Height="110"
  6.            Title="InstalacaoWindow">
  7.     <Grid x:Name="LayoutRoot" Margin="2">
  8.         <Grid.RowDefinitions>
  9.             <RowDefinition />
  10.             <RowDefinition Height="Auto" />
  11.         </Grid.RowDefinitions>
  12.  
  13.         <TextBlock x:Name="notificacaoTxt" Text="Esta aplicação precisa rodar Out-of-Browser" HorizontalAlignment="Center" Margin="10" />
  14.         <Button x:Name="CancelButton" Content="Cancelar" Click="CancelButton_Click" Width="75" Height="23" HorizontalAlignment="Right" Margin="0,12,0,0" Grid.Row="1" />
  15.         <Button x:Name="OKButton" Content="Instalar" Click="OKButton_Click" Width="75" Height="23" HorizontalAlignment="Right" Margin="0,12,79,0" Grid.Row="1" />
  16.     </Grid>
  17. </controls:ChildWindow>

Snippet 1

Nesse XAML, criamos um um botão (OKButton) que irá verificar se a aplicação está instalada na máquina do usuário. Caso não esteja, irá instala-la.

No arquivo InstalacaoWindow.xaml.cs, adicione o seguinte código:

Code Snippet
  1. private void OKButton_Click ( object sender, RoutedEventArgs e )
  2. {
  3.     if ( Application.Current.InstallState == InstallState.NotInstalled )
  4.         Application.Current.Install ( );
  5.     else
  6.         MessageBox.Show ( "Aplicação já instalada" );
  7.     this.DialogResult = true;
  8. }

 

Snippet 2

Na linha 3, checamos o InstallState da aplicação, que nos diz se a aplicação está ou não instalada na máquina do usuário. Caso, não esteja (NotInstalled), chamamos o método Install(), que irá mostrar ao usuário o prompt de instalação.

image

Clique em OK. Agora o runtime do Silverlight irá instalar a aplicação na máquina do usuário, criando atalhos (caso o usuário marque as CheckBoxes) no Menu Iniciar e na Área de Trabalho.

Agora vamos configurar o Visual Studio para executar a aplicação instalada, e não a página que hospeda o controle Silverlight.

Vá nas propriedades do projeto Silverlight, na guia Debug, em “Start Action”, marque a opção “Instaled out-of-browser application”.

image

Em seguida, clique com o botão direito no projeto Silverlight, e marque a opção “Set as StartUp Project”.

image

WebBrowser Control

Iremos agora criar o controle que irá mostrar o WebBrowser. Adicione um novo UserControl a solução chamado BrowserControl.

No arquivo BrowserControl.xaml, adicione o seguinte XAML:

Code Snippet
  1. <UserControl x:Class="SilverlightBrowser.BrowserControl"
  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.  
  8.     <Grid x:Name="LayoutRoot" Background="White">
  9.         <Grid.RowDefinitions>
  10.             <RowDefinition Height="23" />
  11.             <RowDefinition Height="*" />
  12.         </Grid.RowDefinitions>
  13.         <Grid.ColumnDefinitions>
  14.             <ColumnDefinition Width="*" />
  15.             <ColumnDefinition Width="25" />
  16.         </Grid.ColumnDefinitions>
  17.         
  18.         <TextBox x:Name="urlTxt" Text="http://www.bing.com" />
  19.         <Button x:Name="navBtn" Content="Go" Grid.Column="1" Click="navBtn_Click" />
  20.         
  21.         <WebBrowser x:Name="browser" Grid.Row="1" Grid.ColumnSpan="2" Width="1000" Height="700" />
  22.     </Grid>
  23. </UserControl>

Snippet 3

Neste snippet, criamos uma TextBox que onde o usuário irá digitar a URL da página que ele deseja visitar, um botão que irá navegar para a página desejada.

Na linha 21, criamos o controle WebBrowser, demos a ele um nome (browser) e setamos sua largura, altura e sua posição no Grid.

No arquivo BrowserControl.xaml.cs, edite o EventHandler navBtn_Click, adicionando o seguinte código:

Code Snippet
  1. private void navBtn_Click ( object sender, RoutedEventArgs e )
  2. {           
  3.     String html = String.Empty;
  4.     if ( urlTxt.Text.StartsWith ( "http://" ) )
  5.         html = "<iframe src=\"" + this.urlTxt.Text + "\" style=\"width:100%;height:100%;\" />";
  6.     else
  7.         html = this.urlTxt.Text;
  8.  
  9.     this.browser.NavigateToString ( html );
  10. }

Snippet 4

O controle WebBrowser possui 2 métodos que podem ser usados para mostrar o conteúdo HTML.

  • Navigate(URI source): esse método recebe uma URI que irá apontar para o site que deseja-se mostrar no WebBrowser. Entretanto, há um porém. Devido a restrições de segurança, o WebBrowser só renderiza páginas no mesmo domínio da sua aplicação. Uma maneira de contornar essa situação é usando o método abaixo.
  • NavigateToString(String text): esse método recebe como parâmetro uma string com qualquer conteúdo HTML e o WebBrowser control irá renderizá-lo. Então podemos usar este método para mostrar páginas que sejam de outro domínio que não seja o nosso. Basta criamos um <iframe> e setarmos o atributo src para a página que desejamos abrir.

Na linha 4, verificamos se o usuário está tentando abrir uma página, verificando se o enderço começa com “http://”. Se sim, criamos o <iframe> e setamos o atributo src com o enderço digitado pelo usuário.

O usuário também pode digitar qualquer conteúdo HTML na TextBox que o WebBrowser irá interpretar e  rederizar na tela.

 

Aperte F5, e execute a aplicação.

Na imagem abaixo, vemos o WebBrowser mostrando a página do Bing.

image

E na imagem abaixo, vemos o WebBrowser mostrando um conteúdo HTML referente a um vídeo do YouTube.

image


Permalink | Comentários (0) | Post RSSRSS comment feed

Introdução ao Silverlight

O que é Silverlight

Silverlight é uma solução da Microsoft para a criação de Rich Internet Applications. Mas o que são exatamente essas RIAs? Bem, são aplicações web (que rodam no browser) que apresentam para o usuário conteúdo multimídia, mas principalmente, de forma interativa, rica e fácil de usar. São aplicações que unem o melhor dos mundos: Web (mobilidade) e Desktop (interface rica).
O Silverlight possui um framework bem abrangente para criação de RIAs, que incluem:

  • Vídeo (inclusive de alta definição)
  • Imagens
  • Animações
  • Gráficos vetoriais
  • APIs de rede (Web-Services, REST, Sockets, …)
  • e váriozs outros recursos

É importante saber que, para o Silverlight rodar na máquina cliente (ou seja, no browser do usuário), ele terá que instalar o plug-in do Silverlight, que é um download de aproximadamente 4MB. Esse plug-in é multi-browser e multi-plataforma. Funciona tanto no IE, Firefox (e na versão 4 já tem suporte oficial ao Chrome), e roda tanto em Windows quanto no Mac OS X.

O que é preciso para começar

Para começar a desenvolver suas aplicações em Silverlight, voce terá que baixar algumas coisas primeiro. São elas:

IDE: Voce provavelmente não vai querer escrever o seu programa no Notepad. Existem ferramentas que auxiliam (e muito!) no desenvolvimento de aplicações. E para Silverlight não é diferente. Eu recomendo o Visual Studio 2010 Beta 2. Voce pode baixá-lo aqui: http://www.microsoft.com/visualstudio/en-us/try/default.mspx#download

Obs: caso voce não queira, ou não possa, instalar produtos beta no seu computador, também há a opção de baixar a Trial do Visual Studio 2008 (no mesmo link acima) ou instalar o Visual Web Developer 2008 Express (gratuito) aqui: http://www.microsoft.com/express/download/

SDK: Voce também precisará baixar o SDK do Silverlight. Eu recomento baixar a Beta do Silverlight 4, que possui vários recursos novos (que irei abordar em posts futuros). Voce pode baixar o Silverlight 4 Beta Tools for Visual Studio 2010 aqui.

Obs: caso voce tenha optado pelo Visual Studio 2008, voce pode baixar o Silverlight 3 for Visual Studio 2008 SP1 aqui.

Expression Blend: apesar de não ser necessário, é altamente recomendado. Essa ferramenta irá ajudar muito na hora de criar as interfaces e animar os objetos nas suas aplicações. Se voce pretende usar o Visual Studio 2010, baixe o Expression Blend Preview for .NET 4 aqui. Se voce escolheu o Visual Studio 2008, baixe o Expression Blend 3 Trial aqui.

Começando

Quando voce terminar os downloads, abra o Visual Studio (qualquer seja a versão que voce tenha escolhido). Para esse tutorial, utilizarei o Visual Studio 2010 Beta 2 com o Silverlight 4.

Com o Visual Studio aberto, vá em File – New – Project.

image

Escolha a linguagem que voce deseja (eu tenho C# como minha preferência, mas voce também pode escolher Visual Basic), vá na guia Silverlight e escolha “Silverlight Application” e clique em OK.

image

O Visual Studio irá mostrar uma tela onde voce poderá escolher se deseja criar um projeto “ASP.NET Web Application” que irá hospedar a aplicação Silverlight. Se voce marcar a checkbox, ele irá criar um projeto ASP.NET com uma pasta chamada ClientBin que irá conter o arquivo .xap, que nada mais é do que um zip contendo os arquivos da aplicação Silverlight compilados. Se voce nao marcar, o Visual Studio irá criar automaticamente uma página HTML que irá referenciar esse .xap na página e mostrar a aplicação no Browser.

image

Indo ao Solution Explorer, voce verá a seguinte lista de arquivos:

image

XAML para Leigos

Arquivos .xaml (pronunciado zemel), em Silverlight (e também em Windows Presentation Foundation), são arquivos xml que declarativamente descrevem a interface da aplicação.

O arquivo App.xaml é geralmente utilizado para guardar recursos como estilos de controles, imagens e Brushes que são utilizados em toda a aplicação. O arquivo App.xaml.cs descreve funcionalidades que são utilizadas por toda a aplicação, eventos como Startup e Exit.

O arquivo MainPage.xaml é a tela principal da aplicação. Nesse arquivo, voce irá adicionar os controles que serão mostrados na tela, como textbox, botões, vídeos, imagens, etc.. Já o arquivo MainPage.xaml.cs é o code-behind, que irá tratar eventos como Click do botão e funcionalidades que irão controlar como a aplicação irá se comportar em tempo de execução.

Abra o arquivo MainPage.xaml e voce verá o seguinte conteúdo:

Code Snippet
  1. <UserControl x:Class="IntroducaoSilverlight.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="300" d:DesignWidth="400">
  8.  
  9.     <Grid x:Name="LayoutRoot" Background="White">
  10.  
  11.     </Grid>
  12. </UserControl>

Snippet 1

Toda aplicação Silverlight possui um ou mais UserControls, que definem a interface que é mostrada ao usuário. Nos arquivos .xaml desses UserControls voce encontrará:

XML Namespaces: são esses xmlns que voce ve nas linhas 2-5. Esses namespaces são utilizados pelo interpretador para saber onde encontrar o conteúdo referenciado no arquivo XAML. Por exemplo:

Na linha 2, declaramos o namespace que referencia os controles padrões do Silverlight, como Button, TextBox, Grid, etc.. Por isso, na linha 9, não usamos nenhum prefixo para referenciar o controle Grid. Se tivessemos usado algum prefixo no namespace, teriamos que utilizar esse prefixo também na hora de declarar o controle. Por exemplo:

Code Snippet
  1. xmlns:teste="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  2. xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  3. ...
  4. >
  5.  
  6.  <teste:Grid x:Name="LayoutRoot" Background="White">
  7.  
  8.  </teste:Grid>

Snippet 2

Já o namespace delcarado na linha 3 do snippet 1, referencia algumas funcionalidades específicas do XAML, como a capacidade de declarar nomes para controles, para que eles possam ser referenciados no code-behind. Por exemplo, o Grid na linha 9, tem o nome “LayoutRoot”.

Adicionando Controles à Interface

Irei apresentar um exemplo bem simples, que irá ilustrar algumas funcionalidades do Silverlight interessantes. Iremos fazer uma pequena aplicação, onde o usuário irá escolher um site que ele deseja visitar, e nossa aplicação irá mostrar o link para a página escolhida.

Abra o arquivo MainPage.xaml e adicione o seguinte conteúdo:

Code Snippet
  1. <Grid x:Name="LayoutRoot" Background="White">
  2.     <StackPanel>
  3.         <TextBlock Text="Escolha um site" FontWeight="Bold" Foreground="DarkBlue"/>
  4.         <ComboBox x:Name="siteCmbBox" Width="100" HorizontalAlignment="Left" SelectionChanged="siteCmbBox_SelectionChanged">
  5.                 <ComboBoxItem Tag="http://www.bing.com" Content="Bing" />
  6.                 <ComboBoxItem Tag="http://silverlight.net" Content="Silverlight" />
  7.                 <ComboBoxItem Tag="http://www.msdn.com.br" Content="MSDN" />
  8.                 <ComboBoxItem Tag="http://www.microsoft.com" Content="Microsoft" />
  9.         </ComboBox>
  10.         <HyperlinkButton x:Name="linkBtn" TargetName="_blank" Opacity="0" />
  11.     </StackPanel>
  12. </Grid>

Snippet 3

O que exatamente isso faz? Bem, explicando passo-a-passo:

Na primeira linha, temos um Grid com o nome “LayoutRoot” e com Branco de cor de fundo. O Grid é um Panel usado para mostrar conteúdo de forma tabular, com linhas e colunas. No nosso caso, só temos uma linha e uma coluna, que ocupam a tela inteira. Esses Panels são utilizados para mostrar, agrupar e organizar controles dentro de uma aplicação Silverlight. No nosso caso, o Grid possui um filho, um StackPanel.

O StackPanel é um outro tipo de Panel, mas esse, em vez de mostrar o conteúdo em linhas e colunas, funciona como uma pilha (daí o nome StackPanel). Ele vai “empilhando” os controles filhos um embaixo do outro. Também é possível mostrar os controles um do lado do outro, alterando-se a propriedade Orientation para Horizontal. O padrão é Vertical.

Dentro do StackPanel, temos 3 controles:

TextBlock: é um controle bem simples, que mostra apenas texto. É possível alterar sua formatação. Nesse exemplo, temos o texto em negrito (Bold), com a cor azul escuro (DarkBlue).

ComboBox: ComboBox tradicional. Mostra várias informações para o usuário escolher uma. No nosso exemplo, alteramos seu nome para siteCmbBox, sua largura para 100 pixels e seu alinhamento horizontal para “Left”, assim ele ficará alinhado no canto esquerdo em relação ao seu pai (StackPanel). Adicionamos também um EventHandler (siteCmbBox_SelectionChanged) que irá tratar o evento SelectionChanged (quando o usuário seleciona um item diferente na ComboBox).

     ComboBoxItem: temos várias ComboBoxItems, que são mostradas quando o usuário clica na ComboBox. Setamos seu conteúdo com o nome do site (Bing, MSDN, …) e temos a propriedade Tag guardando a URL do site.

HyperLinkButton: simplesmente um HyperLink, que irá abrir uma nova janela do browser quando o usuário selecionar o site que ele deseja visitar e clicar no link. Note que sua propriedade Opacity está com o valor 0 (zero), ou seja ele está invisível. Iremos adicionar uma animação que irá gradualmente aumentar esse valor para 1 (visível).

Adicionando funcionalidades

No arquivo MainPage.xaml, clique com o botão direito no nome do EventHandler (no nosso exemplo, siteCmbBox_SelectionChanged), e clique em “Navigate To Event Handler”:

image

No arquivo MainPage.xaml.cs, adicione o seguinte código:

Code Snippet
  1. private void siteCmbBox_SelectionChanged ( object sender, SelectionChangedEventArgs e )
  2. {
  3.     String siteURL = ( ( ComboBoxItem ) this.siteCmbBox.SelectedItem ).Tag.ToString ( );
  4.     String site = ( ( ComboBoxItem ) this.siteCmbBox.SelectedItem ).Content.ToString ( );
  5.     this.linkBtn.NavigateUri = new Uri ( siteURL );
  6.     this.linkBtn.Content = "Clique aqui para acessar: " + site;
  7. }

Snnipet 4

Nesse snippet, a variável siteURL irá pegar o valor da propriedade Tag (nós guardamos a URL do site na propriedade Tag das ComboBoxItems lembra?) e aí setamos a propriedade NavigateUri do HyperLinkButton com o valor da URL. Também pegamos o valor da propriedade Content (que armazena o nome do site), e setamos o valor da propriedade Content do HyperLinkButton com o valor do nome do site.

Ok, mas se voce rodar a aplicação agora, voce verá que o link não aparece. Isso por que nós setamos a Opacity do HyperLinkButton para 0. Vamos então, criar uma animação bem simples, que irá gradualmente aumentar a Opacity de 0, para 1, dando um efeito bem bacana.

De volta no arquivo MainPage.xaml, adicione o seguinte trecho de XAML logo antes da declaração do Grid:

Code Snippet
  1. <UserControl.Resources>
  2.     <Storyboard x:Name="OpacityAnimation" >
  3.         <DoubleAnimation Storyboard.TargetName="linkBtn" Storyboard.TargetProperty="Opacity"
  4.                          Duration="0:0:0.3" From="0" To="1" />
  5.     </Storyboard>
  6. </UserControl.Resources>

Snippet 5

Aqui nos criamos uma StoryBoard, que é uma espécie de repositório de animações. A Storyboard irá guardar várias animações, essas então, irão alterar as propriedades de algum elemento da interface. Nesse exemplo temos uma Storyboard com o nome OpacityAnimation.

Nessa Storyboard, possuimos apenas uma animação. Essa animação é do tipo DoubleAnimation, isso quer dizer que ela irá animar o valor de uma propriedade do tipo Double entre 2 valores (iniciais e finais) usando interpolação linear sobre um determinado tempo (http://msdn.microsoft.com/en-us/library/system.windows.media.animation.doubleanimation(VS.95).aspx). Resumindo, ela irá alterar os valores da propriedade (no nosso caso Opacity), calculando os valores medianos entre os valores iniciais e finais (0 e 1) baseado no tempo especificado (no nosso caso, 0.3 segundos).

Tudo que nós temos que fazer agora, é iniciar essa animação quando o usuário escolhe um valor na ComboBox. Volte ao MainPage.xaml.cs e altere-o conforme o snippet abaixo:

Code Snippet
  1. private void siteCmbBox_SelectionChanged ( object sender, SelectionChangedEventArgs e )
  2. {
  3.     String siteURL = ( ( ComboBoxItem ) this.siteCmbBox.SelectedItem ).Tag.ToString ( );
  4.     String site = ( ( ComboBoxItem ) this.siteCmbBox.SelectedItem ).Content.ToString ( );
  5.     this.linkBtn.NavigateUri = new Uri ( siteURL );
  6.     this.linkBtn.Content = "Clique aqui para acessar: " + site;
  7.  
  8.     this.OpacityAnimation.Begin ( );
  9. }

Snippet 6

Nesse snippet, chamamos o método Begin() da Storyboard, que inicia a animação.

Considerações finais

Bem pessoal, é isso. Espero que esse tutorial tenha ajudado os iniciantes, que talvez estivessem perdidos e não sabiam por onde começar seus estudos em Silverlight. Espero ter sido claro o suficiente e não ter deixado dúvidas. Mas, caso este não seja o caso, não exite em entrar em contato comigo ou deixar um comentário neste post. Tenha certeza que é com seu feedback é muito importante para garantir a qualidade do conteúdo deste site.

Caso queira baixar o código da aplicação escrita para este tutorial, acesse o link abaixo:

http://cid-1498c467c14dc20b.skydrive.live.com/self.aspx/BrSilverlight/Tutoriais/IntroducaoSilverlight.zip

Breno Ferreira


Categories: Silverlight
Permalink | Comentários (4) | Post RSSRSS comment feed