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:

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
- public class FeedItemModel
- {
- public String Title { get; set; }
- public Uri Link { get; set; }
- public DateTime Date { get; set; }
- public Int32 Duration { get; set; }
- public String Creator { get; set; }
- public String Description { get; set; }
- }
Snippet 1
ViewModel
Code Snippet
- public class FeedViewModel : INotifyPropertyChanged
- {
- public FeedViewModel ( )
- {
- this.ReadRssFeeds ( );
- }
-
- public ObservableCollection<FeedItemModel> Feeds { get; private set; }
- public ICommand OutlookApplicationCommand { get { return new OutlookCommand ( ); } }
-
- private void ReadRssFeeds ( )
- {
- RssReader reader = new RssReader ( );
- reader.RssFeedReadCompleted += new EventHandler<RssFeedReadCompletedEventArgs> ( reader_RssFeedReadCompleted );
- 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" ) );
- }
-
- void reader_RssFeedReadCompleted ( object sender, RssFeedReadCompletedEventArgs e )
- {
- if ( this.Feeds == null )
- this.Feeds = new ObservableCollection<FeedItemModel> ( );
-
- Array.ForEach ( e.Feeds.ToArray ( ), ( feed ) => this.Feeds.Add ( feed ) );
- OnPropertyChanged ( "Feeds" );
- }
-
- #region INotifyPropertyChanged Members
-
- public event PropertyChangedEventHandler PropertyChanged;
-
- private void OnPropertyChanged ( string propertyName )
- {
- if ( PropertyChanged != null )
- {
- PropertyChanged ( this, new PropertyChangedEventArgs ( propertyName ) );
- }
- }
-
- #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
- public class OutlookCommand : ICommand
- {
- #region ICommand Members
-
- public event EventHandler CanExecuteChanged;
-
- public bool CanExecute ( object parameter )
- {
- return OutlookApplication.IsAvailable;
- }
-
- public void Execute ( object parameter )
- {
- FeedItemModel model;
- if(parameter is FeedItemModel)
- model = parameter as FeedItemModel;
- else
- throw new ArgumentException("Invalid argument", "parameter");
-
- try
- {
- OutlookApplication.CreateCalendarItem ( model );
- MessageBox.Show("Webcast added to your Outlook Calendar");
- }
- catch ( Exception )
- {
- MessageBox.Show ( "An error ocurred when trying to open Outlook resources.\nMake sure you have Microsoft Outlook for Windows installed and properly configured." );
- }
- }
-
- #endregion
- }
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
- <UserControl x:Class="MsdnWebcastsCalendar.MainPage"
- xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
- xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
- xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
- xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
- xmlns:vm="clr-namespace:MsdnWebcastsCalendar.ViewModel"
- mc:Ignorable="d"
- Width="800" Height="600">
-
- <UserControl.Resources>
- <vm:FeedViewModel x:Key="FeedViewModelDataContext" />
- <Style x:Key="ListBoxItemTemplate" TargetType="ListBox">
- <Setter Property="ItemTemplate">
- <Setter.Value>
- <DataTemplate>
- <StackPanel Width="{Binding ElementName=LayoutRoot, Path=ActualWidth}">
- <StackPanel.Background>
- <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
- <GradientStop Color="#FF36295E" Offset="0"/>
- <GradientStop Color="#FFA2DCF3" Offset="1"/>
- </LinearGradientBrush>
- </StackPanel.Background>
- <StackPanel Orientation="Horizontal">
- <Button Command="{Binding ElementName=LayoutRoot, Path=DataContext.OutlookApplicationCommand}"
- CommandParameter="{Binding}"
- ToolTipService.ToolTip="Adicionar webcast ao calendário do Outlook">
- <Image Source="/MsdnWebcastsCalendar;component/Microsoft_Outlook.png" />
- </Button>
- <HyperlinkButton Margin="0, 11, 0, 0" Content="{Binding Path=Title}" NavigateUri="{Binding Path=Link}" />
- </StackPanel>
- <TextBox Text="{Binding Path=Description}" IsReadOnly="True" TextWrapping="Wrap" />
- <StackPanel Orientation="Horizontal">
- <TextBlock Text="Data: " FontStyle="Italic" />
- <TextBlock Text="{Binding Path=Date}" FontStyle="Italic" />
- <TextBlock Text="-" />
- <TextBlock Text="{Binding Path=Creator}" FontWeight="Bold" />
- </StackPanel>
- </StackPanel>
- </DataTemplate>
- </Setter.Value>
- </Setter>
- </Style>
- </UserControl.Resources>
-
- <Grid x:Name="LayoutRoot" Background="White" DataContext="{Binding Source={StaticResource FeedViewModelDataContext}}">
- <ListBox x:Name="feedsListBox" Style="{StaticResource ListBoxItemTemplate}" ItemsSource="{Binding Path=Feeds}" />
- </Grid>
- </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):

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.


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