adotando silverlight 2.0 - v1 - exercicios

163
1 Exercícios Adotando o Silverlight 2.0 [IL400]

Upload: rodrigo-pecanha

Post on 11-Jun-2015

1.784 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Adotando Silverlight 2.0 - v1 - Exercicios

1

Exercícios Adotando o Silverlight 2.0

[IL400]

Page 2: Adotando Silverlight 2.0 - v1 - Exercicios

2

Índice

A- Princípios Básicos do Silverlight 2 .................................................................................... 7

Seção 1: Criando uma Aplicação Simples com Silverlight ................................................... 8

Controles Adicionais ..............................................................................................................13

Crédito Extra .........................................................................................................................14

Seção 2: Layout, Controles e Conteúdo de Vetor.................................................................15

Canvas .................................................................................................................................15

StackPanel ...........................................................................................................................20

Grid .......................................................................................................................................22

Seção 3: Elementos e Objetos XAML ....................................................................................29

Seção 4: Manipulação de Entrada e Eventos ........................................................................32

Manipulação Básica de Eventos de Mouse e Teclado .........................................................32

Roteamento de Eventos e Fontes de Eventos .....................................................................34

Seção 5: Modo de Tela Inteira ................................................................................................38

B- Animação no Silverlight .....................................................................................................40

Seção 1: Uma olhada na API de Animação ..............................................................................41

Storyboards ...........................................................................................................................41

Animações ............................................................................................................................42

Animações de Quadro Chave ................................................................................................42

Animações de Objeto ............................................................................................................44

Seção 2: Usando o Blend para Projetar Animações .............................................................46

Ferramenta Visual que tem Suporte para a API ....................................................................46

Criando uma animação ........................................................................................................46

Inspecionando propriedades animadas ................................................................................47

Editando o storyboard ..........................................................................................................47

Editando KeySplines ............................................................................................................47

Resumo ...............................................................................................................................47

Seção 3: Reutilizando Storyboards .......................................................................................48

Geração de Storyboard de Procedimento ..............................................................................48

Reutilizando o Storyboard gerado .........................................................................................48

Seção 4: Usando o DispatchTimer para Animação de Procedimento .................................50

Page 3: Adotando Silverlight 2.0 - v1 - Exercicios

3

C- Integração de Navegador do Silverlight ...........................................................................51

Introdução ............................................................................................................................51

Seção 1 – Expondo Funções .NET ao Navegador. ...............................................................52

Entendendo o XAML e o Código .NET do Silverlight .............................................................52

Expondo um método .NET ao JavaScript ..............................................................................54

Chamando o Método .NET a partir do JavaScript. .................................................................55

Seção 2 – Manipulando a Árvore de Renderização XAML a partir do Navegador ..............57

Atualizando elementos XAML existentes ...............................................................................57

Criando novos Elementos XAML ...........................................................................................58

Mais Estudo...........................................................................................................................60

Seção 3 – Chamando o Script do Navegador a partir de .NET ............................................61

Mais Estudo...........................................................................................................................63

D- Personalizando a Aparência ..............................................................................................64

Introdução ............................................................................................................................64

Objetivos do Laboratório: .....................................................................................................64

Exercício 1: Criando o controle deslizante do Voyager ........................................................65

Tarefa 1: Montando seu controle deslizante .....................................................................66

Tarefa 2: Personalizando o controle deslizante ..................................................................69

Tarefa 3: Personalizando o Elevador .................................................................................71

Tarefa 4: Testando nosso controle deslizante. ...................................................................74

Tarefa 5: Exercícios para o usuário. ...................................................................................74

Apêndices .................................................................................................................................75

Exercício 1: Respostas das Tarefas ......................................................................................75

Tarefa 2: Personalizando o controle deslizante. .................................................................75

Tarefa 3: Personalizando o Elevador .................................................................................77

E- Layout Personalizado no Silverlight .................................................................................82

Introdução ............................................................................................................................82

Objetivos do Laboratório: .....................................................................................................83

Seção 1: Criando um WrapPanel Personalizado ..................................................................84

Tarefa 1: Crie uma classe WrapPanel ..............................................................................85

Tarefa 2: Meça seus Filhos para encontrar seu Tamanho Desejado. ...............................86

Tarefa 3: Organize nossos filhos .........................................................................................87

Tarefa 4: Exercitando nosso Painel .....................................................................................88

Page 4: Adotando Silverlight 2.0 - v1 - Exercicios

4

Seção 2 – Implementando um TimelineStackPanel para a aplicação do Voyager .............89

Tarefa 1: Crie uma classe TimelineStackPanel ..................................................................91

Tarefa 2: Implementando Measure (Medida) em nosso TimelineStackPanel .....................91

Tarefa 3 Adicionando Propriedades de Dependência Anexadas ao TimelineStackPanel ..92

Tarefa 4: Notificando o Panel quando um Begin ou End for alterado .................................93

Tarefa 5: Adicionando Propriedades de Dependência Low e High ao nosso Painel. ..........95

Tarefa 6: Notificando o Panel que as propriedades Low/High mudaram ............................97

Tarefa 7: Implementando o Arrange (Organizar) ................................................................98

Tarefa 8: Resolvendo um problema de Conversor ........................................................... 100

Tarefa 9: Testando nosso TimelineStackPanel a partir do código .................................... 101

Tarefa 10: Testando nosso TimelineStackPanel a partir do XAML ................................... 102

Tarefa 11: Exercícios para o usuário ................................................................................ 102

Apêndices ............................................................................................................................... 103

Seção 1: Respostas das Tarefas ......................................................................................... 103

Tarefa 1: Crie uma classe WrapPanel .............................................................................. 103

Tarefa 2: Medindo nossos filhos ...................................................................................... 103

Tarefa 3: Passo 1 – Substituindo o Arrange ..................................................................... 103

Tarefa 3: Passo 2 -- Implementando o Arrange ............................................................... 104

Tarefa 4: Exercitando nosso WrapPanel. ......................................................................... 104

Exercício 2: Respostas das Tarefas .................................................................................... 105

Tarefa 1: Criando um TimelineStackPanel ....................................................................... 105

Tarefa 3: Adicionando propriedades de dependência anexadas para Begin e End .......... 105

Tarefa 4: Notificando o Panel quando um Begin ou End for alterado ............................... 106

Tarefa 5: Adicionando Propriedades de Dependência Low e High ao nosso Painel. ........ 107

Tarefa 6: Notificando o Panel que as propriedades Low/High mudaram .......................... 107

Tarefa 7: Implementando o Arrange ................................................................................. 108

Tarefa 8: Resolvendo um problema de Conversor ........................................................... 109

Tarefa 9: Testando nosso TimelineStackPanel a partir do código .................................... 110

Tarefa 10: Testando nosso TimelineStackPanel a partir do XAML ................................... 110

Tarefa 11: Exercícios para o usuário ................................................................................ 110

F- Usando Dados nas Aplicações do Silverlight ................................................................ 111

Introdução ............................................................................................................................... 111

Objetivos do Laboratório: ........................................................................................................ 111

Page 5: Adotando Silverlight 2.0 - v1 - Exercicios

5

Tarefa 1: Criando uma Ligação Simples usando XAML e DataContext ............................ 113

Tarefa 2: Ligando a uma Coleção (usando um DataTemplate) ........................................ 114

Tarefa 3: Substituindo a Listbox por um DataGrid ............................................................ 116

Tarefa 4: Personalizando as colunas no DataGrid ........................................................... 117

Tarefa 5 – Exibição Mestre/Detalhes usando Ligações a partir do código........................ 118

Tarefa 6: Exercício para o usuário ................................................................................... 120

Apêndice ................................................................................................................................. 121

Seção 1: Respostas das Tarefas ......................................................................................... 121

Tarefa 1: Criando uma Ligação Simples usando XAML e DataContext ............................ 121

Tarefa 2: Ligando a uma Coleção (usando um DataTemplate) ........................................ 121

Tarefa 3: Substituindo a Listbox por um DataGrid ............................................................ 124

Tarefa 4: Personalizando as colunas no DataGrid ........................................................... 125

Tarefa 5: Implementando InotifyPropertyChanged (Passo 17) ......................................... 126

Tarefa 6 - Implementando INotifyPropertyChanged (Passo 9). ....................................... 127

G- Construindo um Media Player Simples .......................................................................... 128

Seção 1: Criando uma Página de Mídia no Silverlight ....................................................... 129

Seção 2: Adicionando controles de Reprodução à sua Mídia ........................................... 131

Seção 3: Usando o Progresso de Download e o Progresso de Buffer .............................. 133

Seção 4: Gerenciando Marcadores de Mídia ....................................................................... 134

H- Silverlight e Aplicações Conectadas .............................................................................. 136

Introdução ............................................................................................................................... 136

Seção 1: Usando o WebClient para Ler Dados Remotos de Ligação para o Silverlight .. 137

Tarefa 1: Crie uma aplicação do Silverlight e adicione a Classe CityData ........................... 137

Tarefa 2: Crie o Serviço XML .............................................................................................. 138

Tarefa 3: Crie o XAML ao qual fará ligações ....................................................................... 140

Tarefa 4: Crie a solicitação e o retorno de chamada do WebClient ..................................... 141

Tarefa 5: Ligando os Dados no Retorno de Chamada ......................................................... 143

Mais Estudo......................................................................................................................... 144

Seção 2: Usando WebRequest / WebResponse para obter Dados ................................... 145

Tarefa 1: Crie o Projeto e instale a aplicação de servidor .................................................... 146

Tarefa 2: Crie a interface de usuário do Silverlight .............................................................. 148

Tarefa 3: Escreva a lógica da interface de usuário e da Ligação de Dados ......................... 149

Seção 3: Construindo e Ligando a um Serviço WCF ......................................................... 152

Page 6: Adotando Silverlight 2.0 - v1 - Exercicios

6

Tarefa 1: Crie o Projeto do Silverlight e adicione o Serviço WCF ........................................ 153

Tarefa 2: Crie o Cliente do Silverlight e Ligue-o ao Serviço ................................................. 154

Apêndices ............................................................................................................................... 156

Seção 1: Respostas das Tarefas ......................................................................................... 156

Tarefa 1: Classe CityData ................................................................................................ 156

Tarefa 2: Manipulador Genérico ....................................................................................... 156

Tarefa 3: XAML Ligado .................................................................................................... 157

Tarefa 4: Criando a chamada para o Serviço POX .......................................................... 157

Tarefa 5: Ligando os Dados no Retorno de Chamada ..................................................... 158

Seção 2: .............................................................................................................................. 158

Tarefa 1: Função GetCities .............................................................................................. 158

Tarefa 2: XAML da interface de usuário ........................................................................... 159

Seção 3 ............................................................................................................................... 161

Tarefa 1: Classe de Dados de Cidades ............................................................................ 161

Page 7: Adotando Silverlight 2.0 - v1 - Exercicios

7

A- Princípios Básicos do Silverlight 2

Conceitos básicos do desenvolvimento do Silverlight 2

Este laboratório explora as ferramentas e os recursos fundamentais que servem de base para qualquer

aplicação do Silverlight.

O laboratório não foi projetado para ser feito do início ao fim. Ele foi estruturado como uma série de

exercícios práticos ‘passo a passo’, ilustrando determinadas técnicas. Você é estimulado a explorar e

experimentar. Pense em um exemplo do que gostaria de construir, e use as instruções deste laboratório

como degraus para alcançar esse objetivo.

O conteúdo deste laboratório é baseado na versão Beta 2 do Silverlight 2. Essa é uma versão preliminar

que não representa o conjunto final de recursos ou a funcionalidade final.

Conteúdo:

1. Criando uma Aplicação Simples com Silverlight

2. Layout, Controles e Conteúdo de Vetor

3. Elementos e Objetos XAML

4. Manipulação de Entrada e Eventos

5. Modo de Tela Inteira

Page 8: Adotando Silverlight 2.0 - v1 - Exercicios

8

Seção 1: Criando uma Aplicação Simples

com Silverlight

Esta seção ilustra as técnicas básicas de programação necessárias em qualquer aplicação Silverlight:

construção e depuração, trabalho com objetos de interface de usuário e manipulação de entrada.

Com as ferramentas do Silverlight para Visual Studio instaladas, o Visual Studio 2008 permite que você

construa e depure projetos do Silverlight. Os passos a seguir demonstrarão esse processo e ilustrarão

alguns dos recursos dos projetos do Silverlight 2 que são diferentes de outros projetos no Visual Studio.

1. Execute o Visual Studio 2008. 2. No menu File, selecione New Project … ou pressione Ctrl+Shift+N. 3. Na árvore ‘Project types’ à esquerda, selecione Visual C# Silverlight. Depois, na lista

‘Templates’ à direita, selecione ‘Silverlight Application’.

4. Para depurar e testar um projeto do Silverlight, você vai precisar de um projeto de site na solução. Isso será separado do projeto do Silverlight propriamente dito – os projetos do Silverlight apenas constroem um pacote binário do Silverlight conhecido como um arquivo XAP – vamos falar brevemente sobre isso. Já que isso significa que você normalmente trabalhará com pelo menos dois projetos, não se esqueça de marcar a caixa de verificação ‘Create directory for solution’. Dê um nome adequado ao projeto (para este laboratório, vamos usar FirstSilverlightProject) e clique no botão ‘OK’.

Page 9: Adotando Silverlight 2.0 - v1 - Exercicios

9

5. A seguinte caixa de diálogo aparecerá:

Um projeto do Silverlight não pode ser executado isoladamente – ele deve sempre ser

executado no contexto de uma página da Web pai, que é onde o controle do Silverlight

propriamente dito é incorporado. O assistente permite que você escolha como isso acontecerá.

(Observação: você também pode estabelecer uma associação entre um projeto do Silverlight e

um projeto da web no Solution Explorer do Visual Studio. Se você clicar com o botão direito em

qualquer projeto da web que faça parte de uma solução que contém um projeto do Silverlight,

verá uma opção ‘Add Silverlight Link...’ no menu de contexto. Portanto você sempre tem a

opção de reorganizar a estrutura de seu projeto se mudar de idéia com relação as escolhas que

fez nesse assistente).

A segunda opção, ‘Dynamically generate an HTML test page to host Silverlight within this

project’, é a mais simples – ela apenas cria um único arquivo HTML (oculto) para você que será

usado para incorporar o plug-in do Silverlight. Contudo, isso é insatisfatório por duas razões.

Primeiro, não há acesso ao ambiente de design completo que o Visual Studio pode oferecer para

um projeto da web (particularmente útil se o conteúdo do Silverlight não for a totalidade da

página mas meramente um elemento). E em segundo lugar, isso significa que quando você

executa a aplicação, ela será executada a partir de um arquivo HTML local no disco, em vez de

via HTTP. Um arquivo HTML local pode não fornecer um ambiente realista no qual você executa

seu código, devido ao contexto de segurança em que o navegador será executado.

O terceiro botão de opção do assistente fica desativado porque você acabou de criar uma nova

solução. Se você fosse adicionar um novo Projeto do Silverlight a uma solução existente que já

continha um site, isso permitiria que você usasse esse site para executar o conteúdo do

Silverlight.

Nesse caso, a primeira opção: ‘Add a new Web to the solution for hosting the control’ é a mais

apropriada. Você pode então escolher o tipo de projeto da web – um Projeto de Site ou um

Page 10: Adotando Silverlight 2.0 - v1 - Exercicios

10

Projeto de Aplicação da Web. Essa escolha não faz diferença para o Silverlight – ele está lá

porque o ASP.NET tem suporte para dois estilos diferentes de projeto da web. Para este

laboratório, escolhemos 'Web Site’.

Por fim, note que a caixa de verificação ‘Copy to configuration specific folders’ determina se o

diretório de saída é aninhado para dar suporte a múltiplas configurações de compilação (build).

Se você quiser construir tanto a versão de ‘lançamento’ quanto a de ‘depuração’ de seu arquivo

XAP, por exemplo, é melhor marcar essa caixa.

Clique em OK. O Visual Studio vai criar os dois projetos – seu projeto do Silverlight e um site

para hospedá-lo.

6. Dos dois arquivos que o Visual Studio abre após criar o projeto, um deles não é de uso imediato para você neste laboratório. Ele abrirá a Default.aspx, a página principal de sua aplicação da web. No entanto, essa página não tem seu conteúdo do Silverlight. Em vez disso, o assistente adicionou uma segunda página, que será chamada de ProjectNameTestPage.aspx, e a tornou a página padrão para depuração. (FirstSilverlightProjectTestPage.aspx, neste caso). Feche ou exclua a Default.aspx e abra a FirstSilverlightProjectTestPage.aspx.

7. Note que perto do topo desse arquivo aspx, uma montagem (assembly) é trazida ao escopo contendo as extensões do ASP.NET que têm suporte para o Silverlight:

<%@ Register Assembly="System.Web.Silverlight"

Namespace="System.Web.UI.SilverlightControls"

TagPrefix="asp" %>

Essas extensões incluem o controle <asp:Silverlight>, que é como esse projeto carrega seu

conteúdo do Silverlight, como você pode ver mais abaixo na página. (Se você estiver

familiarizado com a técnica do Silverlight 1.0 de usar o script Silverlight.js, e chamar uma função

JavaScript para carregar o controle do Silverlight, esse método ainda funciona, mas não é o

modo como o assistente do Silverlight escolhe fazer as coisas).

8. Encontre o controle do Silverlight. Note que sua propriedade Fonte se refere a “~/ClientBin/FirstSilverlightProject.xap". O arquivo .xap a que isso se refere é a saída de compilação (build) do projeto do Silverlight.

9. Construa o projeto a fim de criar esse arquivo .xap. No Windows Explorer, vá e encontre o arquivo, que você encontrará na pasta FirstSilverlightProject_Web\ClientBin. Note que o diretório ClientBin fica oculto por padrão; na janela de ferramentas do Solution Explorer (Gerenciador de Soluções), você pode visualizá-lo clicando no botão Show All Files na barra de ferramentas:

Page 11: Adotando Silverlight 2.0 - v1 - Exercicios

11

10. Esse arquivo .xap é na verdade apenas um arquivo ZIP. Faça uma cópia desse arquivo e depois renomeie a extensão da cópia de .xap para .zip. Dê um clique duplo no arquivo ZIP para abri-lo, e você verá que ele contém apenas dois arquivos: um DLL que contém todo o código C# compilado de seu projeto do Silverlight e um arquivo AppManifest.xaml. Esse manifesto lista os conteúdos do ZIP e indica qual DLL contém o ponto de entrada. O DLL também contém a linguagem XAML (eXtensible Application Markup Language) que descreve a interface de usuário de sua aplicação – todos os arquivos XAML são compilados dentro do DLL como recursos incorporados.

11. Tente adicionar um bitmap ao seu projeto do Silverlight. Basta usar o item do menu de contexto ‘Add Existing Item’ no Solution Explorer. (Lembre-se de adicionar isso ao seu projeto do Silverlight, e não o projeto da web associado). Uma vez que tiver adicionado isso, o Visual Studio vai definir a Ação de Compilação (Build) como Conteúdo. Em um projeto do Silverlight, isso significa que o item será adicionado ao arquivo .xap – tente reconstruir o projeto e depois repita o processo de extrair os conteúdos do arquivo .xap, e você verá o bitmap que acabou de adicionar al lado do DLL e manifesto.

12. Para testar seu projeto, você vai precisar de algum conteúdo no Page.xaml, caso contrário não haverá nada para ver. Nós começaremos com algo trivial. Abra o Page.xaml e dentro de Grid, adicione o seguinte:

<Button x:Name="BigButton" Content="Click Me!" FontSize="36" />

Essa marcação cria um botão chamado BigButton que contém o texto “Click Me!” (Clique em

mim!) na fonte de sistema padrão, com um tamanho de 36pt.

13. Agora vamos adicionar um pouco de interatividade à aplicação criando um manipulador de eventos de clique para o botão. Um pouco antes da marca de fechamento, digite ‘Click=’ (sem aspas). O recurso ItelliSense do Visual Studio vai mostrar a opção de criar um novo manipulador:

Clique na dica de ferramenta, o código deve ser assim agora:

<Button x:Name="BigButton" Content="Click Me!" FontSize="36"

Page 12: Adotando Silverlight 2.0 - v1 - Exercicios

12

Click="BigButton_Click" />

14. Em uma aplicação do Silverlight 2, as páginas XAML normalmente têm um arquivo code-behind que define o comportamento da interface de usuário. Por convenção, o arquivo code-behind é nomeado com um “.cs” (ou “.vb” para um projeto do Visual Basic) no final do nome do arquivo XAML. Então abra o Page.xaml.cs para ver o code-behind de seu arquivo XAML. Note que o Visual Studio criou automaticamente um manipulador de eventos para o evento de clique em botão, desta forma: private void BigButton_Click(object sender, RoutedEventArgs e)

{

}

15. No menu, escolha Build / Build Solution para garantir que a conclusão do código do Visual Studio esteja em sincronia com o novo XAML que você adicionou.

16. Dentro do manipulador de eventos de clique, adicione as duas linhas de código a seguir:

BigButton.Background = new SolidColorBrush(Colors.Red);

BigButton.Content = "You clicked me!";

17. Ponha um ponto de interrupção na primeira linha do código especificado no passo anterior (o que você pode fazer colocando o cursor nessa linha e pressionando F9). Comece a depurar o projeto pressionando F5. (Isso também vai construir o projeto para você). Na primeira vez em que depurar um projeto do Silverlight no estilo ‘site’ do projeto da web, você verá a seguinte mensagem:

Certifique-se de que o primeiro botão de opção está selecionado e clique em OK.

18. Uma janela do navegador da web vai aparecer com o conteúdo do Silverlight. Clique no botão. Logo depois, o Visual Studio deve mostrar que você acessou o ponto de interrupção, verificando que você é capaz de depurar o código C# que está sendo executado no navegador da web.

Page 13: Adotando Silverlight 2.0 - v1 - Exercicios

13

19. Pressione F5 para permitir que a aplicação continue. O navegador da web deve estar mostrando agora o texto atualizado dentro de um botão vermelho.

Controles Adicionais

Vale a pena destacar que na versão Beta 2, alguns controles mais complexos como DataGrid,

Calendar, TabControl e GridSplitter não estão incluídos no tempo de execução básico, mas estão

incluídos em montagens separadas (o DataGrid está em System.Windows.Controls.Data.dll e os

outros estão em System.Windows.Controls.Extended.dll). Isso significa que você não os verá por

padrão quando usar o IntelliSense. Veja como pode adicioná-los:

Primeiro, você precisará adicionar uma referência a uma dessas montagens ou a ambas. No

projeto do Silverlight, clique com o botão direito no nó de Referências e escolha “Add

Reference...”. Dentro da guia do .NET, role para baixo até encontrar

System.Windows.Controls.Data e .Extended (redimensione a primeira coluna para ver os nomes

completos):

Page 14: Adotando Silverlight 2.0 - v1 - Exercicios

14

Agora você pode fazer referência aos controles dentro desses namespaces a partir do código.

Para acessá-los a partir do XAML, você também precisará adicionar referências a namespaces no

topo de seu arquivo XAML, imediatamente após as declarações de namespace xmlns e xmlns:x

padrão. Use as duas linhas de código a seguir:

xmlns:ex="clr-

namespace:System.Windows.Controls;assembly=System.Windows.Controls.Extended"

xmlns:data="clr-

namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data"

Agora está pronto. Tudo o que você precisa lembrar de fazer é usar o escopo de namespace, por

exemplo, <data:DataGrid> para instanciar o DataGrid ou <ex:Calendar> para instanciar o

Calendar.

Crédito Extra

Se você quiser ser mais independente e explorar um pouco, experimente. Por exemplo,

verifique as outras propriedades disponíveis no elemento Button a partir do editor do XAML, ou

substitua o Button por um elemento TextBox, TextBlock ou Calendar. (Não se esqueça de

remover ou editar o manipulador de eventos na visualização do código também). Você pode

alterar as propriedades a partir do XAML ou da visualização do código; pode também adicionar

um código de inicialização extra após a chamada InitializeComponent() no construtor de

Páginas.

Page 15: Adotando Silverlight 2.0 - v1 - Exercicios

15

Seção 2: Layout, Controles e Conteúdo

de Vetor

Até agora aprendemos a criar um projeto do Silverlight, adicionar um elemento e interagir com

esse elemento através do uso de manipuladores de eventos definidos no código C#. Mas nossa

aplicação do Silverlight não vai ser muito flexível com apenas um controle.

Painéis são elementos que podem conter múltiplos elementos filhos, e que determinam os

tamanhos e as posições desses filhos. O mais simples é o Canvas – este só tem suporte para

layout fixo, e funciona do mesmo modo que no Silverlight 1.0. O Grid e o StackPanel são de

maior interesse. Eles são novos no Silverlight 2 (e funcionam do mesmo modo que seus

homônimos no WPF).

As seções a seguir ilustram o uso básico dos novos painéis, e apresentam alguns exemplos de

como múltiplos painéis podem ser combinados para produzir layouts mais complexos.

Canvas

Observação: Se você já estiver familiarizado com o Silverlight 1.0, pode pular esta seção e

proceder à do container StackPanel.

A opção mais simples é projetar seu conteúdo do Silverlight com um tamanho fixo, posicionar

elementos individuais com coordenadas (x,y) absolutas e organizar sua página da web para

acomodar esse tamanho fixo. Isso funciona bem quando você está incorporando conteúdo do

Silverlight dentro de um conteúdo HTML existente e o redimensionamento automático do

conteúdo do Silverlight não faz parte das considerações. Os passos a seguir mostram como fazer

isso.

Crie um novo projeto do Silverlight no Visual Studio chamado FixedSize. Deixe que o Visual Studio crie uma Web associada para hospedar o conteúdo do Silverlight.

Abra o Page.xaml. Note que o elemento raiz já tem um tamanho fixo – as propriedades Width e Height foram definidas em 400 e 300 pixels respectivamente. Por padrão, o conteúdo do UserControl é um Grid; substitua os elementos de abertura e de fechamento pelo Canvas, que é uma boa opção para layout fixo.

Page 16: Adotando Silverlight 2.0 - v1 - Exercicios

16

Dentro do elemento Canvas, adicione:

<Rectangle Canvas.Left="60" Canvas.Top="60"

Width="280" Height="180"

Fill="#FFBB8536" Stroke="#FF8A580F"

StrokeThickness="10" RadiusX="70" RadiusY="76.5" />

<Ellipse Canvas.Left="20" Canvas.Top="20"

Width="220" Height="220"

Fill="#FF3685BB" Stroke="#FF0F588A"

StrokeThickness="10" />

<TextBlock FontFamily="Verdana" FontSize="30"

Canvas.Left="180" Canvas.Top="245"

Text="Fixed Layout" />

Rectangle e Ellipse são os dois primitivos simples para desenhar; mais tarde, você verá um elemento Path muito mais sofisticado que tem suporte para curvas de Bézier, linhas e formatos preenchidos. Note que as propriedades anexadas Canvas.Left e Canvas.Top fornecem controle baseado em pixel sobre a localização coordenada dos elementos.

Ter que adicionar e posicionar cada elemento à mão com o XAML é algo bastante trabalhoso. Em vez disso, vamos explorar brevemente o Expression Blend, uma ferramenta de design interativo que funciona junto com o Visual Studio para fornecer uma experiência de design rica par ao layout de conteúdos em um arquivo XAML. Clique com o botão direito em Page.xaml e escolha “Open in Expression Blend”. Você verá o mesmo conteúdo abrir na ferramenta de design:

Mude a visualização para o modo ‘Divisão’, usando as guias do lado direito do Canvas. Isso permitirá que você veja como o XAML muda conforme você edita o formato, ou que edite o XAML à mão e veja o efeito imediatamente.

Page 17: Adotando Silverlight 2.0 - v1 - Exercicios

17

Para começar, vamos ver as classes de formato. Elas podem ser encontradas em dois ícones na barra de ferramentas à esquerda:

A ferramenta Pen (Caneta) permite desenhar curvas de Bézier clicando e arrastando. Se você arrastar, ela desenhará segmentos de linha curvados. Se você clicar, ela desenhará ângulos, e se arrastar dois ou mais ângulos em seguida eles serão unidos por segmentos de linha retos. No XAML, o formato é definido pela propriedade Data do elemento Path, que usa uma sintaxe SVG compacta para representar o contorno do path.

O Silverlight usa a mesma sintaxe que o Windows Presentation Foundation; se quiser aprender mais sobre ela, está documentada em:

http://msdn2.microsoft.com/library/system.windows.media.pathgeometry.aspx.

Os outros dois tipos de formato para os quais o Blend tem suporte são o Rectangle e o Ellipse. Eles estão disponíveis no item da barra de ferramentas abaixo da Pen. (Clique e mantenha pressionado o mouse para fazer o submenu aparecer – ele só mostrará uma das ferramentas por vez. Note que Line não é um formato separado – é apenas uma forma mais simples de criar objetos Path). Você pode definir a largura e a altura desses formatos, e no caso de um Rectangle (Retângulo), o Blend fornece alças de ajuste para arredondar os ângulos. Tente ajustá-los e veja o efeito que isso tem no XAML.

Todos os formatos podem ter seu Fill (Preenchimento) e Stroke (Traço) ajustados da mesma forma. No lado direito, certifique-se de que o painel Properties (Propriedades) está selecionado, e você poderá editá-los nas seções Brushes e Appearance (Pincéis e Aparência). Faça um teste e observe o efeito que têm visualmente e no Xaml.

Observação: O Silverlight tem suporte para uma gama um pouco maior de formatos que o Blend. Ele também tem suporte para Line (Linha), Polygon (Polígono) e Polyline (Polilinha). O

Page 18: Adotando Silverlight 2.0 - v1 - Exercicios

18

Blend não os usa porque tudo o que fazem também pode ser feito com o Path. Mas se você gostaria de testá-los no XAML, encontrará a documentação para eles no Visual Studio Documentation, pesquisando pelo namespace System.Windows.Shapes,

Para certos tipos de gráficos, imagens de bitmap podem ser mais apropriadas que os gráficos orientados a vetor escaláveis oferecidos pelos tipos de formato. Então a seguir você usará a marca Image, que tem suporte para arquivos de bitmap JPEG e PNG. No Blend, selecione a guia ‘Project’ no canto superior direito. Aparecerá uma exibição de árvore de seu projeto. Clique com o botão direito no item de projeto (FixedSize) (o que está abaixo da Solução) e selecione Adicionar item existente. Encontre qualquer imagem de bitmap – pode ser uma das imagens JPEG de exemplo que vêm com o Windows - e abra-a.

Arraste o arquivo de imagem que acabou de adicionar da exibição de árvore do Projeto para a superfície de design. Redimensione-a se necessário para fazer com que caiba no espaço. (Certifique-se de que tem a ferramenta Selection ativa para poder mover e redimensionar itens. É a ferramenta no topo da barra de ferramentas à esquerda). Inspecione o XAML para ver o elemento de Imagem que ele criou para mostrar sua imagem.

Observação: o MediaElement, que tem suporte para a reprodução de arquivos de vídeo, pode ser usado da mesma forma que o elemento Image do Blend. Tente adicionar um arquivo WMV ao seu projeto da mesma forma que adicionou uma imagem. (A documentação do Silverlight descreve os formatos de arquivo de mídia para os quais o Silverlight tem suporte).

Finalmente, você vai testar o TextBlock. Ele pode ser encontrado no final da barra de ferramentas. Note que o TextBox e o TextBlock compartilham o mesmo local na barra de ferramentas; TextBox é para edição de texto, enquanto o TextBlock apenas exibe o texto. Certifique-se de que selecionou o TextBlock – se não clicar e mantiver pressionado o botão da barra de ferramentas para mostrar as duas opções.

Arraste um retângulo na superfície de design – isso determinará o tamanho de seu TextBlock. Também o colocará em modo de edição. Clique fora do TextBlock para terminar a edição. Para editar o texto novamente, escolha a ferramenta Selection e dê um clique duplo no TextBlock.

No XAML, o elemento TextBlock tem suporte para duas maneiras de representar texto. Você pode apenas definir a propriedade do texto diretamente como um atributo dentro da marca TextBlock, por exemplo:

<TextBlock … Text="Foo" />

Ou pode fornecer texto formatado de múltiplas linhas:

<TextBlock TextWrapping="Wrap">

<Run Text="This is some " />

<Run FontWeight="Bold" Text="formatted " />

<Run FontStyle="Italic" Text="text." />

<LineBreak />

<Run Text="It spans multiple lines." />

</TextBlock>

Page 19: Adotando Silverlight 2.0 - v1 - Exercicios

19

Embora o Blend defina elementos Width e Height (largura e altura) quando você arrasta um TextBlock, o bloco vai se autodimensionar se você não especificar essas coisas de maneira explícita. Tente remover o Width e o Height. Tente então especificar apenas um Width, certificando-se de que a propriedade TextWrapping está definida como “Wrap”.

Sinta-se à vontade para testar o Blend um pouco mais - use a janela de Propriedades à direita para testar os pincéis, os gradientes, as transformações e a aparência. Salve então as alterações e feche o Blend para retornar ao editor do Visual Studio. Note que o Visual Studio recarrega o arquivo XAML para garantir que as alterações que você fez sejam atualizadas.

O XAML pode ser usado para representar quase todos os conteúdos de gráficos baseados em vetor. O Expression Design contém suporte embutido para exportar sua saída ao XAML, e há filtros e conversores de terceiros disponíveis para outros formatos como o Adobe Illustrator. Para demonstrar isso, exclua tudo o que está dentro do elemento Canvas do projeto XAML.

Agora abra o arquivo SpaceBeetle.xaml com o Notepad – é uma imagem de exemplo do Expression Design que foi exportada para o XAML. Certifique-se de que a quebra automática de linha está desativada no Notepad, e então copie e cole os conteúdos inteiros para o corpo do elemento Canvas. Você verá que a imagem de vetor é composta de muitos elementos Path, cada qual constituindo uma parte da imagem total.

Exploração extra opcional: Abra o Expression Design e explore essa ferramenta como um modo de criar arte de vetor. Tente abrir uma das outras imagens de exemplo e exportá-la para o XAML. Lembre-se de usar a opção (Silverlight” ou “XAML Silverlight Canvas” (dependendo de qual versão do Expression Design você está usando) para criar um XAML compatível com o subconjunto para o qual o Silverlight tem suporte:

Expression Design 1

Expression Design 2

Page 20: Adotando Silverlight 2.0 - v1 - Exercicios

20

Antes de terminar esse projeto, vamos explorar rapidamente o modo como o conteúdo do Silverlight é hospedado dentro de um container HTML pai. O elemento Canvas dentro do XAML fornece valores de largura e altura que definem o tamanho do container e restringe seus elementos filhos, mas isso não afeta o tamanho do controle do Silverlight propriamente dito - isso é feito a partir do HTML. Abra o arquivo FixedSizeTestPage.aspx e encontre o controle do Silverlight. Por padrão, o projeto define o controle em um tamanho de 640x480:

<asp:Silverlight ID="Xaml1" runat="server"

Source="~/ClientBin/FixedSize.xap" Version="2.0"

Width="640px" Height="480px" />

Execute a aplicação. O conteúdo deve aparecer com o tamanho especificado. Tente adicionar outro conteúdo HTML (por exemplo, um texto) antes e depois do controle do Silverlight, para que você possa ver a posição do plug-in do Silverlight em relação ao conteúdo HTML. O controle do Silverlight acaba encapsulando o plug-in com um <span>, para que ele se comporte como um elemento embutido para fins de layout do HTML.

O controle asp:Silverlight simplesmente pega propriedades Width e Height fixas e as coloca em um estilo gerado no tempo de execução. (Você não verá isso se visualizar a fonte da página HTML, porque o estilo é gerado dinamicamente com script. Contudo, se você usar uma ferramenta inspetora DOM como a que está incluída no Internet Explorer 8, poderá ver o estilo). Um método alternativo é usar estilos CSS diretamente. Para isso, remova a propriedade Width e Height e defina a propriedade CssClass como slPlugin:

<asp:Silverlight ID="Xaml1" runat="server"

Source="~/ClientBin/FixedSize.xap" Version="2.0"

CssClass="slPlugin" />

Na seção <head> da página, após o título, adicione o seguinte:

<style type="text/css">

.slPlugin

{

width: 640px;

height: 480px;

}

</style>

Execute a aplicação novamente. O comportamento deve ser o mesmo de antes.

StackPanel

O StackPanel organiza seus filhos em uma pilha vertical ou horizontal. Se você estiver familiarizado com o StackPanel do WPF, o do Silverlight 2 funciona da mesma forma.

Page 21: Adotando Silverlight 2.0 - v1 - Exercicios

21

1. Crie um novo projeto de aplicação do Silverlight; abra o Page.xaml e remova as propriedades

Width e Height explícitas do UserControl.

2. Substitua o elemento Grid por um StackPanel. Dentro do StackPanel, adicione alguns botões de

opção, como:

<StackPanel x:Name="LayoutRoot" Background="White">

<RadioButton Content="First" />

<RadioButton Content="Second" />

<RadioButton Content="Third" />

<RadioButton Content="Fourth" />

</StackPanel>

3. Veja como os itens são organizados em uma pilha vertical. Edite a propriedade Content de um dos botões para que seja uma seqüência de caracteres muito mais longa, e note como o StackPanel se redimensiona para o conteúdo filho.

4. No segundo RadioButton, tente adicionar uma propriedade HorizontalAlignment com o valor “Right” (Direita), e veja como isso afeta o layout.

5. No StackPanel, adicione uma propriedade Orientation com o valor “Horizontal”. Execute a aplicação novamente, e agora você verá os itens organizados em uma pilha horizontal. Note que a propriedade HorizontalAlignment (alinhamento horizontal) não tem mais efeito nos TextBlocks. Isso porque um painel de pilha sempre fornece exatamente o espaço necessário para cada elemento na direção do empilhamento. Já que agora o painel está empilhando horizontalmente, ele está medindo cada bloco de texto horizontalmente e alocando exatamente o espaço suficiente. Já que não há espaço livre horizontalmente, não há diferença entre alinhamento esquerdo, direito ou centralizado dentro do espaço alocado.

6. Os StackPanels podem conter uma ampla variedade de elementos filhos. Faça uma experiência: tente adicionar elementos de formato como elipses ou retângulos (não se esqueça de especificar uma cor de preenchimento), ou controles como TextBox, Scrollbar, Calendar e Slider. Tente aninhar um StackPanel dentro de um StackPanel. (Para facilitar a visualização da extensão do painel, defina sua propriedade Background com uma cor sólida como “PaleGreen”).

Note que, diferentemente dos controles, que derivam de uma classe pai chamada UIElement e têm um tamanho intrínseco determinado pelo seu conteúdo, elementos de formato como o Rectangle são objetos mais primitivos por razões de desempenho, e não têm um tamanho intrínseco a menos que você especifique uma Width e Height explícita. Já que um StackPanel só oferece o espaço exato necessário na direção do empilhamento, você terá que especificar uma Width (largura) para os elementos gráficos que são filhos do painel horizontal, e uma Height (altura) para os que são filhos do painel vertical – caso contrário os elementos terão zero Width ou Height respectivamente. (Você pode especificar tanto a Width como a Height se preferir. Mas pode omitir a Height no painel horizontal já que o elemento vai simplesmente assumir a altura total do painel; da mesma forma, se não especificar a Width de um elemento gráfico em uma painel vertical, ele será tão largo quanto o próprio painel).

Page 22: Adotando Silverlight 2.0 - v1 - Exercicios

22

7. Tente adicionar uma propriedade Margin a um ou mais controles e veja como isso afeta o layout. As margens podem ser especificadas como um valor único, como um par de valores separados por vírgula que representam as margens esquerda/direita e superior/inferior respectivamente, ou como um conjunto de quatro valores separados por vírgula que representam as margens esquerda, superior, direita e inferior individualmente.

Grid

Grid é o container de layout mais sofisticado e flexível, permitindo que o conteúdo seja escalado conforme o redimensionamento do container pai. O painel Grid dimensiona e posiciona seus filhos gravando o espaço em uma grade. Você pode especificar o número de linhas e colunas de que precisa, usando o dimensionamento fixo, automático ou proporcional. (Por exemplo, você pode dar a uma coluna um quarto da largura e a outra o espaço remanescente). Os filhos de uma grade podem ocupar uma única célula, ou podem abranger múltiplas células. As células também podem conter múltiplos filhos. Os passos a seguir mostram esses recursos do Grid, conforme vamos construindo um seletor de cores simples.

1. Crie um novo projeto do Silverlight chamado SimpleColorPicker. Deixe que o Visual Studio crie uma Web associada para hospedar o conteúdo do Silverlight. Nós vamos criar uma interface simples para selecionar qualquer cor que o Silverlight possa exibir. Vai ficar assim:

No lado esquerdo do seletor de cores estão quadro controles deslizantes para selecionar os valores alfa, vermelho, verde e azul. No lado direito há uma pré-visualização da cor, junto com seu valor hex e uma caixa de grupo para selecionar o modelo de cor que queremos usar. Há várias formas de fazer o layout do conteúdo, mas nós vamos usar uma grade como o container pai, dividindo-a em duas colunas de três linhas, com a primeira coluna abrangendo as três linhas, como mostram as linhas vermelhas abaixo:

Page 23: Adotando Silverlight 2.0 - v1 - Exercicios

23

Dentro do Grid, adicione um elemento <Grid.ColumnDefinitions>. Dentro dele, adicione duas marcas <ColumnDefinition />. Isso indica que a grade terá duas colunas. Da mesma forma, adicione um elemento <Grid. RowDefinitions> contendo três marcas <RowDefinition />. O código deve ficar assim:

<Grid x:Name="LayoutRoot" Background="White">

<Grid.ColumnDefinitions>

<ColumnDefinition />

<ColumnDefinition />

</Grid.ColumnDefinitions>

<Grid.RowDefinitions>

<RowDefinition />

<RowDefinition />

<RowDefinition />

</Grid.RowDefinitions>

Nós podemos modificar a largura das colunas ou a altura das linhas usando as propriedades Width e Height, respectivamente. Elas podem ser especificadas como pixels absolutos (por exemplo, ColumnDefinition Width="250"), como uma razão relativa a outras colunas ou linhas, ou dimensionar automaticamente conforme seu conteúdo (use Width="Auto"). Para este exemplo, vamos definir as larguras das colunas em 60% e 40%. Vamos começar mostrando as linhas de grade para fins de depuração. Adicione o seguinte atributo ao elemento Grid:

<Grid x:Name="LayoutRoot" Background="White" ShowGridLines="True">

Note as linhas azul/amarela na exibição do designer. (Esse recurso serve como um auxílio ao desenvolvimento e não para o desenho de linhas de propósito geral. Use o elemento border para ter um controle refinado sobre as bordas das células). Agora edite as larguras de cada coluna, assim:

<Grid.ColumnDefinitions>

<ColumnDefinition Width="60*" />

<ColumnDefinition Width="40*" />

</Grid.ColumnDefinitions>

(É a razão dos valores que importa aqui, e não os valores absolutos. Nós podíamos ter definido 3* e 2* em vez de 60* e 40* e obtido exatamente o mesmo resultado).

Quando usamos o Canvas como um painel de container em uma seção anterior, pudemos definir o local exato de seus filhos através do uso de propriedades anexadas. Definindo o Canvas.Left e o Canvas.Top em qualquer elemento, nós poderíamos definir uma coordenada (x,y) para qualquer elemento filho. Com um container escalável como o Grid, em vez de especificar propriedades Top e Left, especificamos a célula em que o elemento deve aparecer usando as propriedades anexadas Grid.Row e Grid.Column. Se quisermos um controle mais refinado sobre o local desse elemento dentro de uma célula de grade, podemos aplicar uma margem.

Na célula superior direita, vamos adicionar um retângulo que será preenchido com uma cor de pré-visualização. Imediatamente antes do elemento </Grid> de fechamento, adicione o seguinte:

Page 24: Adotando Silverlight 2.0 - v1 - Exercicios

24

<Rectangle Grid.Row="0" Grid.Column="1"

x:Name="PreviewColor"

Fill="CadetBlue" Margin="10" Stroke="#FF666666" StrokeThickness="2" />

Note que as linhas e colunas são baseadas em zero, portanto isso representa a primeira linha e a segunda coluna. O x:Name é o que nos permitirá fazer referência a esse retângulo a partir do código. O aspecto mais interessante do elemento acima é que não precisamos especificar de forma explícita um tamanho para o retângulo – ele preenche automaticamente a célula (exceto para a margem). Tente mudar o tamanho da margem e definir as propriedades RadiusX e RadiusY para arredondar os ângulos do retângulo, e também a Width e a Height para ajustar o tamanho do retângulo.

Às vezes é mais fácil aninhar os painéis do container um dentro do outro. Vamos adicionar uma caixa de texto e rotular imediatamente abaixo da pré-visualização do retângulo. Adicione o seguinte XAML:

<StackPanel Grid.Row="1" Grid.Column="1" >

<TextBlock FontSize="12">Color</TextBlock>

<TextBox x:Name="HexColor" Text="#FF5F9EA0" Margin="10,5" FontSize="11"/>

</StackPanel>

Por padrão, o StackPanel é alinhado com o topo da célula; nós podemos ajustar isso adicionando VerticalAlignment="Center". Tente isso agora.

Da mesma forma, vamos adicionar alguns botões de opção no final da página:

<StackPanel Grid.Row="2" Grid.Column="1">

<TextBlock FontSize="12">Color Model</TextBlock>

<RadioButton Content="ARGB" Margin="10,0" IsChecked="True"/>

<RadioButton Content="HSL" Margin="10,0" />

</StackPanel>

Se você estiver familiarizado com outros ambientes como o Windows Forms, pode ficar surpreso em ver que não há um container GroupBox para os botões de opção. Como o Silverlight sabe quais botões de opção ficam juntos?

Por padrão, os botões de opção são agrupados de acordo com seu container pai (neste caso, o StackPanel). Você pode, no entanto, mudar isso usando a propriedade GroupName – isso lhe permite ter múltiplos grupos de botões por pai.

Por último, vamos adicionar os controles deslizantes para o lado esquerdo do controle.

<StackPanel Grid.Row="0" Grid.Column="0" Grid.RowSpan="3"

VerticalAlignment="Center">

<TextBlock Text="Alpha" FontSize="12" Margin="10,15,0,0"/>

<Slider x:Name="AlphaSlider" Margin="20,0,10,0" Maximum="255" Value="255"/>

<TextBlock Text="Red" FontSize="12" Margin="10,15,0,0"/>

<Slider x:Name="RedSlider" Margin="20,0,10,0" Maximum="255" Value="95"/>

<TextBlock Text="Green" FontSize="12" Margin="10,15,0,0"/>

<Slider x:Name="GreenSlider" Margin="20,0,10,0" Maximum="255" Value="158"/>

<TextBlock Text="Blue" FontSize="12" Margin="10,15,0,0"/>

<Slider x:Name="BlueSlider" Margin="20,0,10,0" Maximum="255" Value="160"/>

</StackPanel>

Page 25: Adotando Silverlight 2.0 - v1 - Exercicios

25

Aqui você pode ver que estamos definindo uma propriedade RowSpan para mesclar as três linhas na coluna esquerda. Isso nos dá uma forma conveniente de usar uma única grade, embora nem todas as colunas tenham o mesmo número de linhas. Note o uso de um controle Slider para permitir que o usuário selecione uma cor – por padrão ele vai do 0 ao 1, então definimos explicitamente o máximo como 255 e os valores como o equivalente decimal de #FF5F9EA0.

Concluímos o layout da aplicação, vamos agora remover a propriedade ShowGridLines já que ela não é mais necessária:

<Grid x:Name="LayoutRoot" Background="White">

Tudo o que precisamos fazer agora é adicionar um código à aplicação para cuidar do movimento do controle deslizante. Posicione o cursor de texto após o atributo de Valor mas antes do elemento de fechamento do Slider chamado AlphaSlider, e digite ValueChanged=

Uma dica de ferramenta do IntelliSense vai aparecer, oferecendo a você a oportunidade de criar um novo manipulador de eventos:

Dê um clique duplo na dica de ferramenta com seu mouse.

Agora abra o Page.xaml.cs no Solution Explorer (um filho do Page.xaml), e verá que um stub do manipulador de eventos foi adicionado ao código.

private void AlphaSlider_ValueChanged(object sender,

RoutedPropertyChangedEventArgs<double> e)

{

}

Vamos preencher esse manipulador de eventos com alguma lógica para atualizar o retângulo e a caixa de texto de modo a corresponder aos valores atuais dos controles deslizantes:

Color color = Color.FromArgb((byte)AlphaSlider.Value, (byte)RedSlider.Value,

(byte)GreenSlider.Value, (byte)BlueSlider.Value);

PreviewColor.Fill = new SolidColorBrush(color);

HexColor.Text = color.ToString();

O que estamos fazendo aqui é criar um novo objeto de cor com base no valor dos controles deslizantes, e depois usá-lo em um pincel para preencher o retângulo, bem como atualizar a caixa de texto. O valor dos controles deslizantes é armazenado como um valor de ponto

Page 26: Adotando Silverlight 2.0 - v1 - Exercicios

26

flutuante, então temos que converter cada um deles para um byte antes de passá-los ao método estático Color.FromArgb(), que usa quatro parâmetros representando os valores alfa, vermelho, verde e azul e retorna um objeto que representa a cor propriamente dita.

Já que queremos que o mesmo código seja executado independentemente de qual controle deslizante é manipulado, podemos simplificar as coisas definindo o mesmo manipulador para o evento ValueChanged de cada controle deslizante. Renomeie o manipulador de eventos de AlphaSlider_ValueChanged para Slider_ValueChanged (corrija-o tanto no código C# quanto no XAML). Agora vá até o XAML para o controle deslizante vermelho, e digite novamente ValueChanged=

Note que agora é possível selecionar o evento Slider_ValueChanged existente no IntelliSense:

Repita isso para os controles deslizantes verde e azul.

Observação: Você também pode usar a ligação de dados para conectar um elemento a uma fonte de dados subjacente; para mais informações sobre o uso da ligação de dados dessa forma, verifique o laboratório relacionado disponível separadamente.

Tente construir o projeto selecionando a opção do menu Build / Build Solution. A aplicação deve compilar sem erros. Vamos vê-la em ação. Para isso, pressione F5 ou escolha Debug / Start Debugging no menu. Se esta for a primeira vez que tenta executar este projeto, você verá a seguinte caixa de diálogo:

Selecione a opção padrão e clique em OK.

Problema na Beta 2 Você verá que embora a aplicação compile, ela não é executada com sucesso (você recebe um NullReferenceException). A razão para isso é que o evento ValueChanged dispara durante a inicialização dos componentes, porém como os componentes não estão totalmente carregados, não é possível acessá-los a partir do código. Há duas formas de resolver isso: você pode tentar uma das duas para este laboratório. Adicione os manipuladores de eventos a partir do código depois que o método InitializeComponent() tiver disparado, ou defina um campo de bool particular na classe como verdadeiro quando o InitializeComponent tiver executado e teste o valor do bool no evento antes de executar o código. Agora reconstrua e execute.

Page 27: Adotando Silverlight 2.0 - v1 - Exercicios

27

Teste a aplicação: você deve conseguir mover os controles deslizantes e ver o preenchimento do retângulo e a caixa de texto mudarem de acordo.

Agora vamos testar um pouco a depuração do Visual Studio. Sem fechar o navegador, mude para o Visual Studio. Vá para a primeira linha de código no evento Slider_ValueChanged, e adicione um ponto de interrupção pressionando F9 ou clicando na margem esquerda. Você verá um ponto vermelho aparecer para indicar que um ponto de interrupção foi definido:

Agora tente mover um dos controles deslizantes. A aplicação vai disparar instantaneamente o ponto de interrupção e o Visual Studio vai mudar para o modo de depuração. No final da tela, você verá uma série de janelas de ferramenta diferentes que fornecem várias exibições da aplicação em execução.

No lado esquerdo, a janela Autos mostra variáveis que são usadas nas instruções atuais e ao redor; A janela Locals mostra todos os objetos no contexto atual; a janela Watch permite definir uma observação em qualquer objeto ou propriedade que desejar. (Use o menu Debug / Windows para adicioná-las ao seu editor se não vir uma delas por padrão). Você não apenas pode ver as variáveis aqui, mas pode também modificá-las. Se nunca usou o Visual Studio antes, tente isso: edite o GreenSlider.Value para que seja 255.0 e o RedSlider.Value para que seja 0. (Você também pode modificar o BlueSlider.Value inserindo isso no lado esquerdo da grade de propriedades da janela Watch – por padrão, isso não aparece porque o serviço de linguagem só exibe as primeiras entradas automaticamente). Agora pressione F5 para continuar a execução da aplicação. Você verá que os valores alterados são refletidos na aplicação no tempo de execução. Quando terminar, feche o navegador e o Visual Studio vai voltar automaticamente ao modo de edição. (Você pode desativar o ponto de interrupção pressionando F9 na devida linha novamente).

Para provar que o Grid é um objeto fácil de escalar, vamos voltar à exibição do XAML no Visual Studio. No topo do arquivo, você verá que já há uma largura e uma altura padrão aplicadas ao UserControl de 400x300. Remova os atributos Width e Height. Você verá que a exibição do design vai parecer minúscula repentinamente. O que está acontecendo agora é que o objeto inteiro está se dimensionando para o mínimo que tenha suporte para seu conteúdo filho. Na verdade, isso é tão pequeno que os controles deslizantes na coluna esquerda têm tão pouco espaço que se tornam inúteis, portanto vamos ajustar isso um pouco.

Page 28: Adotando Silverlight 2.0 - v1 - Exercicios

28

Na coleção ColumnDefinitions, nós vamos adicionar propriedades MinWidth a ambas as colunas para que o controle não possa ter uma dimensão menor que um tamanho específico. Defina a largura mínima da primeira para 100 pixels, e a segunda para 90 pixels, assim:

<Grid.ColumnDefinitions>

<ColumnDefinition Width="60*" MinWidth="100"/>

<ColumnDefinition Width="40*" MinWidth="90"/>

</Grid.ColumnDefinitions>

(Note que esses valores não podem ser obedecidos se a janela do navegador obrigar o controle geral do Silverlight a ser menor que esse tamanho).

Agora vamos executar a aplicação novamente. Desta vez, você deve conseguir redimensionar a janela e fazer com que o controle fique visível.

Page 29: Adotando Silverlight 2.0 - v1 - Exercicios

29

Seção 3: Elementos e Objetos XAML

Conforme vimos acima, as interfaces de usuário do Silverlight são normalmente construídas com o uso do XAML. Cada elemento da interface de usuário criado com XAML tem um objeto .NET correspondente no Silverlight 2. Uma coisa importante a notar é que você não é obrigado a usar o XAML para criar os elementos – você pode escrever um código que crie objetos de elemento da interface de usuário diretamente, sem usar o XAML. A seção a seguir usa essa técnica para desenhar um gráfico de barras.

1. Crie um novo projeto do Silverlight no Visual Studio chamado BarGraph. Deixe que o Visual Studio crie uma Web associada para hospedar o conteúdo do Silverlight.

Abra o Page.xaml. Remova os elementos Width e Height do UserControl, e defina uma Width (largura) e uma Height (altura) no Grid (grade) de 600 e 400 pixels respectivamente.

Adicione uma nova classe chamada GraphBuilder para seu projeto do Silverlight. Clique com o botão direito no projeto BarGraph e escolha Add / Class.

Neste laboratório, vamos embutir em código alguns recursos do gráfico, portanto adicione os seguintes campos à classe para representar essas configurações fixas:

const int FirstBar = -5;

const int LastBar = 5;

const double Mean = 0;

const double StandardDeviation = 1;

Adicione a seguinte função – esta é a função de densidade de probabilidade para a distribuição normal, e fornecerá os valores para o gráfico:

static double NormalProbability(double x)

{

double stdx = (x - Mean) / StandardDeviation;

double stdy = (1 / (2 * Math.PI)) * Math.Exp(-(stdx * stdx) / 2);

return stdy / StandardDeviation;

}

Adicione o seguinte método a esta classe:

public static void BuildGraphInGrid(Grid g)

{

}

Você vai adicionar código a este método para criar o gráfico.

Já que precisaremos repetir os mesmos passos para cada barra do gráfico, comece com um loop:

for (int bar = FirstBar; bar <= LastBar; ++bar)

Page 30: Adotando Silverlight 2.0 - v1 - Exercicios

30

{

}

O código restante vai dentro desse loop.

Nós precisamos de uma coluna de grade para cada barra do gráfico, então adicione o seguinte código:

ColumnDefinition colDef = new ColumnDefinition();

g.ColumnDefinitions.Add(colDef);

Cada barra será representada por um Rectangle, então vamos criar um com preenchimento e contorno adequados:

Rectangle barRect = new Rectangle();

barRect.Fill = new SolidColorBrush(Colors.Red);

barRect.Stroke = new SolidColorBrush(Colors.Black);

barRect.StrokeThickness = 1.5;

barRect.Margin = new Thickness(5, 0, 5, 0);

barRect.VerticalAlignment = VerticalAlignment.Bottom;

A Margem garante que haja algum espaço dos lados da barra. O VerticalAlignment garante que a barra inicie na parte inferior do gráfico.

Em seguida, defina a altura da barra conforme a função que estamos plotando:

double value = NormalProbability(bar);

barRect.Height = value * g.Height * 4;

Note que é considerada uma escala vertical de 0 a 0.25. É uma escala razoável para essa aplicação específica. Uma ferramenta de gráfico mais sofisticada precisaria, é claro, fazer algo mais inteligente a respeito das escalas.

Para vê-la, temos que adicionar a barra ao gráfico. Isso significa definir sua coluna de grade e adicioná-la à grade:

Grid.SetColumn(barRect, bar - FirstBar);

g.Children.Add(barRect);

Finalmente, para ver os resultados de seu trabalho, você precisa chamar o código que escreveu. Abra o arquivo code-behind Page.xaml.cs, e no construtor adicione o seguinte depois do método InitializeComponent:

GraphBuilder.BuildGraphInGrid(LayoutRoot);

Page 31: Adotando Silverlight 2.0 - v1 - Exercicios

31

Execute a aplicação. Você deve ver uma série de barras como esta:

Se você não vir nada na tela, é possível que tenha pulado o passo em que define a altura e a largura na grade. Definir essas propriedades na grade (em vez do controle) é importante, porque sem ela o gráfico de barras é desenhado antes do dimensionamento da grade, o que significa que ela terá largura e altura de “Auto” e altura e largura reais de 0. Como resultado, todas as alturas das barras serão 0!

(Avançado). Remova a largura e a altura fixas do controle da grade e modifique a aplicação para permitir que a grade seja reescalada conforme o usuário redimensiona a janela do navegador. (Dica: você terá que interceptar o evento SizeChanged e corrigir um pouco o método BuildGraphInGrid para que ele não continue adicionando colunas e retângulos toda vez que o tamanho for alterado).

(Opcional). Use essa aplicação para testar mais o Silverlight. Aqui estão algumas idéias:

a. Modifique o algoritmo no método BuildGraphInGrid para produzir um tipo diferente de grade. Por exemplo, use os valores para modificar a cor das barras;

b. Adicione um eixo à grade – origine linhas e rótulos nos intervalos;

c. Adicione um pouco de interatividade – uma dica de ferramenta mostrando os valores reais em um evento de focalização do mouse.

Page 32: Adotando Silverlight 2.0 - v1 - Exercicios

32

Seção 4: Manipulação de Entrada e

Eventos

Esta parte do laboratório se concentra em vários aspectos da manipulação de entrada. Começamos olhando a manipulação básica de eventos de mouse e teclado, antes de cobrir manipulações de eventos roteados mais avançadas, à medida que criamos um mini-jogo que exercita tudo o que aprendemos até agora.

Manipulação Básica de Eventos de Mouse e Teclado

Nesta seção, você vai realizar algumas manipulações de eventos bastante simples, para permitir que um objeto seja arrastado pelo controle do Silverlight com o mouse, e sua cor seja alterada com o teclado.

Crie um novo projeto do Silverlight no Visual Studio chamado BasicInput. Deixe que o Visual Studio crie uma Web associada para hospedar o conteúdo do Silverlight.

No Page.xaml, mude o elemento Grid para um Canvas e adicione uma elipse dentro do elemento Canvas, assim:

<Canvas x:Name="LayoutRoot" Background="White">

<Ellipse x:Name="ellipse"

Fill="Blue" Width="100" Height="50"

Canvas.Top="20" Canvas.Left="20" />

</Canvas>

Construa o projeto (Ctrl+Shift+B ou Build / Build Solution) para garantir que o IntelliSense fique atualizado com as mudanças que você fez no XAML.

Abra o Page.xaml.cs. No construtor, após a chamada do InitializeComponent, adicione código para conectar manipuladores para os eventos MouseLeftButtonDown, MouseLeftButtonUp e MouseMove da elipse. (Lembre-se de que no exemplo do seletor de cores, fizemos isso com o código XAML adicionando um atributo para cada evento. Desta vez faremos através do código para demonstrar um método alternativo. Nenhum é “melhor” – você pode usar o estilo ou o fluxo de trabalho de sua preferência). Novamente, o Visual Studio pode fazer a maior parte do trabalho – se você digitar apenas +=, ele mostrará uma ToolTip (dica de ferramenta):

Se você pressionar a tecla TAB duas vezes, ele vai gerar o construtor delegado primeiro, e o método do manipulador de eventos depois. Faça isso para os três eventos.

Page 33: Adotando Silverlight 2.0 - v1 - Exercicios

33

Remova de cada método o código que abre uma exceção. O Visual Studio adiciona isso para que você não se esqueça de escrever o método. Aqui, isso só vai atrapalhar conforme avançarmos pelos passos.

Você vai adicionar código que permite ao usuário arrastar a elipse. O manipulador MouseMOve fará o trabalho, mas ele precisa saber duas coisas: se a operação arrastar está em progresso no momento e, se estiver, onde o mouse estava com relação à elipse quando a operação arrastar começou, para saber onde posicioná-lo agora. Para armazenar esse estado, adicione os seguintes campos à classe Page:

public partial class Page : UserControl

{

bool dragInProgress = false;

Point dragOffset;

No código MouseLeftButtonDown, defina estes campos:

dragInProgress = true;

dragOffset = e.GetPosition(ellipse);

No manipulador MouseMove, atualize a posição se uma operação arrastar estiver em progresso:

if (dragInProgress)

{

Point mousePoint = e.GetPosition(this);

Canvas.SetLeft(ellipse, mousePoint.X - dragOffset.X);

Canvas.SetTop(ellipse, mousePoint.Y - dragOffset.Y);

}

Execute a aplicação. Você deve conseguir clicar na elipse para começar a arrastá-la, mas há dois problemas a resolver. O primeiro e mais óbvio é que a operação arrastar continua depois que você solta o mouse. Para corrigir isso, adicione o seguinte código ao manipulador MouseLeftButtonUp:

dragInProgress = false;

O segundo problema, mais sutil, é que se você mover o mouse rápido demais, ele pode deixar a elipse para trás. A razão disso é que uma vez que o mouse deixa a elipse, o manipulador MouseMove para de ser chamado. Para corrigir isso, adicione uma chamada para ellipse.CaptureMouse() dentro do manipulador MouseLeftButtonDown – isso faz com que a elipse continue a ter eventos de mouse durante o tempo em que o mouse estiver dentro do plug-in do Silverlight, não importa se já tiver passado a elipse. Adicione também uma chamada para ellipse.ReleaseMouseCapture() no manipulador de eventos MouseLeftButtonUp.

Page 34: Adotando Silverlight 2.0 - v1 - Exercicios

34

Execute a aplicação. Você deve conseguir mover o mouse mais rápido agora sem problemas, contanto que permaneça dentro da área ocupada pelo controle do Silverlight.

A seguir você vai adicionar manipulação de teclado. Adicione um manipulador para o evento KeyDown do Page: use essa palavra-chave para se referir ao objeto atual, assim:

this.KeyDown += new KeyEventHandler(Page_KeyDown);

Adicione um código que altere o preenchimento da elipse com base na tecla pressionada:

void Page_KeyDown(object sender, KeyEventArgs e)

{

Color c;

switch (e.Key)

{

case Key.R:

c = Colors.Red;

break;

case Key.G:

c = Colors.Green;

break;

case Key.B:

c = Colors.Blue;

break;

default:

return;

}

ellipse.Fill = new SolidColorBrush(c);

e.Handled = true;

}

Execute a aplicação. Quando clicar em algum lugar dentro do controle, ela responderá às teclas pressionadas.

Roteamento de Eventos e Fontes de Eventos

Muitos eventos no Silverlight 2 “se propagam”, assim como fazem no Silverlight 1.0 e no WPF. Isso permite anexar um único manipulador de eventos que recebe notificações de eventos de múltiplos elementos. No Silverlight 1.0 isso era um pouco limitado, já que não havia uma forma de descobrir de qual elemento um determinado evento tinha se propagado. O Silverlight 2 tem suporte para a propriedade Source no objeto de argumento do evento, o que resolve esse problema.

Page 35: Adotando Silverlight 2.0 - v1 - Exercicios

35

Vamos colocar tudo o que aprendemos até agora em prática para criar um mini-jogo divertido chamado “PopTheBubble”. Nesse jogo, vamos desenhar novas “bolhas” (elipses) na tela a cada ½ segundo, e o jogador deve removê-las o mais rápido possível clicando nelas.

1. Crie um novo projeto do Silverlight no Visual Studio chamado PopTheBubble. Deixe que o Visual Studio crie uma Web associada para hospedar o conteúdo do Silverlight.

2. Mude o elemento Grid padrão (chamado LayoutRoot) para um Canvas.

Abra o arquivo code-behind Page.xaml.cs. Nós precisamos que um evento de marcação (tick) dispare a cada ½ segundo. Podemos fazer isso usando um DispatcherTimer, que dispara um evento no thread da interface de usuário a um intervalo específico. O DispatcherTimer reside no namespace System.Windows.Threading, portanto vamos adicioná-lo à nossa classe colocando a seguinte cláusula no topo do projeto:

using System.Windows.Threading;

Agora vamos adicionar dois campos à classe, imediatamente após a declaração da classe e antes do construtor:

public partial class Page : UserControl

{

DispatcherTimer timer;

Random rng;

No construtor, após a chamada para InitializeComponent, vamos inicializar o temporizador e o gerador de número aleatório:

rng = new Random();

timer = new DispatcherTimer();

timer.Interval = new TimeSpan(0, 0, 0, 0, 500);

A classe TimeSpan representa um período de tempo (aqui, 0 dias, 0 horas, 0 minutos, 0 segundos, 500 milissegundos).

Vamos criar um manipulador de eventos para o evento Tick que o objeto temporizador vai disparar. Faça com que o Visual Studio gere um método chamado timer_Tick que vai manipular o evento.

timer.Tick += new EventHandler(timer_Tick);

Page 36: Adotando Silverlight 2.0 - v1 - Exercicios

36

No método Timer_Tick, vamos ter uma elipse criada de tamanho e cor aleatórios que será adicionada ao canvas:

void timer_Tick(object sender, EventArgs e)

{

byte[] colors = new byte[3];

Ellipse el = new Ellipse();

Point c1 = new Point(rng.NextDouble() * Width, rng.NextDouble() *

Height);

Point c2 = new Point(rng.NextDouble() * Width, rng.NextDouble() *

Height);

Rect bounds = new Rect(c1, c2);

el.Width = bounds.Width;

el.Height = bounds.Height;

Canvas.SetLeft(el, bounds.Left);

Canvas.SetTop(el, bounds.Top);

rng.NextBytes(colors);

Color c = Color.FromArgb(255, colors[0], colors[1], colors[2]);

el.Fill = new SolidColorBrush(c);

LayoutRoot.Children.Add(el);

}

Por último, precisamos iniciar o temporizador. Adicione a seguinte linha como a última instrução do método InitializeComponent():

timer.Start();

Vamos construir e executar a aplicação. Você deve ver algumas elipses dispersas aleatoriamente, aparecendo a cada meio segundo. Se as elipses aparecerem no mesmo lugar, verifique se você mudou o elemento Grid no XAML para um Canvas (passo 2).

Em seguida, você vai adicionar código para manipular os cliques do mouse em qualquer uma dessas Ellipses. No construtor, anexe um manipulador ao evento de botão esquerdo do mouse pressionado no controle, e crie um manipulador de eventos de stub, conforme descrevemos no início desta seção.

A propagação de eventos significa que os eventos MouseLeftButtonDown gerados pelas Ellipses vão se propagar para o nível da página. É claro que o argumento ‘sender’ do manipulador sempre vai se referir ao elemento ao qual você anexou o manipulador – o Page neste caso – mas o Silverlight 2 tem agora suporte para a propriedade Source no argumento do evento, como o WPF. Isso permite descobrir de onde o evento veio. Então podemos descobrir se o evento veio de uma das elipses que adicionamos substituindo a posição NotImplementedException() no manipulador de eventos de mouse pelo seguinte código:

Ellipse target = e.Source as Ellipse;

if (target != null)

{

}

Page 37: Adotando Silverlight 2.0 - v1 - Exercicios

37

Se você não estiver familiarizado com o código .NET, a primeira instrução converte e.Source (que é do tipo object) para o tipo Ellipse. Se a fonte não for realmente uma elipse (por exemplo, você clica no segundo plano da página), o target será definido como nulo. Isso é diferente na escrita: Ellipse target = (Ellipse) e.Source; (outra forma de converter que usamos anteriormente no laboratório), já que o método gera uma exceção se o objeto não corresponder ao tipo target (alvo).

Dentro do bloco if, vamos adicionar uma única instrução para remover a elipse em que o usuário clicou da coleção Canvas pai. Isso vai apagá-la da tela e liberar os recursos que ela estava usando.

if (target != null)

{

LayoutRoot.Children.Remove(target);

}

Execute a aplicação – você deve conseguir estourar as bolhas clicando nelas.

(Opcional). Use essa aplicação para testar mais o Silverlight. Aqui estão algumas idéias:

a. Adicionar um TextBlock à tela que conta o número de bolhas estouradas.

b. Use um segundo DispatcherTimer para dar ao jogador um limite de tempo para estourar o máximo de bolhas possível. Após (digamos) 30 segundos, exiba uma mensagem na tela que mostra quantas bolhas foram estouradas, junto com um botão que permite ao jogador recomeçar o jogo.

c. Use o evento MouseLeftButtonDown para testar se o jogador “perdeu” uma bolha clicando no segundo plano da página em vez de clicar na bolha. (Dica, a conversão para Ellipse vai falhar porque o Target será de um tipo diferente). Se ele perder uma bolha, exiba uma mensagem de “fim de jogo”.

d. Adicione um controle deslizante de dificuldade à tela para controlar o intervalo de aparecimento das bolhas.

e. Verifique se o jogador conseguiu remover todas as bolhas com sucesso, e exiba a mensagem “você venceu”.

f. Use o Expression Blend para projetar uma “bolha” mais bonita em vez da Ellipse de cor sólida. Substitua a elipse pela nova bolha baseada em XAML.

g. Use as técnicas da próxima seção do laboratório para permitir que o jogo seja jogado em modo de tela inteira.

Page 38: Adotando Silverlight 2.0 - v1 - Exercicios

38

Seção 5: Modo de Tela Inteira

Anteriormente você viu como fazer seu conteúdo do Silverlight preencher completamente a janela do navegador. No entanto, às vezes é útil ir um passo além disso, e fazer seu conteúdo preencher a tela inteira. Os passos a seguir mostram como usar o suporte do Silverlight para operações de tela inteira.

1. Crie um novo projeto do Silverlight chamado FullScreen. Deixe que o Visual Studio crie uma Web associada para hospedar o conteúdo do Silverlight.

2. Abra o Page.xaml. Remova as propriedades Width e Height do UserControl raiz.

3. Substitua o conteúdo por isso:

<Grid x:Name="LayoutRoot" >

<Rectangle x:Name="toggleFullScreen" Fill="Blue"

RadiusX="30" RadiusY="30" />

</Grid>

Uma pequena peculiaridade: note que o designer não mostrará o conteúdo nesse ponto, porque nenhum tamanho é especificado no retângulo e portanto o designer dimensiona a grade de acordo com seu conteúdo, que é 0px de largura e 0px de altura. O retângulo será exibido no tempo de execução porque o container HTML para o controle do Silverlight fará com que o conteúdo seja dimensionado de modo a caber no container (isto é, a largura e a altura da tela).

4. Adicione um manipulador de eventos para o evento MouseLeftButtonDown no Rectangle usando a técnica descrita anteriormente.

5. Neste manipulador, você precisa recuperar o objeto ‘conteúdo’ do plug-in do Silverlight, pois esse é o objeto que controla se o plug-in está no modo de tela inteira no momento:

System.Windows.Interop.Content contentObject =

Application.Current.Host.Content;

6. Para fazer o plug-in mudar de tela inteira para o modo normal quando esse elemento for clicado, adicione o seguinte código no manipulador:

contentObject.IsFullScreen = !contentObject.IsFullScreen;

7. Execute a aplicação. Quando você clicar no retângulo azul, a aplicação deve mudar para o modo de tela inteira. Quando você clicar novamente ela voltará par o modo normal.

8. Note que quando for para a tela inteira, aparecerá uma mensagem indicando que o usuário pode reverter para o modo normal pressionando Esc. (Essa mensagem é fornecida automaticamente pelo Silverlight. Você não pode desativá-la – ela faz parte do design, para impedir que um código mal-intencionado crie uma imagem falsa de estação de trabalho,

Page 39: Adotando Silverlight 2.0 - v1 - Exercicios

39

com aparência plausível, para enganar o usuário e fazê-lo digitar uma senha). Isso significa que a aplicação pode sair do modo de tela inteira sem que seu código seja envolvido. Para essa aplicação em particular, isso não é problema – a única coisa interessante que acontece quando vamos para a tela inteira é que o retângulo se redimensiona, e isso acontece automaticamente graças ao elemento Grid raiz. Entretanto, uma aplicação mais útil pode ter que fazer algo quando transitar entre o modo de tela inteira e o modo normal. Para lidar com isso, adicione código ao construtor para anexar um manipulador ao evento FullScreenChanged do objeto de conteúdo:

Application.Current.Host.Content.FullScreenChanged +=

new EventHandler(Content_FullScreenChanged);

9. No manipulador desse evento, escreva o seguinte código para mudar a cor do retângulo a fim de indicar o modo:

void Content_FullScreenChanged(object sender, EventArgs e)

{

toggleFullScreen.Fill = Application.Current.Host.Content.IsFullScreen ?

new SolidColorBrush(Colors.Red) :

new SolidColorBrush(Colors.Blue);

}

10. Execute a aplicação. Note que o retângulo será vermelho quando for tela inteira e azul quando for modo normal. A volta para a cor azul acontece mesmo que o modo normal entre pela tecla Escape.

Page 40: Adotando Silverlight 2.0 - v1 - Exercicios

40

B- Animação no Silverlight

Primeiros Passos na criação e no uso de animações

Neste laboratório, você terá uma visão geral da API de Animação no Silverlight, vai se familiarizar com

as ferramentas de design visual para animação disponíveis no Blend e executar uma aplicação bastante

simples que usa código de procedimento para gerar e reutilizar Storyboards. E no final aprenderá a usar

um DispatcherTimer.

Page 41: Adotando Silverlight 2.0 - v1 - Exercicios

41

Seção 1: Uma olhada na API de

Animação

Animação no contexto do Silverlight pode ser resumida em uma linha – mudar o valor de uma propriedade de um objeto com o tempo (variando de instantâneo a infinito). Criar animações é na verdade algo bastante simples de aprender. Basicamente, você cria um Storyboard que executa uma ou mais Animações quando o Storyboard é começado. A parte difícil das animações é saber quando e onde usá-las e o que animar. Após este Laboratório, você terá uma boa idéia de como animar propriedades. Com relação a quando e onde usá-las, pode não ser tão difícil quanto mencionamos acima. Há milhões de exemplos (bons e maus (e péssimos)) de animações em sites, interfaces de usuário de aplicações, e ocultos naqueles GIFs animados que você adiciona à sua assinatura de e-mail e encaminha a todos os seus amigos. Agora vamos nos divertir um pouco e ver como os Storyboards e as Animações funcionam, depois vamos levá-los para um teste no Expression Blend.

Storyboards A classe Storyboard é o container e a interface de suas Animações. Storyboards contêm uma ou mais Animações e as controlam com base em métodos simples de transporte como Begin, Pause, Stop, Resume, Seek e SkipToFill. Os quatro primeiros são auto-explicativos (Começar, Pausar, Parar e Continuar), mas Seek significa mover para um ponto específico na linha do tempo e SkipToFill move a linha do tempo dos Storyboards para o final de seu período ativo. Os storyboards também têm propriedades que podem modificar radicalmente seu comportamento.

AutoReverse determina se um Storyboard deve ser reproduzido inversamente após concluir a iteração de avanço.

BeginTime pode ser usado para atrasar a reprodução.

Duration define a duração total do Storyboard.

FillBehavior determina se o novo valor deve ser mantido quando o storyboard for concluído.

RepeatBehavior determina quanto tempo ou quantas vezes o Storyboard deve ser repetido. Os storyboards têm um único evento chamado Completed que dispara quando todas as iterações de avanço, inversas e repetições do Storyboard tiverem sido executadas até o final. A manipulação desse evento permite decidir qual código executar, se houver algum, após a animação, o que poderia iniciar outro Storyboard. Ou você pode modificar o Storyboard e as Animações e executá-lo novamente.

Page 42: Adotando Silverlight 2.0 - v1 - Exercicios

42

Animações

Todas as Animações e Storyboards herdam da classe Timeline que fornece a funcionalidade baseada em tempo mencionada acima. Quando um Storyboard ou uma Animação iteram, quanto tempo, quantas vezes, etc. O que torna cada classe de Animação única é o tipo de propriedade animada e a interpolação entre os valores atuais e de destino. Primeiro vamos ver Animações simples que têm propriedades From, To e By.

From é opcional e define quando a animação começa o que o valor de partida é. Se a From não for definida o valor de partida será o valor atual da propriedade.

To define o valor final da propriedade.

By aumenta o valor da propriedade animada com seu valor. Por exemplo, se você tem um Rectangle com um Canvas.Left definido em 0 e o anima com By=50 e RepeatBehavior definido em 2x no final da Animação o Canvas.Left do Rectangle seria igual a 100.

Em uma animação simples, a interpolação entre os valores durante a transição é linear- uma mudança tranqüila e constante. Os três tipos de Animações simples incluem:

ColorAnimation Usando um exemplo de animação da cor de um Traço (Stroke) de Retângulo, você pode ver que a cor mudou de azul escuro para vermelho.

DoubleAnimation Neste exemplo o deslocamento do Gradiente Stop de azul mais escuro foi animado para dar uma sensação de sobreposição do Retângulo. PointAnimation Aqui o StartPoint e o EndPoint do LinearGradientBrush foram animados para dar um Preenchimento radicalmente diferente para o Retângulo.

Animações de Quadro Chave

As animações de quadro chave permitem definir múltiplos valores e controlar a interpolação entre valores iniciais e finais. Os KeyFrames (quadros chave) são adicionados à Animação e cada um define um Valor e um KeyTime usados durante a animação. A interpolação é controlada definindo a propriedade KeySpline que só está disponível no tipo SplineKeyFrame. Animações de Quadro Chave possibilitam animações muito mais orgânicas, envolvendo aceleração e desaceleração.

Page 43: Adotando Silverlight 2.0 - v1 - Exercicios

43

Cada tipo de animação simples tem um tipo de animação KeyFrame correspondente, incluindo ColorAnimationUsingKeyFrames, DoubleAnimationUsingKeyFrames, PointAnimationUsingKeyFrames e ObjectAnimationUsingKeyFrames. Em animações simples nós explicamos que a interpolação Linear é tranqüila e constante do valor inicial ao valor final. Quando você começa a usar AnimationUsingKeyFrames, pode tirar proveito da interpolação baseada em Spline. Aqui estão alguns exemplos para ilustrar o conceito de KeyFrames e KeySplines. Sinta-se à vontade para seguir estes exemplos no Blend ou no Visual Studio para ver as Animações se moverem em tempo real. A seguinte animação simples da propriedade Canvas.Left de nosso objeto OrangeShip mudará o valor de 0 para 500 em um segundo:

<Storyboard x:Name="SplineTest">

<DoubleAnimationUsingKeyFrames

Storyboard.TargetName="OrangeShip"

Storyboard.TargetProperty="(Canvas.Left)">

<SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>

<SplineDoubleKeyFrame KeyTime="00:00:01" Value="500"/>

</DoubleAnimationUsingKeyFrames>

</Storyboard>

Note que o KeySpline à direita (representado pela ferramenta visual do Blend) está definido com o valor padrão, que é equivalente à interpolação Linear. A linha verde abaixo representa a taxa de mudança conforme a nave desliza pela tela. Ela vai de forma suave e estável. Agora vamos modificar o KeySpline:

<Storyboard x:Name="SplineTest">

<DoubleAnimationUsingKeyFrames

Storyboard.TargetName="OrangeShip"

Storyboard.TargetProperty="(Canvas.Left)">

<SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>

<SplineDoubleKeyFrame KeyTime="00:00:01" Value="500">

<SplineDoubleKeyFrame.KeySpline>

<KeySpline ControlPoint1="1,0" ControlPoint2="1,1"/>

</SplineDoubleKeyFrame.KeySpline>

</SplineDoubleKeyFrame>

</DoubleAnimationUsingKeyFrames>

</Storyboard>

Page 44: Adotando Silverlight 2.0 - v1 - Exercicios

44

Com o KeySpline definido assim, a nave cobre pouco espaço no início mas acelera bastante até o final.

Vamos adicionar outro KeyFrame e mover o KeySpline:

<Storyboard x:Name="SplineTest">

<DoubleAnimationUsingKeyFrames

Storyboard.TargetName="OrangeShip"

Storyboard.TargetProperty="(Canvas.Left)">

<SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/>

<SplineDoubleKeyFrame KeyTime="00:00:01" Value="200">

<SplineDoubleKeyFrame.KeySpline>

<KeySpline ControlPoint1="0,1" ControlPoint2="1,1"/>

</SplineDoubleKeyFrame.KeySpline>

</SplineDoubleKeyFrame>

<SplineDoubleKeyFrame KeyTime="00:00:03" Value="500" />

</DoubleAnimationUsingKeyFrames>

</Storyboard>

A nave agora acelera até 200 e depois vai deslizando daí para 500. Embora seja um pouco mais

complexa de ver em XAML, Animações baseadas em Quadro Chave podem ser muito potentes e

bastante simples de criar no Blend.

Animações de Objeto

Mencionadas rapidamente acima, as Animações de Objeto são novas no Silverlight 2 beta 2 e permitem

definir valores de Objetos que não são Doubles, Colors ou Points. As Animações de Objeto só podem ser

usadas com KeyFrames e com a Interpolação Discrete. Discrete significa completamente alterada no

final da duração. Olhando o exemplo abaixo você pode entender o porquê. Seria muito difícil

(impossível) interpolar entre dois valores Enumeration (Enumeração).

Page 45: Adotando Silverlight 2.0 - v1 - Exercicios

45

Animar a propriedade Visibility é um caso de uso comum, especialmente quando você considera que ia

usar Opacity em vez de Visibility, mesmo com uma opacidade 0 os eventos de mouse ainda são

capturados.

<ObjectAnimationUsingKeyFrames

Storyboard.TargetName="OrangeShip"

Storyboard.TargetProperty="(FrameworkElement.Visibility)">

<DiscreteObjectKeyFrame KeyTime="00:00:01">

<DiscreteObjectKeyFrame.Value>

<Visibility>Collapsed</Visibility>

</DiscreteObjectKeyFrame.Value>

</DiscreteObjectKeyFrame>

</ObjectAnimationUsingKeyFrames>

Page 46: Adotando Silverlight 2.0 - v1 - Exercicios

46

Seção 2: Usando o Blend para Projetar

Animações

Ferramenta Visual que tem Suporte para a API

O Blend é a ferramenta visual para layout interatividade e criação de gráficos básicos para aplicações do

WPF e do Silverlight. As tecnologias WPF trazem novos paradigmas de programação para

desenvolvedores, mas também trazem inovações para os designers. No contexto da Animação, o Blend

fornece um conjunto bastante robusto de ferramentas. Abaixo está um passo a passo sobre como usar

cada uma das Ferramentas de Animação e como elas se relacionam ao conceito de API acima.

Abra o SimpleAnimation.sln no Blend e abra o Page.xaml.

Agora você tem um objeto interessante para manipular com um segundo plano bastante contrastante.

Criando uma animação

Selecione o objeto OrangeShip.

Encontre o painel Objetos e Linha do Tempo e

selecione o botão “+” para adicionar um novo

storyboard.

Dê ao Storyboard o nome que desejar.

A Interface do Blend muda um pouco quando um Storyboard é carregado.

Note que o Storyboard é exibido agora por nome na área superior do painel de Objetos.

A Linha do Tempo é mostrada à direita da árvore de Objetos.

Finalmente a superfície de design tem um contorno vermelho, notificando que você está em

modo de gravação. Edições no objeto serão agora acompanhadas no Storyboard.

Você pode ligar e desligar a gravação clicando no círculo vermelho, que vai alternar para cinza quando

estiver desligado.

O padrão do Expression Blend é usar KeyFrames, já que visualmente esse é o modo mais fácil de

projetar animações complexas. Olhando a Linha do Tempo você pode ver o playhead, que é a linha

vertical amarela com a seta no topo.

Page 47: Adotando Silverlight 2.0 - v1 - Exercicios

47

Acima do marcador de 0 segundos você verá o botão Record KeyFrame , com o OrangeShip

selecionado, clique nesse botão e grave um KeyFrame no marcador de 0 segundos.

Arraste o playhead para o marcador de 1 segundo.

Arraste o OrangeShip de onde está para um novo local.

Clique no botão Record KeyFrame novamente.

Acima da linha do tempo você encontrará controles de transporte que permitem pré-visualizar

sua animação. Clique no botão Play.

Bela animação! Muito suave e linear.

Inspecionando propriedades animadas

Você pode inspecionar quais propriedades está animando visualmente expandindo os objetos no Painel

de Objetos que têm o ícone vermelho selecionado. É claro que também pode mudar para a exibição

XAML ou abrir o arquivo no Visual Studio para ver as propriedades alvo.

Editando o storyboard

Se seu Storyboard não estiver mais aberto ou você quiser selecionar um diferente, clique no botão

que vai mostrar a lista dos Storyboards existentes. Com um Storyboard selecionado, você vai ver as

propriedades do Storyboard no Painel de Propriedades. Aqui você pode editar o AutoReverse e o

RepeatBehavior.

Editando KeySplines

Com um Storyboard aberto, selecione um KeyFrame na área da Linha do Tempo. Note que agora o

painel de Propriedades mudou para trazer acima o editor do KeySpline, bem como uma forma de inserir

um Valor para um KeyFrame. Desse modo, se você não puder arrastar algo para o ponto exato que

deseja, pode apenas digitar o valor exato.

Resumo

Em alto nível, a criação de Storyboards pode ser realizada simplesmente movendo o playhead para

tempos diferentes e modificando qualquer propriedade de seu objeto. Lembre-se que um Storyboard

pode conter mais que uma animação e pode ser reutilizado com alguma ajuda do code-behind.

Page 48: Adotando Silverlight 2.0 - v1 - Exercicios

48

Seção 3: Reutilizando Storyboards

Geração de Storyboard de Procedimento

A habilidade de criar e projetar storyboards visualmente dentro do Blend é muito potente, mas em

algum ponto você pode precisar entrar em seu código para manipular ou reutilizar o storyboard com

base em dados dinâmicos ou entrada do usuário. Vamos usar a Animação Simples para ver um exemplo

básico de como fazer isso.

Abra o SimpleAnimation.sln no Blend e abra o Page.xaml.cs.

Adicione o seguinte código ao seu Loaded EventHandler.

BasicAnimation = new Storyboard();

da = new DoubleAnimation();

da.To = 350;

da.Duration = TimeSpan.FromMilliseconds(800);

Storyboard.SetTarget(da, OrangeShip);

Storyboard.SetTargetProperty(da, new PropertyPath("(Canvas.Left)"));

BasicAnimation.Children.Add(da);

Aqui estamos usando código de procedimento para instanciar um novo Storyboard. Depois

instanciamos uma DoubleAnimation para animar a propriedade Canvas.Left do objeto OrangeShip para

350 pixels em 800 milissegundos.

Usando o código acima como um modelo, tente adicionar outra DoubleAnimation ao mesmo

Storyboard que anima a propriedade Canvas.Top do OrangeShip.

Agora faça a chamada para Begin BasicAnimation.

Execute a aplicação para ver o OrangeShip voar pela tela.

Reutilizando o Storyboard gerado

Agora vamos modificar e executar novamente o Storyboard com base na entrada do usuário.

Abra o Page.xaml ou Page.xaml.cs para adicionar um EventHandler a MouseLeftButtonDown.

Dentro do EventHandler, adicione o seguinte código:

da = BasicAnimation.Children[0] as DoubleAnimation;

da.To = e.GetPosition(LayoutRoot).X - OrangeShip.Width;

da = BasicAnimation.Children[1] as DoubleAnimation;

da.To = e.GetPosition(LayoutRoot).Y - OrangeShip.Height / 2;

BasicAnimation.Begin();

Page 49: Adotando Silverlight 2.0 - v1 - Exercicios

49

Como o Storyboard foi armazenado em uma variável de nível de classe, nós podemos manipulá-lo. Esse

código passa pela árvore de Filhos do Storyboard e redefine as propriedades To com base na posição do

mouse e no tamanho da nave.

Esse tipo de acesso a Storyboards possibilita uma maior flexibilidade, como a habilidade de modificar os

Storyboards existentes projetados dentro do Blend no tempo de execução para corresponder à sua

interação ou seus dados de tempo de execução.

Page 50: Adotando Silverlight 2.0 - v1 - Exercicios

50

Seção 4: Usando o DispatchTimer para

Animação de Procedimento

A construção do Storyboard é uma forma muito útil de animar sua interface de usuário, mas há alguns

casos em que você pode querer um evento de marcação de jogo para atualizar manualmente os objetos

usando seu próprio código. Abaixo você usará a classe DispatcherTimer para criar um efeito de chama

bastante simples para sua nave conduzida pela entrada do mouse.

No Loaded EventHandler, adicione o seguinte código:

DispatcherTimer timer = new DispatcherTimer();

timer.Interval = TimeSpan.FromMilliseconds(100);

timer.Tick += new EventHandler(timer_Tick);

timer.Start();

Essa é uma instanciação simples da classe DispatcherTimer. Uma vez que o método Start é chamado, o

evento Tick começa a disparar com base no Intervalo definido.

Adicione este código ao seu Tick EventHandler:

Canvas.SetLeft(Tail, ((Canvas.GetLeft(OrangeShip) - Canvas.GetLeft(Tail) –

16) / 2) + Canvas.GetLeft(Tail));

Canvas.SetTop(Tail, ((Canvas.GetTop(OrangeShip) - Canvas.GetTop(Tail) + 45) /

2) + Canvas.GetTop(Tail));

Agora o objeto Tail vai se reposicionar para ficar um pouco mais próximo à nave a cada Tick. Ao

executar a aplicação, você verá algo como a imagem abaixo. Note a saída da cauda azul atrás do motor.

Page 51: Adotando Silverlight 2.0 - v1 - Exercicios

51

C- Integração de Navegador do Silverlight

Tornando o Silverlight um cidadão de navegador de primeira classe

Introdução

O Silverlight 2 fornece uma funcionalidade que lhe permite ser um cidadão de primeira classe em uma

página HTML dentro do navegador. Ele possibilita uma série de recursos, incluindo (mas não limitado a):

Funções de código .NET podem ser expostas ao navegador e chamadas a partir do JavaScript

A árvore de renderização XAML pode ser exposta ao navegador e manipulada a partir de

JavaScript

O código JavaScript dentro do HTML pode ser chamado de dentro do code-behind do .NET

Neste Laboratório você vai explorar cada um desses recursos, e as classes dentro do namespace

System.Windows.Browser que habilita essa funcionalidade. Você vai trabalhar com os conceitos por

trás de cada um deles, usando o Microsoft Virtual Earth como base, antes de explorar como eles são

usados dentro da aplicação de exemplo, a Margie’s Travel.

As aplicações de exemplo são chamadas de Sample 1, Sample 2 e Sample 3. Elas têm cópias com os

sufixos ‘Start’ e ‘Completed’. Você deve começar com as versões ‘Start’ e adicionar código a elas

conforme as instruções. Se tiver problemas, as versões de trabalho concluídas são fornecidas.

Page 52: Adotando Silverlight 2.0 - v1 - Exercicios

52

Seção 1 – Expondo Funções .NET ao

Navegador.

Dentro do diretório dos Laboratórios você encontrará um projeto chamado Sample 1. Abra a versão do

diretório Sample 1 – Completed e execute-a. Você verá uma tela como a da Figura 1. O objeto bege no

topo à esquerda é um objeto do Silverlight, abaixo dele estão três controles <Input> HTML e à direita

está um mapa do Virtual Earth. Se pressionar os botões, o conteúdo do Silverlight será atualizado com

as cidades do país selecionado.

Nós vamos construir a partir desse exemplo conforme avançarmos, então feche esse projeto e abra o do

diretório Sample 1 – Start.

Figura 1. Executando o Projeto Sample 1

Entendendo o XAML e o Código .NET do Silverlight

O conteúdo do Silverlight na Figura 1 usa XAML que contém um ItemsControl que contém um TextBlock,

que está ligado ao nome de uma cidade (‘CityName’).

Page 53: Adotando Silverlight 2.0 - v1 - Exercicios

53

<ItemsControl x:Name="_cities">

<ItemsControl.ItemTemplate>

<DataTemplate>

<TextBlock FontSize="14" Height="30" Text="{Binding CityName}" />

</DataTemplate>

</ItemsControl.ItemTemplate>

</ItemsControl>

O Code-Behind contém uma Classe usada para representar Cidades, e contém um elemento membro

chamado CityName.

public class CityData

{

public string CityName { get; set; }

public double Latitude{get;set;}

public double Longitude{get;set;}

public CityData(string strCityName, double nLatitude,

double nLongitude)

{

CityName = strCityName;

Latitude = nLatitude;

Longitude = nLongitude;

}

}

Observação: Os valores Latitude e Longitude serão usados em um outro exemplo.

A função de membro getCities vai retornar um List<CityData> contendo várias cidades de um

determinado país. Isso é embutido em código para fins de demonstração.

Veja um exemplo da construção de cidades para o ‘uk’.

switch (strCountry)

{

case "uk":

{

ret.Add(new CityData("London", 51.5, 0));

ret.Add(new CityData("Stratford-Upon-Avon", 52.3, -1.71));

ret.Add(new CityData("Edinburgh", 55.95, -3.16));

break;

}

Para ligar esses resultados ao ItemsControl, basta construir o List<CityData> e defini-lo de acordo

com a propriedade ItemsSource do ItemsControl. (‘_cities’ é o nome do ItemsControl)

Aqui está uma função que consegue isso. Adicione-a à página de code-behind do Page.xaml.cs:

public void upDateCities(string strCountry)

{

myCities = getCities(strCountry);

Page 54: Adotando Silverlight 2.0 - v1 - Exercicios

54

_cities.ItemsSource = myCities;

}

Essa função está disponível para o código .NET, mas como podemos expor ao navegador? Você verá

isso na próxima seção.

Expondo um método .NET ao JavaScript

Para expor seu código .NET ao navegador, você primeiro terá que fazer referência ao

System.Windows.Browser dentro de seu código no topo de seu code behind.

using System.Windows.Browser;

Em seguida você terá que adicionar um evento Loaded e um manipulador de eventos à sua página. Você

pode fazer isso dentro do construtor Page(), portanto adicione esta linha ao construtor Page():

this.Loaded += new RoutedEventHandler(Page_Loaded);

Agora você vai implementar o manipulador de eventos Page_Loaded e usá-lo para registrar seu controle

do Silverlight como um objeto “scriptável”. Nesse caso você vai registrar o objeto scriptable (não o

confunda com o objeto do Silverlight propriamente dito) como MySilverlightObject

Talvez você tenha notado, enquanto estava digitando a linha anterior, que o Visual Studio podia gerar

automaticamente um manipulador de eventos Page_Loaded para você. Se ele o fez, edite-o para ficar

como o código abaixo, caso contrário apenas adicione este código ao code-behind de seu Page.xaml.cs.

void Page_Loaded(object sender, RoutedEventArgs e)

{

HtmlPage.RegisterScriptableObject("MySilverlightObject", this);

}

Quando tiver feito isso, você poderá registrar seus métodos Public para serem “scriptáveis”, usando o

atributo [ScriptableMember]. Veja como expor o método upDateCities que vimos anteriormente

para que ele possa ser chamado do navegador, então lembre-se de adicionar o atributo, assim:

[ScriptableMember]

public void upDateCities(string strCountry)

{

myCities = getCities(strCountry);

_cities.ItemsSource = myCities;

}

Page 55: Adotando Silverlight 2.0 - v1 - Exercicios

55

Na próxima seção você verá como chamar isso a partir do JavaScript.

Chamando o Método .NET a partir do JavaScript.

A primeira coisa da qual deve se certificar é se o objeto do Silverlight tem um ID. Isso é necessário para

que o JavaScript possa ter uma referência a ele.

<object data="data:application/x-silverlight,"

type="application/x-silverlight-2-b2"

width="300" height="400" id="slControl">

...

</object>

Agora o JavaScript pode ter uma referência ao seu controle do Silverlight com base neste ID:

var slPlugin = document.getElementById("slControl");

Para chamar o método baseado em .NET você usa a sintaxe

<PluginID>.content.<ScriptableObjectName>.method(parameters)

Neste caso ela fica assim:

slPlugin.content.MySilverlightObject.upDateCities("uk");

No Projeto Sample 1 você precisará de uma função do JavaScript chamada doCities que pega um

parâmetro e o usa para chamar o código .NET. Aqui está a função - adicione-a a um bloco do script em

Sample1TestPage.html dentro do projeto Sample1Web:

function doCities(country)

{

var slPlugin = document.getElementById("slControl");

slPlugin.content.MySilverlightObject.upDateCities(country);

}

Os botões de Entrada de HTML chamam isso e passam o devido país. Aqui está sua declaração, você

pode vê-la no final do Sample1TestPage.html:

<input id="bUK" type="button" value="uk" onclick="doCities('uk');" />

<input id="bGermany" type="button" value="germany"

onclick="doCities('germany');"/>

Page 56: Adotando Silverlight 2.0 - v1 - Exercicios

56

<input id="bFrance" type="button" value="france" onclick="doCities('france');"

/>

O resultado final é que quando o usuário pressiona um botão de HTML, a função doCities do JavaScript é

executada. Isso chama o objeto scriptável do Silverlight passando a ele o parâmetro que recebe. Agora o

código .NET assume e usa esse parâmetro para criar uma List<CityData> de cidades para o país

selecionado, que será então ligada ao Items Presenter.

Page 57: Adotando Silverlight 2.0 - v1 - Exercicios

57

Seção 2 – Manipulando a Árvore de

Renderização XAML a partir do Navegador

No exemplo anterior você viu como pode acessar o código .NET dentro do JavaScript, mas além disso

você também pode manipular o XAML que o Silverlight renderiza usando o JavaScript no navegador.

Você tem várias opções para fazer isso.

Atualizando elementos XAML existentes

Primeiro, se você tem um elemento XAMl existente, pode encontrá-lo usando o método findName do

controle do Silverlight, e definir seus conteúdos.

Abra o projeto Sample2 do diretório Sample2 – Completed e execute-o. Você pode ver isso na Figura 2.

Figura 2. O Projeto Sample 2

Page 58: Adotando Silverlight 2.0 - v1 - Exercicios

58

Você pode ver que isso atualiza o Sample 1 adicionando o nome do país ao topo do conteúdo do

Silverlight. Feche isso e abra o projeto Sample2 – Start, que você usará para trabalhar.

O XAML precisa ser atualizado com um novo TextBlock, então adicione o XAML destacado ao Page.xaml

no lugar correto.

<UserControl x:Class="Sample1.Page"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

FontFamily="Trebuchet MS" FontSize="11"

Width="300" Height="400">

<Grid x:Name="LayoutRoot" Background="Beige">

<StackPanel x:Name="stk">

<TextBlock FontSize="40" Foreground="Red"

x:Name="txtCountry">Country</TextBlock>

<ItemsControl x:Name="_cities">

</ItemsControl>

</StackPanel>

</Grid>

</UserControl>

No JavaScript, se você tiver uma referência ao objeto do Silverlight chamado slControl, pode corrigir os

conteúdos do TextBlock usando o seguinte JavaScript:

slPlugin.content.findName("txtCountry").text = "germany";

Dê uma olhada no Sample2TestPage.html, e encontre a função do JavaScript chamada ‘doCities’. Você

verá que ela está vazia, então atualize-a com o seguinte código:

function doCities(country)

{

var slPlugin = document.getElementById("slControl");

slPlugin.content.MySilverlightObject.upDateCities(country);

slPlugin.content.findName("txtCountry").text = country;

}

Agora o efeito é que quando você pressiona os botões de HTML, a lista de cidades do Silverlight se

atualiza com as cidades desse país, e o título do país é definido de acordo.

Criando novos Elementos XAML

O Silverlight não se restringe ao XAML que é definido como parte do projeto Visual Studio / Blend. Ele

pode ser adicionado dinamicamente no tempo de execução dentro da ferramenta do JavaScript. Neste

exemplo simples você verá como fazer isso.

Page 59: Adotando Silverlight 2.0 - v1 - Exercicios

59

Se você consultar a listagem do XAML anterior, verá que o StackPanel que contém a lista de cidades é

chamada de ‘stk’. Você vai adicionar o novo XAML a esse elemento como filhos desse elemento.

Veja um exemplo:

var xamlFragment =

'<TextBlock FontSize="20" Foreground="Blue">' + Date() + '</TextBlock>';

tb = slPlugin.content.createFromXaml(xamlFragment,false);

slPlugin.content.findName("stk").children.add(tb);

Você pode adicionar esse código à função doCities do outro exemplo, e sempre que o usuário clicar no

botão um novo TextBlock do XAML será adicionado ao StackPanel, e esse TextBlock vai conter a Data e o

Horário atuais. Aqui está o código que faz isso:

function doCities(country)

{

var slPlugin = document.getElementById("slControl");

slPlugin.content.MySilverlightObject.upDateCities(country);

slPlugin.content.findName("txtCountry").text = country;

var xamlFragment =

'<TextBlock FontSize="20" Foreground="Blue">' + Date() + '</TextBlock>';

tb = slPlugin.content.createFromXaml(xamlFragment,false);

slPlugin.content.findName("stk").children.add(tb);

}

O resultado dessa execução e de clicar em vários botões é mostrado na Figura 3.

Figura 3. Adicionando novo XAML

Page 60: Adotando Silverlight 2.0 - v1 - Exercicios

60

Lembre-se de que não é um XAML existente que você está manipulando. Você está adicionando um

novo XAML à árvore de renderização no tempo de execução. Isso torna o Silverlight bastante flexível

para atender as necessidades de suas aplicações conectadas.

Mais Estudo

Além de manipular XAML existente e adicionar novo XAML, você também pode remover o XAML.

Quando tiver uma referência a um elemento de container (como ‘stk’), você pode usar Remove ou

RemoveAt para remover elementos. Além disso, pode fazer referência a itens nos filhos de um container

usando getItem(index). Aqui há um bom material: http://msdn.microsoft.com/en-

us/library/bb980118(VS.95).aspx

Page 61: Adotando Silverlight 2.0 - v1 - Exercicios

61

Seção 3 – Chamando o Script do

Navegador a partir de .NET

Agora nós vimos como você pode chamar o código .NET a partir do JavaScript dentro do navegador –

mas e a outra maneira? Nesta seção você verá como fazer exatamente isso. Como exemplo, o SDK do

Virtual Earth é baseado em JavaScript, então, em vez de construir seu próprio host do Silverlight para o

Virtual Earth, é muito mais fácil fazer o Silverlight chamar o navegador e o JavaScript chamar os serviços

do Virtual Earth em seu nome.

Carregue o projeto ‘Sample3’ do diretório Sample3 – Completed e execute-o. Anteriormente você viu

que pressionar os botões de HTML que representam os diferentes países fará com que as cidades desses

países sejam carregadas para o conteúdo do Silverlight. O projeto Sample3 contribui para isso – quando

você seleciona o nome de uma cidade (lembre-se disso na área do Silverlight), será feita uma chamada

para o navegador, para uma função do JavaScript que localiza a cidade selecionada no mapa com base

em sua latitude e longitude.

Você pode ver isso na Figura 4.

Page 62: Adotando Silverlight 2.0 - v1 - Exercicios

62

Figura 4. Chamando o navegador a partir do Silverlight para usar o SDK do Virtual Earth

Feche esse projeto e abra o projeto Sample3 – Start, que você usará para construir a funcionalidade

completa.

Primeiro, note que o XAML precisa ser atualizado para que o ItemsControl se ligue a CityName,

Longitude e Latitude. Os últimos elementos ficam ‘ocultos’ se você definir sua altura em ‘0’. Você terá

que atualizar o XAML para adicionar o seguindo código ao DataTemplate.

Veja como seu XAML deve ficar:

<ItemsControl x:Name="_cities">

<ItemsControl.ItemTemplate>

<DataTemplate>

<StackPanel Orientation="Horizontal"

MouseLeftButtonUp="StackPanel_MouseLeftButtonUp">

<TextBlock FontSize="14" Height="30" Text="{Binding CityName}" />

<TextBlock Height="0" Text="{Binding Latitude}" />

<TextBlock Height="0" Text="{Binding Longitude}" />

</StackPanel>

</DataTemplate>

</ItemsControl.ItemTemplate>

</ItemsControl>

Note também que o manipulador de eventos de Mouse é conectado ao StackPanel que contém os

elementos TextBlock. Isso significa que o evento será capturado pelo container, e os valores de cada um

de seus filhos podem ser derivados, permitindo receber o Name, a Latitude e a Longitude da cidade

atual.

Vamos dar uma olhada nesse manipulador de eventos. Adicione este código ao Page.xaml.cs quando

estiver trabalhando.

Primeiro, vamos converter o ‘sender’ a um StackPanel, e seus filhos a TextBlocks:

private void StackPanel_MouseLeftButtonUp(object sender,

MouseButtonEventArgs e)

{

StackPanel s = sender as StackPanel;

TextBlock t0 = s.Children[0] as TextBlock;

TextBlock t1 = s.Children[1] as TextBlock;

TextBlock t2 = s.Children[2] as TextBlock;

Agora podemos receber o nome da cidade, sua latitude e longitude a partir destes objetos:

strCurrentCity = t0.Text;

nCurrentLatitude = Convert.ToDouble(t1.Text);

nCurrentLongitude = Convert.ToDouble(t2.Text);

É aqui que começa a diversão. Lembre-se primeiro de adicionar uma referência a System.Windows.Browser no topo da página de código. Isso nos dá uma referência às classes de

Page 63: Adotando Silverlight 2.0 - v1 - Exercicios

63

que precisaremos: A classe HtmlPage nos permite ter uma referência a uma função do JavaScript invocando seu método Window.GetProperty e passando a ele o nome da função. Isso tem que ser convertido como um ScriptObject para poder ser chamado:

ScriptObject myScriptMethod =

(ScriptObject) HtmlPage.Window.GetProperty("plotCity");

Agora você pode chamar a função usando InvokeSelf e passando a ela os

parâmetros:

myScriptMethod.InvokeSelf(nCurrentLatitude, nCurrentLongitude);

}

É claro que a função plotCity tem que existir na página, e tem que corresponder à assinatura que está

sendo usada para chamá-la.

Aqui está a função do JavaScript que pega a latitude e a longitude e centraliza o mapa do Virtual Earth

conforme essas coordenadas. Adicione isso ao Sample3TestPage.html:

function plotCity(latitude, longitude)

{

tourMap.LoadMap(new VELatLong(latitude, longitude),8);

}

*Note que ‘8’ é o nível de zoom. Veja o SDK do Virtual Earth para mais detalhes:

http://dev.live.com/virtualearth/sdk/]

Mais Estudo

Além de chamar Funções do JavaScript, você também pode manipular elementos HTML.

Você faz isso atribuindo um HtmlElement aos resultados de uma chamada de função

HtmlPage.Document.GetElementByID(‘elementName’).

Depois você pode manipular isso chamando o método ‘setAttibute’ da instância de HtmlElement

passando a ele um nome e um valor de atributo como esse element.setAttribute(“attributeName”,

value).

Page 64: Adotando Silverlight 2.0 - v1 - Exercicios

64

D- Personalizando a Aparência

Modelagem de Controle, Styling (Estilo), Visual State Manager, controles de

Subclasse.

Introdução

O Silverlight oferece muitas opções diferentes para personalizar sua interface de usuário e torná-la

atraente:

Você pode simplesmente definir propriedades em cada controle (usando código ou atributos

XAML) e ter um nível básico de personalização.

Você também pode usar o Styling, que oferece o mesmo nível de personalização (definição de

valores em propriedades existentes) de modo mais reutilizável e amigável para o designer.

Finalmente, a modelagem de controle o libera para redefinir a aparência de um controle

substituindo suas partes.

Todos esses recursos podem ser combinados e correspondidos para ter a maior flexibilidade e

maximizar a reutilização de seus ativos.

Objetivos do Laboratório:

Neste laboratório você vai aprender sobre recursos, estilo e modelagem de controle. Vamos criar um

controle deslizante personalizado para a aplicação Voyager.

Page 65: Adotando Silverlight 2.0 - v1 - Exercicios

65

Exercício 1: Criando o controle deslizante do Voyager

Neste exercício você vai usar o Expression Blend para criar um controle deslizante com certa

personalização para ser usado no Voyager.

O controle deslizante tem esta aparência:

Aqui estão as personalizações do controle

deslizante:

n

No Exercício 1, você vai usar o Expression Blend para desativar os RepeatButtons e criar a aparência

personalizada para o elevador. Você não vai escrever nenhum código neste exercício.

Page 66: Adotando Silverlight 2.0 - v1 - Exercicios

66

Tarefa 1: Montando seu controle deslizante

1. Inicie o Expression Blend e crie uma nova Aplicação do Silverlight 2. O nome do projeto não

importa, já que você vai criar XAML e pode então recortar e colar na aplicação do Voyager.

2. Arraste e solte um controle deslizante na superfície de design de nosso Page.xaml.

[Dica: Você pode encontrar o controle deslizante (Slider) na Caixa de Ferramentas, na mesma

seção em que estão Button e outros controles, ou selecionando o botão ‘>>’ na parte inferior da

Caixa de Ferramentas e depois ‘Slider’ na biblioteca de ativos].

3. Clique com o botão direito em seu controle deslizante e selecione ‘Edit a Copy’ para Editar o

Modelo.

Page 67: Adotando Silverlight 2.0 - v1 - Exercicios

67

Editar o Modelo dessa forma nos permitirá personalizar o Modelo de Controle padrão para

controle deslizante (podemos adicionar/remover partes, mudar as transições de estado, etc.).

A caixa de diálogo Edit Template vai pedir o nome e um escopo para nosso modelo.

4. Digite VoyagerSlider no nome.

5. Deixe “This document” marcado como o alvo para essa definição.

Significa que o estilo (gerado pelo Blend) e o Modelo desse controle só estarão disponíveis

dentro do Page.xaml. Se tivéssemos escolhido App.xaml, ele estaria disponível em qualquer

Página da aplicação.

Page 68: Adotando Silverlight 2.0 - v1 - Exercicios

68

6. Clique na exibição SplitView ou XAML para analisar o que o Blend acabou de fazer por você:

O Blend extraiu o Estilo padrão (e o Modelo de Controle para o Controle Deslizante) e o colocou na

coleção UserControl.Resources para que você possa reutilizá-lo dentro da página.

Page 69: Adotando Silverlight 2.0 - v1 - Exercicios

69

Tarefa 2: Personalizando o controle deslizante

Se você olhar o ControlTemplate para controle deslizante, verá que há dois Grids principais dentro do

modelo: “HorizontalTemplate” e “VerticalTemplate”. Essas grades encapsulam a aparência padrão do

controle deslizante quando ele está na respectiva Orientação. Este exercício vai focar apenas na

personalização do Modelo Horizontal (Horizontal Template).

O HorizontalTemplate tem:

dois controles RepeatButton,

um Rectangle (como faixa) e

Um Thumb (Elevador).

Se você estiver na exibição de Design, pode vê-los na exibição de Objetos e Linha do Tempo – assim:

No controle deslizante do Voyager, não há nenhuma faixa visível, então comece tornando a “Track”

(Faixa) transparente.

1. Modifique o elemento [Rectangle]para que seu Traço e Preenchimento fiquem transparentes

[Dica, você faz isso no Blend clicando com o botão direito no editor de pincel e selecionando

“Reset”+

Agora, desative os RepeatButtons para que eles não interfiram em nossos controles deslizantes

sobrepostos.

2. Selecione HorizontalTrackLargeDecreaseRepeatButton na Guia Objects and Timeline

Após selecioná-lo, a janela de propriedades mostra todas as propriedades para

HorizontalTrackLargeDecreaseRepeatButton.

Page 70: Adotando Silverlight 2.0 - v1 - Exercicios

70

3. Mude a propriedade IsHitTestVisible para “False” (ou Unchecked).

[Dica: você pode usar a Janela de Pesquisa na Guia de propriedades; digite “IsH” (menos as

aspas) e o Filtro vai trazer IsHitTestVisible].

Você está mudando IsHitTestVisible para falso em oposição a configurar a Visibilidade como

recolhida porque a Visibilidade afeta o layout e o Controle Deslizante está alongando/reduzindo

o botão para alterar a posição do Elevador.

Você não mudou “IsEnabled” para falso apenas por causa de um problema conhecido na beta2.

Para o RTM, IsEnabled deve resolver o problema.

4. Agora selecione HorizontalTrackLargeDecreaseRepeatButton e mude IsHitTestVisible para

“False” também.

Ótimo! Agora nosso trabalho no controle deslizante está pronto, mas precisamos modificar o

modelo do Elevador dentro do controle deslizante.

Page 71: Adotando Silverlight 2.0 - v1 - Exercicios

71

Tarefa 3: Personalizando o Elevador

Agora vamos personalizar o modelo do Elevador no controle deslizante.

5. Selecione HorizontalThumb no HorizontalTemplate e Edit the Template.

6. Quando nome e local forem solicitados, insira “SliderThumbStyle” no nome e o escopo para

definição está dentro de This document.

Page 72: Adotando Silverlight 2.0 - v1 - Exercicios

72

Note como o Modelo do elevador tem muitos elementos: ThumbOurterBorderFill,

ThumbOuterRoundBorder, etc. Em nosso caso, queremos uma aparência completamente

diferente/nova, então podemos excluir todos eles.

7. Selecione todos os elementos em VoyagerThumbStyle e exclua todos eles, exceto o Grid na Raiz.

Agora, você pode definir sua própria aparência para o elevador.

8. Pegue a ferramenta Pen no Blend e desenhe o formato para seu elevador.

[Dica, certifique-se de que tem o elemento [Grid] selecionado quando pegar a Pen, pois você

desenhará na grade].

Use a criatividade (sinta-se à vontade para fazer um triângulo, uma estrela, o que quiser). O

elevador padrão no Voyager é assim:

Page 73: Adotando Silverlight 2.0 - v1 - Exercicios

73

OBSERVAÇÃO: Para tornar meu desenho mais fácil, usei o zoom de 800%, tinha Snaplines sendo

exibidos e “Snap to grid” estava ativo também.

Page 74: Adotando Silverlight 2.0 - v1 - Exercicios

74

Tarefa 4: Testando nosso controle deslizante.

Essas são todas as alterações necessárias para personalizar o Controle Deslizante e o Elevador no Voyager. Agora vamos ver se funciona.

9. Dentro do Blend, clique em Test Solution (no Menu Project) para ver como é seu controle deslizante. Não se preocupe se não for muita coisa, ele vai se integrar bem ao controle de linha do tempo de seu Voyager.

Tarefa 5: Exercícios para o usuário.

Durante as Tarefas 1-4 você se concentrou no Modelo Horizontal do controle deslizante. Você deve

voltar e aplicar mudanças similares ao modelo vertical.

Resumo desse Exercício:

Embora não tenha escrito nenhum código, você redefiniu a aparência do elevador e alterou

radicalmente a aparência e o comportamento do controle deslizante. Você não escreveu código nesse

exercício, cada mudança foi feita com o Blend de uma forma amigável para o designer.

Page 75: Adotando Silverlight 2.0 - v1 - Exercicios

75

Apêndices

Exercício 1: Respostas das Tarefas

Tarefa 2: Personalizando o controle deslizante.

Seu estilo dentro da seção UserControl.Resources deve ficar assim:

<Style x:Key="VoyagerSlider" TargetType="Slider">

<Setter Property="Template">

<Setter.Value>

<ControlTemplate TargetType="Slider">

<Grid x:Name="Root">

<Grid.Resources>

<ControlTemplate

x:Key="RepeatButtonTemplate">

<Grid x:Name="Root"

Background="Transparent" Opacity="0"/>

</ControlTemplate>

</Grid.Resources>

<vsm:VisualStateManager.VisualStateGroups>

<vsm:VisualStateGroup

x:Name="CommonStates">

<vsm:VisualStateGroup.Transitions>

<vsm:VisualTransition Duration="0"/>

</vsm:VisualStateGroup.Transitions>

<vsm:VisualState

x:Name="Normal"/>

<vsm:VisualState

x:Name="MouseOver"/>

<vsm:VisualState

x:Name="Disabled">

<Storyboard>

<DoubleAnimationUsingKeyFrames Storyboard.TargetName="Root"

Storyboard.TargetProperty="(UIElement.Opacity)">

<SplineDoubleKeyFrame KeyTime="00:00:00" Value="0.5"/>

</DoubleAnimationUsingKeyFrames>

</Storyboard>

</vsm:VisualState>

</vsm:VisualStateGroup>

<vsm:VisualStateGroup

x:Name="FocusStates">

Page 76: Adotando Silverlight 2.0 - v1 - Exercicios

76

<vsm:VisualStateGroup.Transitions>

<vsm:VisualTransition Duration="0"/>

</vsm:VisualStateGroup.Transitions>

<vsm:VisualState

x:Name="Unfocused"/>

<vsm:VisualState

x:Name="Focused">

<Storyboard>

<DoubleAnimationUsingKeyFrames Storyboard.TargetName="FocusVisual"

Storyboard.TargetProperty="(UIElement.Opacity)">

<SplineDoubleKeyFrame KeyTime="00:00:00" Value="1"/>

</DoubleAnimationUsingKeyFrames>

</Storyboard>

</vsm:VisualState>

</vsm:VisualStateGroup>

</vsm:VisualStateManager.VisualStateGroups>

<Grid

x:Name="HorizontalTemplate">

<Grid.ColumnDefinitions>

<ColumnDefinition

Width="Auto"/>

<ColumnDefinition

Width="Auto"/>

<ColumnDefinition

Width="*"/>

</Grid.ColumnDefinitions>

<Rectangle Height="3"

Margin="5,0,5,0" Grid.Column="0" Grid.ColumnSpan="3" StrokeThickness="0.5"/>

<RepeatButton

IsTabStop="False" Template="{StaticResource RepeatButtonTemplate}"

x:Name="HorizontalTrackLargeChangeDecreaseRepeatButton" Grid.Column="0"

IsHitTestVisible="False"/>

<Thumb Height="18"

x:Name="HorizontalThumb" Width="11" Grid.Column="1"/>

<RepeatButton

IsTabStop="False" Template="{StaticResource RepeatButtonTemplate}"

x:Name="HorizontalTrackLargeChangeIncreaseRepeatButton" Grid.Column="2"

IsHitTestVisible="False"/>

</Grid>

<Grid x:Name="VerticalTemplate"

Visibility="Collapsed">

<Grid.RowDefinitions>

<RowDefinition

Height="*"/>

<RowDefinition

Height="Auto"/>

<RowDefinition

Height="Auto"/>

</Grid.RowDefinitions>

<Rectangle

Margin="0,5,0,5" Width="3" Grid.Row="0" Grid.RowSpan="3" Fill="#FFE6EFF7"

Stroke="Black" StrokeThickness="0.5"/>

<RepeatButton

IsTabStop="False" Template="{StaticResource RepeatButtonTemplate}"

x:Name="VerticalTrackLargeChangeDecreaseRepeatButton" Grid.Row="2"/>

Page 77: Adotando Silverlight 2.0 - v1 - Exercicios

77

<Thumb Height="11"

x:Name="VerticalThumb" Width="18" Grid.Row="1"/>

<RepeatButton

IsTabStop="False" Template="{StaticResource RepeatButtonTemplate}"

x:Name="VerticalTrackLargeChangeIncreaseRepeatButton" Grid.Row="0"/>

</Grid>

<Rectangle x:Name="FocusVisual"

Stroke="#666666" StrokeDashArray=".2 5" StrokeDashCap="Round"

IsHitTestVisible="false" Opacity="0"/>

</Grid>

</ControlTemplate>

</Setter.Value>

</Setter>

</Style>

Tarefa 3: Personalizando o Elevador

Aqui está o UserControl.Resources com o Elevador e com o Controle Deslizante.

<UserControl.Resources>

<Style x:Key="VoyagetThumbStyle" TargetType="Thumb">

<Setter Property="Template">

<Setter.Value>

<ControlTemplate TargetType="Thumb">

<Grid>

<Grid.Resources>

<SolidColorBrush

x:Key="Background" Color="#FF003255"/>

<Color

x:Key="ThumbOuterBorderFillColor1">#FFF9FAFA</Color>

<Color

x:Key="ThumbOuterBorderFillColor2">#FFEDF1F4</Color>

<Color

x:Key="ThumbOuterBorderFillColor3">#FFE2E8EF</Color>

<Color

x:Key="ThumbOuterBorderFillColor4">#FFAFB9C1</Color>

<SolidColorBrush

x:Key="ThumbInnerRoundBorderBrush" Color="#FFFFFFFF"/>

<SolidColorBrush

x:Key="ThumbOuterRoundBorderBrush" Color="#FF000000"/>

<Color

x:Key="ThumbInnerBorderFillColor1">#CDFFFFFF</Color>

<Color

x:Key="ThumbInnerBorderFillColor2">#45FFFFFF</Color>

<Color

x:Key="MouseOverThumbOuterBorderFillColor1">#FFEAF0F0</Color>

<Color

x:Key="MouseOverThumbOuterBorderFillColor2">#FFDCE5EC</Color>

<Color

x:Key="MouseOverThumbOuterBorderFillColor3">#FFD5DDE6</Color>

<Color

x:Key="MouseOverThumbOuterBorderFillColor4">#FF798893</Color>

<Color

x:Key="MouseOverThumbInnerBorderFillColor1">#CDFFFFFF</Color>

<Color

x:Key="MouseOverThumbInnerBorderFillColor2">#45FFFFFF</Color>

<Color

x:Key="PressedThumbOuterBorderFillColor1">#FFEAF0F0</Color>

<Color

x:Key="PressedThumbOuterBorderFillColor2">#FFDCE5EC</Color>

Page 78: Adotando Silverlight 2.0 - v1 - Exercicios

78

<Color

x:Key="PressedThumbOuterBorderFillColor3">#FFD5DDE6</Color>

<Color

x:Key="PressedThumbOuterBorderFillColor4">#FF798893</Color>

<Color

x:Key="PressedThumbInnerBorderFillColor1">#CDFFFFFF</Color>

<Color

x:Key="PressedThumbInnerBorderFillColor2">#45FFFFFF</Color>

<Color

x:Key="DisabledThumbOuterBorderFillColor1">#FFF9FAFA</Color>

<Color

x:Key="DisabledThumbOuterBorderFillColor2">#FFEDF1F4</Color>

<Color

x:Key="DisabledThumbOuterBorderFillColor3">#FFE2E8EF</Color>

<Color

x:Key="DisabledThumbOuterBorderFillColor4">#FFC3C9CD</Color>

<Color

x:Key="DisabledThumbInnerBorderFillColor1">#CDFFFFFF</Color>

<Color

x:Key="DisabledThumbInnerBorderFillColor2">#45FFFFFF</Color>

</Grid.Resources>

<vsm:VisualStateManager.VisualStateGroups>

<vsm:VisualStateGroup

x:Name="CommonStates">

<vsm:VisualStateGroup.Transitions>

<vsm:VisualTransition Duration="0:0:0.1" To="MouseOver"/>

<vsm:VisualTransition Duration="0:0:0.1" To="Pressed"/>

<vsm:VisualTransition Duration="0:0:0.1" To="Disabled"/>

</vsm:VisualStateGroup.Transitions>

<vsm:VisualState

x:Name="Normal"/>

<vsm:VisualState

x:Name="MouseOver">

<Storyboard/>

</vsm:VisualState>

<vsm:VisualState

x:Name="Pressed">

<Storyboard/>

</vsm:VisualState>

<vsm:VisualState

x:Name="Disabled">

<Storyboard/>

</vsm:VisualState>

</vsm:VisualStateGroup>

</vsm:VisualStateManager.VisualStateGroups>

<Path

HorizontalAlignment="Stretch" Margin="-0.625,-0.5,-0.75,0.125"

VerticalAlignment="Stretch" Stretch="Fill" StrokeThickness="1" Data="M-

0.12461852,-0.0002709807 L11.250342,-0.0002709807 L11.249977,9.5004301

L6.0003605,17.375883 L0.00038105118,9.7503767 z" Stroke="#FF696A6E">

<Path.Fill>

<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">

<GradientStop

Color="#FF53050E" Offset="1"/>

Page 79: Adotando Silverlight 2.0 - v1 - Exercicios

79

<GradientStop

Color="#FFFFFFFF" Offset="0.002"/>

</LinearGradientBrush>

</Path.Fill>

</Path>

</Grid>

</ControlTemplate>

</Setter.Value>

</Setter>

</Style>

<Style x:Key="VoyagerSlider" TargetType="Slider">

<Setter Property="Template">

<Setter.Value>

<ControlTemplate TargetType="Slider">

<Grid x:Name="Root">

<Grid.Resources>

<ControlTemplate

x:Key="RepeatButtonTemplate">

<Grid x:Name="Root"

Background="Transparent" Opacity="0"/>

</ControlTemplate>

</Grid.Resources>

<vsm:VisualStateManager.VisualStateGroups>

<vsm:VisualStateGroup

x:Name="CommonStates">

<vsm:VisualStateGroup.Transitions>

<vsm:VisualTransition Duration="0"/>

</vsm:VisualStateGroup.Transitions>

<vsm:VisualState

x:Name="Normal"/>

<vsm:VisualState

x:Name="MouseOver"/>

<vsm:VisualState

x:Name="Disabled">

<Storyboard>

<DoubleAnimationUsingKeyFrames Storyboard.TargetName="Root"

Storyboard.TargetProperty="(UIElement.Opacity)">

<SplineDoubleKeyFrame KeyTime="00:00:00" Value="0.5"/>

</DoubleAnimationUsingKeyFrames>

</Storyboard>

</vsm:VisualState>

</vsm:VisualStateGroup>

<vsm:VisualStateGroup

x:Name="FocusStates">

<vsm:VisualStateGroup.Transitions>

<vsm:VisualTransition Duration="0"/>

</vsm:VisualStateGroup.Transitions>

<vsm:VisualState

x:Name="Unfocused"/>

<vsm:VisualState

x:Name="Focused">

<Storyboard>

Page 80: Adotando Silverlight 2.0 - v1 - Exercicios

80

<DoubleAnimationUsingKeyFrames Storyboard.TargetName="FocusVisual"

Storyboard.TargetProperty="(UIElement.Opacity)">

<SplineDoubleKeyFrame KeyTime="00:00:00" Value="1"/>

</DoubleAnimationUsingKeyFrames>

</Storyboard>

</vsm:VisualState>

</vsm:VisualStateGroup>

</vsm:VisualStateManager.VisualStateGroups>

<Grid

x:Name="HorizontalTemplate">

<Grid.ColumnDefinitions>

<ColumnDefinition

Width="Auto"/>

<ColumnDefinition

Width="Auto"/>

<ColumnDefinition

Width="*"/>

</Grid.ColumnDefinitions>

<Rectangle Height="3"

Margin="5,0,5,0" Grid.Column="0" Grid.ColumnSpan="3" StrokeThickness="0.5"/>

<RepeatButton

IsTabStop="False" Template="{StaticResource RepeatButtonTemplate}"

x:Name="HorizontalTrackLargeChangeDecreaseRepeatButton" Grid.Column="0"

IsHitTestVisible="False"/>

<Thumb Height="18"

x:Name="HorizontalThumb" Style="{StaticResource VoyagetThumbStyle}" Width="11"

Grid.Column="1"/>

<RepeatButton

IsTabStop="False" Template="{StaticResource RepeatButtonTemplate}"

x:Name="HorizontalTrackLargeChangeIncreaseRepeatButton" Grid.Column="2"

IsHitTestVisible="False"/>

</Grid>

<Grid x:Name="VerticalTemplate"

Visibility="Collapsed">

<Grid.RowDefinitions>

<RowDefinition

Height="*"/>

<RowDefinition

Height="Auto"/>

<RowDefinition

Height="Auto"/>

</Grid.RowDefinitions>

<Rectangle

Margin="0,5,0,5" Width="3" Grid.Row="0" Grid.RowSpan="3" Fill="#FFE6EFF7"

Stroke="Black" StrokeThickness="0.5"/>

<RepeatButton

IsTabStop="False" Template="{StaticResource RepeatButtonTemplate}"

x:Name="VerticalTrackLargeChangeDecreaseRepeatButton" Grid.Row="2"/>

<Thumb Height="11"

x:Name="VerticalThumb" Width="18" Grid.Row="1"/>

<RepeatButton

IsTabStop="False" Template="{StaticResource RepeatButtonTemplate}"

x:Name="VerticalTrackLargeChangeIncreaseRepeatButton" Grid.Row="0"/>

</Grid>

<Rectangle x:Name="FocusVisual"

Stroke="#666666" StrokeDashArray=".2 5" StrokeDashCap="Round"

IsHitTestVisible="false" Opacity="0"/>

</Grid>

</ControlTemplate>

Page 81: Adotando Silverlight 2.0 - v1 - Exercicios

81

</Setter.Value>

</Setter>

</Style>

</UserControl.Resources>

Page 82: Adotando Silverlight 2.0 - v1 - Exercicios

82

E- Layout Personalizado no Silverlight

Criando Painéis de Layout Personalizado

Introdução

A maioria das aplicações web podem se beneficiar com um layout dinâmico que pode se ‘redimensionar’

para tirar proveito do estado real disponível na tela.

No Silverlight2, os ‘containers’ usados para Layout são chamados de Panels (painéis) porque são classes

que geralmente herdam da classe Panel.

O Silverlight vem com três painéis de layout:

Canvas possibilita um posicionamento absoluto de seus elementos Filhos. Você define um

Canvas.Left e Canvas.Top em um Elemento de interface de usuário, e o Canvas o coloca no

deslocamento Top, Left (Superior, Esquerda) apropriado a partir de sua posição 0,0.

StackPanel é um painel simples que pode ser orientado em uma direção horizontal ou vertical e

coloca seus filhos um ao lado do outro nessa direção.

Grid é o container de layout mais potente do Silverlight. Ele tem um comportamento de tabela

em que os itens podem ser posicionados explicitamente em uma combinação de

Linhas/Colunas. O Grid tem suporte para:

o “Starsizing” ou uso de uma porcentagem do estado real disponível na tela.

o ColumnSpan/RowSpan para elementos de interface de usuário que querem usar mais

que uma única Linha ou Coluna.

o Margem e Preenchimento.

Se você precisar de um layout diferente dos que vêm com o produto, é fácil criar um painel

personalizado. O Silverlight, como o WPF, faz o que é chamado de layout em dois passos:

1. Um container vai chamar Measure () em cada um de seus filhos. Durante o passo Measure, o

container está dizendo aos filhos quanto elemento está disponível.

a. Durante o Measure, cada Filho define sua propriedade DesiredSize para que o container

saiba de quanto espaço o elemento Filho vai precisar.

Page 83: Adotando Silverlight 2.0 - v1 - Exercicios

83

2. Quando o container tiver medido seus filhos, ele os organiza (Arrange()) nos lugares adequados.

Organizá-los significa posicionar no deslocamento certo a partir da coordenada 0,0 no Painel.

Objetivos do Laboratório:

Neste laboratório você vai além dos painéis básicos que vêm com o Silverlight 2, criando vários painéis

personalizados. Você vai construir:

Um WrapPanel básico para se familiarizar com os fundamentos do Layout

Um painel personalizado chamado “TimelinePanel” para ser usado na aplicação Voyager.

Page 84: Adotando Silverlight 2.0 - v1 - Exercicios

84

Seção 1: Criando um WrapPanel

Personalizado

Neste exercício você vai construir um WrapPanel simples.

Um WrapPanel posiciona seus filhos em um layout de fluxo seqüencial, da esquerda para a direita.

Se o painel alcançar o final do lado direito, ele começará uma nova linha e continuará dispondo os itens

da esquerda para a direita na linha seguinte.

Veja um exemplo de um WrapPanel dispondo alguns filhos.

Para criar um WrapPanel você vai herdar de Panel; ele dá a você a coleção Children (Filhos), e as funções

Measure/Arrange (Medir/Organizar) necessárias; você vai substituí-los para personalizar o layout.

Page 85: Adotando Silverlight 2.0 - v1 - Exercicios

85

Tarefa 1: Crie uma classe WrapPanel

Crie uma nova aplicação do Silverlight, e chame-a de MargiesTravel.Controls.

[Se o Visual Studio pedir para que você crie uma aplicação web para o projeto, fique à vontade para

dizer Não e apenas diga ao VS para criar uma página HTML para teste].

1. Adicione uma nova classe, chamada WrapPanel para o projeto MargiesTravel.Controls.

2. Faça sua classe WrapPanel herdar de Panel

public class WrapPanel : Panel { }

Page 86: Adotando Silverlight 2.0 - v1 - Exercicios

86

Tarefa 2: Meça seus Filhos para encontrar seu Tamanho

Desejado.

O WrapPanel herda uma coleção de Children (Filhos) de Panel, e é assim que vai acessar itens nela.

Para descobrir o DesiredSize (Tamanho Desejado) de cada um de seus filhos, o WrapPanel terá que

chamar Measure() em cada filho. Você pode realizar isso implementando (ou substituindo) a função

MeasureOverride do Panel. Essa função será chamada sempre que ocorrer um passo de layout (e antes

de Arrange).

Nós não fazemos nenhum trabalho além de Medir (Measure) os itens (isto é, não os posicionamos

durante um passo de medida).

1. Substitua a classe MeasureOverride de seu WrapPanel e chame Measure() para cada um dos Children do painel.

protected override Size MeasureOverride(Size availableSize)

{

foreach (UIElement child in Children)

{

child.Measure(availableSize);

}

}

Note que o parâmetro availableSize que você está passando para Measure é o tamanho disponível para

nosso WrapPanel. Passando esse Size (Tamanho) para cada filho, o WrapPanel está dizendo a cada Child

(Filho) que esse espaço está disponível. Se o DesiredSize (tamanho desejado) do filho for maior que o

availableSize (tamanho disponível), ele será recortado. Por exemplo, se o DesiredSize.Width do Child

for 500, mas o availableSize.Width do WrapPanel for 400, apenas os primeiros 400 pixels estarão

visíveis. Os outros 100 serão recortados.

Page 87: Adotando Silverlight 2.0 - v1 - Exercicios

87

Tarefa 3: Organize nossos filhos

Quando você mediu as dimensões dos filhos, cada um deles definiu sua propriedade DesiredSize com o

tamanho que queria ter; agora você precisa ler esse tamanho e posicioná-los dentro do espaço do Panel.

Substitua o método ArrangeOverride na classe de nosso Panel.

protected override Size ArrangeOverride(Size finalSize)

{

}

Dentro de ArrangeOverride vamos organizar – Arrange() – ou posicionar os itens. Usaremos o

availableWidth do Panel para dispor os itens da esquerda para a direita, sem perder de vista cada Left

(esquerda) do painel, e quando chegarmos ao final da linha, iniciaremos uma nova.

1. Crie um loop (for ou foreach) para iterar nos filhos

2. Chame Arrange () nos Children () dando a ele um Rect () de onde precisa estar.

O código está abaixo, mas ele é pré-comentado, então você pode comentar as linhas de que precisa.

(Sou astuto, não?)

//protected override Size ArrangeOverride(Size finalSize)

//{

// double currentLeft = 0;

// double currentTop = 0;

// double currentRowHeight = 0 ;

// foreach (UIElement child in Children)

// {

// // Verifique se ele não cabe na linha atual

// if ((currentLeft + child.DesiredSize.Width) >

finalSize.Width)

// {

// //Inicie uma nova linha redefinindo nossas coisas

// currentLeft = 0;

// currentTop += currentRowHeight;

// currentRowHeight = 0;

// }

// // isto faz o posicionamento do filho

// child.Arrange(new Rect(currentLeft, currentTop,

child.DesiredSize.Width, child.DesiredSize.Height));

// // mude para o próximo espaço disponível.

// currentLeft += child.DesiredSize.Width ;

// currentRowHeight = Math.Max( currentRowHeight,

child.DesiredSize.Height );

// }

// return finalSize;

Page 88: Adotando Silverlight 2.0 - v1 - Exercicios

88

Tarefa 4: Exercitando nosso Painel

O WrapPanel está bastante básico agora, ele não manipula Margem ou Visibilidade, mas é bom o

suficiente para ser exibido. Vamos entrar no Page.xaml e criar uma instância do WrapPanel e dar a ele

alguns filhos.

1. No Page.xaml, na declaração UserControl, adicione uma declaração xmlns para seu namespace

<UserControl x:Class="MargiesTravel.Controls.Page"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:lcl="clr-namespace:MargiesTravel.Controls"

Width="400" Height="300">

2. Dentro do Grid, insira um WrapPanel e lembre-se de colocar nele um prefixo com o namespace

xml que você declarou no passo acima (se copiou e colou, o namespace é lcl).

<lcl:WrapPanel>

3. Agora, dentro do WrapPanel, apenas digite Elementos XAML válidos (Button, Rectangle,

TextBlock, etc.). Já que nosso WrapPanel é burro e não faz o alongamento, lembre-se de

especificar para todos os seus itens uma Width e uma Height. Novamente, se você passar do

espaço disponível seus Filhos serão recortados.

[Dica, olhe as Respostas para ver sugestões sobre inserção de Filhos]

Page 89: Adotando Silverlight 2.0 - v1 - Exercicios

89

Seção 2 – Implementando um

TimelineStackPanel para a aplicação do Voyager

O WrapPanel é fácil porque não há nenhum metadado fornecido dentro do Panel. Isso é bastante

incomum. Se reparar nos outros containers de layout (como Grid e Canvas), você anexou propriedades

como Canvas.Left e Canvas.Top ou Grid.Row e Grid.Column que os itens inseridos nesses painéis usam

como dicas do painel em que precisam estar.

Desta vez, para sua aplicação de viagem, vamos criar um TimelinePanel básico.

Será um Panel que organiza seus Children com base em datas, cada Child diz a ele qual é sua data inicial

e final e a partir daí o Panel vai dizer a ele onde ele precisa estar.

Por exemplo, um painel Timeline com esses filhos:

<lcl:TimelineStackPanel x:Name="timepanel" Height="60" >

<Rectangle Fill="Red" lcl:TimelineStackPanel.StrBegin="6/11/2008"

lcl:TimelineStackPanel.StrEnd="6/12/2008" />

<Rectangle Fill="Yellow"

lcl:TimelineStackPanel.StrBegin="6/13/2008"

lcl:TimelineStackPanel.StrEnd="6/15/2008" />

<Rectangle Fill="Green" lcl:TimelineStackPanel.StrBegin="6/16/2008"

lcl:TimelineStackPanel.StrEnd="6/19/2008" />

</lcl:TimelineStackPanel>

Isso será realizado:

Ele vai particionar seu espaço disponível em 8 dias (6/19 – 6/11).

Vai dispor seu primeiro filho de modo a ocupar 1/8 do espaço, já que tem a duração de um dia.

Vai dispor o segundo filho em 2/8 do espaço (dois dias de duração) e ele será posicionado no

deslocamento de largura de 2/8 a partir da posição 0,0 do Panel, já que a data Begin (Inicial) é

dois dias a partir da data mais baixa no painel.

E assim por diante. Resumindo, a disposição dos filhos ficará assim:

Page 90: Adotando Silverlight 2.0 - v1 - Exercicios

90

[se você pensar bem, Viagem é baseada em tempo, aviões partem e pousam em horários específicos,

você faz reservas em hotéis para dias, etc., então o painel será útil para dispor as coisas com base no

tempo].

Page 91: Adotando Silverlight 2.0 - v1 - Exercicios

91

Tarefa 1: Crie uma classe TimelineStackPanel

1. Adicione uma nova classe ao nosso projeto Silverlight.Controls e chame-a de TimelineStackPanel

2. Faça a classe herdar de Panel

public class TimelineStackPanel : Panel

{

}

Tarefa 2: Implementando Measure (Medida) em nosso TimelineStackPanel

Se você se lembrar do WrapPanel, nós implementamos MeasureOverride para fazer os Filhos calcularem

seu DesiredSize. Na verdade nós NÃO temos que fazer isso para TimelineStackPanel porque ele não

respeita o DesiredSize dos Filhos; ele calcula seu tamanho com base em sua Data Inicial/Final.

Page 92: Adotando Silverlight 2.0 - v1 - Exercicios

92

Tarefa 3 Adicionando Propriedades de Dependência Anexadas ao

TimelineStackPanel

O TimelineStackPanel precisa saber onde colocar seus filhos; os Filhos precisarão definir sua

Propriedade Begin/End para que o TimelineStackPanel saiba onde colocá-los.

Os filhos, no entanto, são controles e UIElements existentes (como Rectangle), então você não pode

voltar a eles e adicionar uma propriedade Begin/End. Propriedades Anexadas salvam o dia; o

TimelineStackPanel vai expor uma propriedade Begin/End que os Filhos poderão definir.

O modo mais fácil de criar uma propriedade anexada no editor de Código do Visual Studio é através de

trechos de código.

Se você digitar propa, pressionar Enter e depois Tab, o gerenciador de trechos vai criar uma Propriedade

de Dependência Anexada. Isso, infelizmente, usa a sintaxe do WPF, que é um pouco diferente do

Silverlight mas ainda ajuda bastante. Tudo o que você tem que fazer é mudar o último parâmetro na

propriedade de dependência anexada para que seja nulo, em vez de uma nova UIPropertyMetadata (0).

A Propriedade de Dependência anexada para uma propriedade chamada Begin deve ficar assim:

public static DateTime GetBegin(DependencyObject obj)

{

return (DateTime)obj.GetValue(BeginProperty);

}

public static void SetBegin(DependencyObject obj, DateTime value)

{

obj.SetValue(BeginProperty, value);

}

// Usar uma DependencyProperty como armazenamento de backup para Begin.

Isso habilita a animação, o estilo, a ligação, etc...

public static readonly DependencyProperty BeginProperty =

DependencyProperty.RegisterAttached("Begin", typeof(DateTime),

typeof(TimelineStackPanel), null );

Vamos ao trabalho:

3. Adicione uma Propriedade de Dependência Anexada ao nosso TimelineStackPanel para Begin, o

tipo é DateTime

[Dica, você pode fazer isso recortando e colando o texto acima]

4. Adicione uma Propriedade de Dependência Anexada para nosso TimelineStackPanel para End, o

tipo é DateTime

Page 93: Adotando Silverlight 2.0 - v1 - Exercicios

93

Tarefa 4: Notificando o Panel quando um Begin ou End for alterado

Imagine que os itens são inseridos no TimelineStackPanel, o painel está fazendo a Organização de

nossos itens, se uma propriedade Begin ou End mudar ou for modificada, o Panel deve invalidar seu

layout e Reorganizar os filhos apropriadamente. Infelizmente, na declaração das propriedades na Tarefa

3, nós não vimos uma Change Notification Callback (Chamada de Notificação de Mudança).

5. Volte às Propriedades Begin/End e na declaração RegisterAttached (), onde você põe nulo no

último parâmetro, mude-a para uma instância de PropertyMetaData com um Manipulador

PropertyChangedCallback, e isso será chamado sempre que Begin ou End forem modificados. O

nome da função chamada será OnBeginEndChanged.

É assim que a EndProperty fica, com as alterações destacadas.

public static readonly DependencyProperty EndProperty =

DependencyProperty.RegisterAttached("End", typeof(DateTime),

typeof(TimelineStackPanel),

new PropertyMetadata(new

PropertyChangedCallback(OnBeginEndChanged)));

6. Defina a função OnBeginEndChanged. A assinatura é assim

protected static void OnBeginEndChanged(DependencyObject obj,

DependencyPropertyChangedEventArgs args){ }

Note que a assinatura é Estática, ela tem que ser assim porque é chamada a partir de RegisterAttached,

que também é estático.

Agora OnBeginEndChanged não está fazendo nada, e ele precisa avisar o TimelineStackPanel que este

deve invalidar o layout. Como podemos fazer isso?

7. Dentro de OnBeginEndChanged, converta o objeto Parameter para um FrameworkElement

8. Veja se a propriedade Framework Parent está definida [esse será um TimelineStackPanel]

9. Veja se o Parent (Pai) é uma instância de TimelineStackPanel

Se o pai for um TimelineStackPanel, vamos invalidar seu Layout.

Este é o código para 7-9 acima, comentados novamente.

//protected static void OnBeginEndChanged(DependencyObject obj,

DependencyPropertyChangedEventArgs args)

//{

// FrameworkElement fe = obj as FrameworkElement;

// if (fe != null)

Page 94: Adotando Silverlight 2.0 - v1 - Exercicios

94

// {

// TimelineStackPanel parent = fe.Parent as TimelineStackPanel;

// if (parent != null)

// {

// parent.InvalidateArrange();

// }

// else

// fe.InvalidateArrange();

// }

//}

Page 95: Adotando Silverlight 2.0 - v1 - Exercicios

95

Tarefa 5: Adicionando Propriedades de Dependência Low e High ao nosso

Painel.

Este passo poderia ser opcional, mas é necessário para a aplicação do Voyager e faz sentido até para o

Panel. Vamos dar a ele uma propriedade Low (Baixa) e High (Alta) para seu layout. Imagine por exemplo

que o TimelineStackPanel está plotando um vôo. O vôo pode ser de 9:45 a 12:15 mas nós queremos

plotá-lo como um dia inteiro, ou ao menos como 9 da manhã a 1 da tarde. É isso que a Low e a High

farão; elas definem o intervalo.

Low e High não são propriedades de dependência anexadas. Elas são propriedades de dependência

regulares, pois são definidas na instância do Panel. É como definir Width/Height ou qualquer outra

propriedade anexada.

Similar ao trecho propa, há um trecho propdp que podemos usar para criar nossas propriedades no

Editor de Código.

Digite propdp, pressione Enter e depois Tab no editor do VS, dentro de nossa classe TimelineStackPanel.

Você vai precisar substituir novamente o último parâmetro já que são trechos do WPF.

Para uma propriedade DateTime chamada Low, possuída pelo TimelineStackPanel, ficará assim:

public DateTime Low

{

get { return (DateTime)GetValue(LowProperty); }

set { SetValue(LowProperty, value); }

}

// Using a DependencyProperty as the backing store for Low. This

enables animation, styling, binding, etc...

public static readonly DependencyProperty LowProperty =

DependencyProperty.Register("Low", typeof(DateTime),

typeof(TimelineStackPanel), null );

10. Adicione uma DependencyProperty Low ao nosso TimelineStackPanel

Dica:

public DateTime Low

{

get { return (DateTime)GetValue(LowProperty); }

set { SetValue(LowProperty, value); }

}

public static readonly DependencyProperty LowProperty =

DependencyProperty.Register("Low", typeof(DateTime),

typeof(TimelineStackPanel), null );

Page 96: Adotando Silverlight 2.0 - v1 - Exercicios

96

11. Adicione uma DependencyProperty High ao nosso TimelineStackPanel (igual ao passo acima mas

com Low em vez de High).

Page 97: Adotando Silverlight 2.0 - v1 - Exercicios

97

Tarefa 6: Notificando o Panel que as propriedades Low/High mudaram

Similar a quando uma propriedade do Filho mudou, o TimelineStackPanel precisa invalidar seu layout

quando suas propriedades Low e High mudaram.

12. Adicione um PropertyChangedCallback à Propriedade Low. Chame-o de OnRangeChanged

[O trecho abaixo mostra a sintaxe; em destaque está o que mudou na declaração LowProperty].

public static readonly DependencyProperty LowProperty =

DependencyProperty.Register("Low", typeof(DateTime),

typeof(TimelineStackPanel),

new PropertyMetadata ( new PropertyChangedCallback

(OnRangeChanged)));

13. Repita o passo acima e adicione OnRangeChanged como notificação, mas desta vez para a

Propriedade High.

[Igual ao passo acima]

14. Crie a função OnRangeChanged:

protected static void OnRangeChanged(DependencyObject dep,

DependencyPropertyChangedEventArgs args)

Embora a assinatura para PropertyChangedCallback seja a mesma das propriedades de dependência

anexadas anteriores, desta vez a instância passada para os parâmetros é um pouco diferente porque

essa não é uma Propriedade Anexada. Neste caso dep, o parâmetro DependencyObject, é uma

instância da classe TimelineStackPanel. Você não tem que detectar o Pai.

15. Converta o parâmetro dep passado para um TimelineStackPanel e

Chame InvalidateArrange no painel.

protected static void OnRangeChanged(DependencyObject dep,

DependencyPropertyChangedEventArgs args)

{

TimelineStackPanel panel = dep as TimelineStackPanel;

panel.InvalidateArrange();

}

Page 98: Adotando Silverlight 2.0 - v1 - Exercicios

98

Tarefa 7: Implementando o Arrange (Organizar)

Agora que o TimelineStackPanel tem um Low e um High para o intervalo, é hora de Organizar nossos

Filhos com base em sua linha do tempo. Aqui está tudo o que temos que fazer; a maior parte é lógica de

negócios, então o código está incluído.

16. Implemente ArrangeOverride

17. Dentro de ArrangeOverride, cheque primeiro para ver se as funções Low e High foram definidas.

Se não, itere em todos os filhos para ter um intervalo das datas.

Note que eu fiz algo engraçado e converti as datas para o dobro. Fica mais fácil comparar e

calcular larguras...

double min = 0, max = 0;

if (ReadLocalValue(LowProperty) != DependencyProperty.UnsetValue)

{

min = Low.ToOADate();

}

if (ReadLocalValue(HighProperty) != DependencyProperty.UnsetValue)

{

max = High.ToOADate();

}

if (min == 0 || max == 0)

{

GetMinMax(ref min, ref max);

}

GetMinMax é simplesmente uma iteração de todos os filhos para capturar a Min

StartDate e a Max EndDate.

protected bool GetMinMax(ref double min, ref double max)

{

min = double.MaxValue;

max = double.MinValue;

DateTime mindate = DateTime.MaxValue;

DateTime maxdate = DateTime.MinValue;

foreach (UIElement e in Children)

{

double begin = GetBegin(e).ToOADate();

double end = GetEnd(e).ToOADate();

min = Math.Min(begin, min);

max = Math.Max(end, max);

}

return true;

}

Page 99: Adotando Silverlight 2.0 - v1 - Exercicios

99

18. Agora que temos um intervalo de datas, pegue o espaço disponível e divida-o pelo intervalo para calcular um multiplicador para onde colocar os itens.

double range = (max - min);

double multiplier = finalSize.Width / (max - min);

19. Finalmente, organize os filhos de modo similar ao que fizemos antes no WrapPanel.

foreach (UIElement e in Children)

{

try

{

double itembegin = GetBegin(e).ToOADate();

double itemend = GetEnd(e).ToOADate();

double left = Math.Max(0, multiplier * (itembegin - min));

double height = Math.Max(0, multiplier * (itemend - itembegin));

e.Arrange(new Rect(left, 0, height, finalSize.Height));

}

finally { }

}

return finalSize;

Page 100: Adotando Silverlight 2.0 - v1 - Exercicios

100

Tarefa 8: Resolvendo um problema de Conversor

[Esse passo é opcional]. Você implementou cada coisa necessária para a aplicação do Voyager. Mas

para testar de forma fácil o laboratório você deve querer fazê-lo em XAML, certo??

Na beta2, há um problema em aberto: nós não podemos anexar um Conversor de DateTime to String a

uma propriedade anexada. Sem um conversor, não seria fácil definir a propriedade Begin/End a partir

do XAML. Como solução temporária, você pode anexar a propriedade de Dependências Anexadas (como

na Tarefa 3), mas desta vez do tipo string. O TimelineStackPanel vai conectar essas propriedades às

propriedades Begin/End originais.

A tarefa é apenas uma repetição de tudo que fizemos antes (Tarefa 3).

20. Adicione Propriedades de Dependência Anexadas ao TimelineStackPanel de tipo string

(seqüência de caracteres), chamando-as de StrBegin e StrEnd.

21. Manipule o PropertyChangedCallback para essas propriedades.

22. Dentro do callback, leia o valor de StrBegin/StrEnd respectivamente, converta-o a um DateTime

e defina a propriedade Begin ou End correspondente.

Você pode recortar e colar o código do link da resposta abaixo.

Page 101: Adotando Silverlight 2.0 - v1 - Exercicios

101

Tarefa 9: Testando nosso TimelineStackPanel a partir do código

O Voyager é controlado por dados, então vamos usar o TimelineStackPanel a partir do código. Portanto

você podia ter pulado o passo 8. Para testar a partir do código vamos ter que:

23. Voltar ao Page.xaml, onde testamos nosso WrapPanel, e substituir a declaração WrapPanel por

uma declaração TimelineStackPanel. [Certifique-se de dar a ela um x:Name no XAML para que

possamos acessá-la a partir do código].

<lcl:TimelineStackPanel x:Name="timepanel" Height="60" >

24. Entre no Page.xaml.cs e no construtor adicione um manipulador para o evento Loaded

25. Dentro do evento Loaded, execute algum código fictício que insira itens em nosso StackPanel.

Lembre-se de fazer o seguinte:

a. Defina uma Data Inicial (Begin)

b. Defina uma Data Final (End)

O End deve ser maior que o Begin porque não há verificação de erros em nosso Panel.

void Page_Loaded(object sender, RoutedEventArgs e)

{

timepanel.Low = DateTime.Today;

timepanel.High = DateTime.Today.AddDays(30);

for (int x = 0; x < 5; x++)

{

Rectangle rect = new Rectangle();

rect.Fill = new SolidColorBrush(Color.FromArgb((byte)255,

(byte)(x * 70), (byte)(x * 70), (byte)(x * 70)));

TimelineStackPanel.SetBegin(rect, DateTime.Today.AddDays((4 *

x)));

TimelineStackPanel.SetEnd(rect, DateTime.Today.AddDays((4 * x)

+ 1));

timepanel.Children.Add(rect);

}

}

Page 102: Adotando Silverlight 2.0 - v1 - Exercicios

102

Tarefa 10: Testando nosso TimelineStackPanel a partir do XAML

Se você implementou o passo 8, pode testar nosso painel no XAML. Basta entrar na declaração

TimelineStackPanel no XAML e inserir qualquer tipo de UIElement no painel. Lembre-se de colocar datas

StreBegin e StrEnd no item. Essas são propriedades anexadas, então têm esta sintaxe atribuída (para

uma declaração de namespace de lcl)

<Rectangle Fill="Red" lcl:TimelineStackPanel.StrBegin="6/11/2008"

lcl:TimelineStackPanel.StrEnd="6/12/2008" />

Tarefa 11: Exercícios para o usuário

Divirta-se um pouco com o painel. Faça combinações e correspondências, inserindo por exemplo itens a

partir do XAML e do Código.

Adicione um botão ao seu código ou algum controle de datas que altere as datas no vôo para poder ver

o painel de pilha se reorganizar conforme o necessário.

Page 103: Adotando Silverlight 2.0 - v1 - Exercicios

103

Apêndices

Seção 1: Respostas das Tarefas

Tarefa 1: Crie uma classe WrapPanel

//É assim que fica o WrapPanel inteiro. Em destaque está o código que você teria adicionado.

using System;

using System.Net;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Documents;

using System.Windows.Ink;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Animation;

using System.Windows.Shapes;

namespace MargiesTravel.Controls

{

public class WrapPanel : Panel

{

}

}

Tarefa 2: Medindo nossos filhos

//Dentro da classe WrapPanel, adicione o seguinte método

protected override Size MeasureOverride(Size availableSize)

{

foreach (UIElement child in Children)

{

child.Measure(availableSize);

}

}

Tarefa 3: Passo 1 – Substituindo o Arrange

protected override Size ArrangeOverride(Size finalSize)

{

}

Page 104: Adotando Silverlight 2.0 - v1 - Exercicios

104

Tarefa 3: Passo 2 -- Implementando o Arrange

protected override Size ArrangeOverride(Size finalSize)

{

double currentLeft = 0;

double currentTop = 0;

double currentRowHeight = 0;

foreach (UIElement child in Children)

{

// Check if it does not fit into current row

if ((currentLeft + child.DesiredSize.Width) > finalSize.Width)

{

//Start a new row by resetting our stuff

currentLeft = 0;

currentTop += currentRowHeight;

currentRowHeight = 0;

}

// this does the positioning of the child

child.Arrange(new Rect(currentLeft, currentTop,

child.DesiredSize.Width, child.DesiredSize.Height));

// move to next available space..

currentLeft += child.DesiredSize.Width;

currentRowHeight = Math.Max(currentRowHeight,

child.DesiredSize.Height);

}

return finalSize;

}

Tarefa 4: Exercitando nosso WrapPanel.

Escolha entre estas soluções:

<UserControl x:Class="MargiesTravel.Controls.Page"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:lcl="clr-namespace:MargiesTravel.Controls"

Width="400" Height="300">

<Grid x:Name="LayoutRoot" Background="White">

<lcl:WrapPanel>

<Rectangle Width="200" Height="50" Fill="Red" > </Rectangle>

<Rectangle Width="100" Height="20" Fill="Yellow" ></Rectangle>

<Rectangle Width="70" Height="70" Fill="Green" ></Rectangle>

<Rectangle Width="100" Height="50" Fill="Red" ></Rectangle>

<Rectangle Width="100" Height="100" Fill="Yellow" ></Rectangle>

<Rectangle Width="400" Height="70" Fill="Green" ></Rectangle>

<Rectangle Width="500" Height="70" Fill="Red" ></Rectangle>

</lcl:WrapPanel>

</Grid>

</UserControl>

OU

<UserControl x:Class="MargiesTravel.Controls.Page"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

Page 105: Adotando Silverlight 2.0 - v1 - Exercicios

105

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:lcl="clr-namespace:MargiesTravel.Controls"

Width="400" Height="300">

<Grid x:Name="LayoutRoot" Background="White">

<lcl:WrapPanel>

<Button Content="button" Width="200" Height="50" />

<TextBlock Text="TextBlock" Width="150" Height="30"/>

<Slider Width="300" Value="6" Height="50"/>

<TextBox Width="200" Height="30" />

<TextBlock Text="Autosize text" />

<CheckBox Content="checkbox" Width="140" />

</lcl:WrapPanel>

</Grid>

</UserControl>

Exercício 2: Respostas das Tarefas

Tarefa 1: Criando um TimelineStackPanel

public class TimelineStackPanel : Panel

{

}

Tarefa 3: Adicionando propriedades de dependência anexadas para Begin e

End

// Esta resposta inclui as propriedades e a declaração de classe propriamente

dita.

public class TimelineStackPanel : Panel

{

public static DateTime GetBegin(DependencyObject obj)

{

return (DateTime)obj.GetValue(BeginProperty);

}

public static void SetBegin(DependencyObject obj, DateTime value)

{

obj.SetValue(BeginProperty, value);

}

// Usar uma DependencyProperty como o armazenamento de backup para

Begin. Isso habilita a animação, o estilo, a ligação, etc...

public static readonly DependencyProperty BeginProperty =

DependencyProperty.RegisterAttached("Begin", typeof(DateTime),

typeof(TimelineStackPanel), null );

public static DateTime GetEnd(DependencyObject obj)

{

return (DateTime)obj.GetValue(EndProperty);

Page 106: Adotando Silverlight 2.0 - v1 - Exercicios

106

}

public static void SetEnd(DependencyObject obj, DateTime value)

{

obj.SetValue(EndProperty, value);

}

// Usar uma DependencyProperty como o armazenamento de backup para End.

Isso habilita a animação, o estilo, a ligação, etc...

public static readonly DependencyProperty EndProperty =

DependencyProperty.RegisterAttached("End", typeof(DateTime),

typeof(TimelineStackPanel), null)

}

Tarefa 4: Notificando o Panel quando um Begin ou End for alterado

//Esta tarefa mostra em destaque as alterações que você teve que fazer para conectar a notificação de Alteração. //Abaixo disso está o código para OnBeginEndChanged. É ‘código novo’.

public static readonly DependencyProperty EndProperty =

DependencyProperty.RegisterAttached("End", typeof(DateTime),

typeof(TimelineStackPanel),

new PropertyMetadata(new

PropertyChangedCallback(OnBeginEndChanged)));

public static readonly DependencyProperty BeginProperty =

DependencyProperty.RegisterAttached("Begin", typeof(DateTime),

typeof(TimelineStackPanel),

new PropertyMetadata(new

PropertyChangedCallback(OnBeginEndChanged)));

protected static void OnBeginEndChanged(DependencyObject obj,

DependencyPropertyChangedEventArgs args)

{

FrameworkElement fe = obj as FrameworkElement;

if (fe != null)

{

TimelineStackPanel parent = fe.Parent as TimelineStackPanel;

if (parent != null)

{

parent.InvalidateArrange();

}

else

fe.InvalidateArrange();

}

}

Page 107: Adotando Silverlight 2.0 - v1 - Exercicios

107

Tarefa 5: Adicionando Propriedades de Dependência Low e High ao nosso

Painel.

public DateTime Low

{

get { return (DateTime)GetValue(LowProperty); }

set { SetValue(LowProperty, value); }

}

// Usar uma DependencyProperty como o armazenamento de backup para Low.

Isso habilita a animação, o estilo, a ligação, etc...

public static readonly DependencyProperty LowProperty =

DependencyProperty.Register("Low", typeof(DateTime),

typeof(TimelineStackPanel), null );

public DateTime High

{

get { return (DateTime)GetValue(HighProperty); }

set { SetValue(HighProperty, value); }

}

// Usar uma DependencyProperty como o armazenamento de backup para

High. Isso habilita a animação, o estilo, a ligação, etc...

public static readonly DependencyProperty HighProperty =

DependencyProperty.Register("High", typeof(DateTime),

typeof(TimelineStackPanel), null);

Tarefa 6: Notificando o Panel que as propriedades Low/High mudaram

public static readonly DependencyProperty LowProperty =

DependencyProperty.Register("Low", typeof(DateTime),

typeof(TimelineStackPanel),

new PropertyMetadata ( new PropertyChangedCallback

(OnRangeChanged)));

public static readonly DependencyProperty HighProperty =

DependencyProperty.Register("High", typeof(DateTime),

typeof(TimelineStackPanel),

new PropertyMetadata(new PropertyChangedCallback(OnRangeChanged)));

protected static void OnRangeChanged(DependencyObject dep,

DependencyPropertyChangedEventArgs args)

{

TimelineStackPanel panel = dep as TimelineStackPanel;

panel.InvalidateArrange();

}

Page 108: Adotando Silverlight 2.0 - v1 - Exercicios

108

Tarefa 7: Implementando o Arrange

protected bool GetMinMax(ref double min, ref double max)

{

min = double.MaxValue;

max = double.MinValue;

DateTime mindate = DateTime.MaxValue;

DateTime maxdate = DateTime.MinValue ;

foreach (UIElement e in Children)

{

double begin = GetBegin(e).ToOADate();

double end = GetEnd(e).ToOADate();

min = Math.Min(begin, min);

max = Math.Max(end, max);

}

return true;

}

protected override Size ArrangeOverride(Size finalSize)

{

double min = 0, max = 0 ;

if (ReadLocalValue(LowProperty) != DependencyProperty.UnsetValue)

{

min = Low.ToOADate();

}

if (ReadLocalValue(HighProperty) != DependencyProperty.UnsetValue)

{

max = High.ToOADate();

}

if ( min == 0 || max == 0 )

{

GetMinMax ( ref min, ref max );

}

double range = ( max - min);

double multiplier = finalSize.Width /( max - min );

foreach ( UIElement e in Children )

{

double itembegin = GetBegin (e).ToOADate ();

double itemend = GetEnd(e).ToOADate();

double left = Math.Max(0, multiplier * (itembegin - min ));

double height = Math.Max(0, multiplier * ( itemend -

itembegin));

e.Arrange ( new Rect ( left , 0, height , finalSize.Height))

;

}

return finalSize;

}

Page 109: Adotando Silverlight 2.0 - v1 - Exercicios

109

Tarefa 8: Resolvendo um problema de Conversor

public static string GetStrBegin(DependencyObject obj)

{

return (string)obj.GetValue(StrBeginProperty);

}

public static void SetStrBegin(DependencyObject obj, string value)

{

obj.SetValue(StrBeginProperty, value);

}

public static string GetStrEnd(DependencyObject obj)

{

return (string)obj.GetValue(StrEndProperty);

}

public static void SetStrEnd(DependencyObject obj, string value)

{

obj.SetValue(StrEndProperty, value);

}

// Usar uma DepStrEndencyProperty como o armazenamento de backup para

StrEnd. Isso habilita a animação, o estilo, a ligação, etc...

public static readonly DependencyProperty StrEndProperty =

DependencyProperty.RegisterAttached("StrEnd", typeof(string),

typeof(TimelineStackPanel),

new PropertyMetadata(new

PropertyChangedCallback(OnStrBeginStrEndChanged)));

public static readonly DependencyProperty StrBeginProperty =

DependencyProperty.RegisterAttached("StrBegin", typeof(string),

typeof(TimelineStackPanel),

new PropertyMetadata(new

PropertyChangedCallback(OnStrBeginStrEndChanged)));

protected static void OnStrBeginStrEndChanged(DependencyObject obj,

DependencyPropertyChangedEventArgs args)

{

UIElement e = obj as UIElement;

DateTime dte = DateTime.Parse((string)args.NewValue);

if (args.Property == TimelineStackPanel.StrBeginProperty)

{

TimelineStackPanel.SetBegin(obj, dte);

}

else

{

TimelineStackPanel.SetEnd(obj, dte);

}

}

Page 110: Adotando Silverlight 2.0 - v1 - Exercicios

110

Tarefa 9: Testando nosso TimelineStackPanel a partir do código

void Page_Loaded(object sender, RoutedEventArgs e)

{

timepanel.Low = DateTime.Today;

timepanel.High = DateTime.Today.AddDays(30);

for (int x = 0; x < 5; x++)

{

Rectangle rect = new Rectangle();

rect.Fill = new SolidColorBrush(Color.FromArgb((byte)255,

(byte)(x * 70), (byte)(x * 70), (byte)(x * 70)));

TimelineStackPanel.SetBegin(rect, DateTime.Today.AddDays((4 *

x)));

TimelineStackPanel.SetEnd(rect, DateTime.Today.AddDays((4 * x)

+ 1));

timepanel.Children.Add(rect);

}

}

Tarefa 10: Testando nosso TimelineStackPanel a partir do XAML

<UserControl x:Class="MargiesTravel.Controls.Page"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:lcl="clr-namespace:MargiesTravel.Controls"

Width="400" Height="300">

<Grid x:Name="LayoutRoot" Background="White">

<lcl:TimelineStackPanel x:Name="timepanel" >

<Rectangle Fill="Red" lcl:TimelineStackPanel.StrBegin="6/11/2008"

lcl:TimelineStackPanel.StrEnd="6/12/2008" />

<Rectangle Fill="Yellow"

lcl:TimelineStackPanel.StrBegin="6/13/2008"

lcl:TimelineStackPanel.StrEnd="6/15/2008" />

<Rectangle Fill="Green" lcl:TimelineStackPanel.StrBegin="6/17/2008"

lcl:TimelineStackPanel.StrEnd="6/18/2008" />

</lcl:TimelineStackPanel>

</Grid>

</UserControl>

Tarefa 11: Exercícios para o usuário

Veja o SilverlightUserControl1 na solução.

Page 111: Adotando Silverlight 2.0 - v1 - Exercicios

111

F- Usando Dados nas Aplicações do Silverlight

Ligação de dados a objetos, coleções e o DataGrid.

Introdução

A ligação de dados é o processo de estabelecer uma conexão, ou ligação, entre a interface de usuário (UI) e um objeto CLR para permitir que os dados fluam entre os dois: Se uma ligação Bidirecional é estabelecida e os dados mudam, os elementos da interface de usuário ligados aos dados podem refletir as mudanças automaticamente. Similarmente, as mudanças feitas através da interface de usuário são refletidas no objeto de dados.

Há dois objetos que participam de uma ligação: a Fonte e o Destino:

A fonte (Source) pode ser qualquer objeto CLR ou uma coleção de objetos

O destino (Target) é uma classe que herda de FrameworkElement

O Silverlight oferece diferentes Modos de ligação

As ligações Únicas atualizam o destino com os dados da fonte quando a ligação é criada. Não atualizam mais do que isso.

As ligações Unidirecionais atualizam o destino com os dados da fonte quando a ligação é criada e a qualquer mudança dos dados.

As ligações Bidirecionais atualizam tanto o destino quanto a fonte quando o outro muda.

A notificação das mudanças da Fonte ao Destino é feita via interface INotifyPropertyChanged. Para coleções, a notificação de mudanças é implementada usando INotifyCollectionChanged.

A Validação de Dados tem suporte em Ligações Bidirecionais. Ela é ‘roteada’ através do BindingValidationError.

Objetivos do Laboratório:

Na Tarefa 1, você vai aprender os fundamentos da ligação de dados.

Implementando InotifyPropertyChanged para Notificar Mudanças

Page 112: Adotando Silverlight 2.0 - v1 - Exercicios

112

Ligando uma Coleção a uma ListBox e criando Datatemplates

Criando uma Exibição Mestre/Detalhes

No Exercício 2 (laboratório separado), você vai implementar a ligação de dados necessária para a

aplicação do Voyager.

Criando Ligações via XAML

Herdando DataContext

Criando e usando conversores de Tipo

Page 113: Adotando Silverlight 2.0 - v1 - Exercicios

113

Tarefa 1: Criando uma Ligação Simples usando XAML e

DataContext

Abra o DataLab.sln na pasta Data_Starter.

O Page.xaml tem uma Interface de Usuário pré-definida que representa um Pacote de remessa.

Há também um objeto de negócios chamado TestData. Se você fosse conectar esses dois sem a ligação

de dados, teria que escrever código para carregar os dados para a interface de usuário e depois ler de

novo a partir da interface de usuário para salvá-los em seu objeto de negócios TestData. Em vez de

todo esse trabalho, você pode deixar a ligação de dados fazer tudo por você.

1.A partir do code-behind, no construtor Page(), após a chamada InitalizeComponent, defina o valor

de packageGrid.DataContext para TestData.package.

2.Agora edite o XAML para que ele ligue a Interface de Usuário às propriedades de

TestData.Package.

Elemento x:Name Caminho da Propriedade lblTrackingNo TrackingNo txtName Address.Name txtCity Address.City txtCountry Address.Country txtZip Address.ZipCode txtShipped ShippedDate

3. Execute sua aplicação e note que a interface de usuário exibe agora todos os dados de

TestData.Package.

Nós definimos o DataContext para o packageGrid e todos os elementos da interface de usuário

abaixo dele herdaram automaticamente o DataContext e por isso as ligações funcionaram.

Exercício para o usuário:

Tente entrar de novo em qualquer um de nossos blocos de texto e ‘substitua’ a propriedade

DataContext por um Valor local como este:

DataContext="{Binding Address}" Text="{Binding Street}"

Note que o DataContext pode ser substituído localmente, e também que um DataContext local pode ser ligado ao

DataContext herdado de um elemento.

Page 114: Adotando Silverlight 2.0 - v1 - Exercicios

114

Tarefa 2: Ligando a uma Coleção (usando um

DataTemplate)

Ligar a um item foi ótimo para testar, mas é necessário neste caso ligar a uma coleção (a propriedade

TestData.Packages).

Há ao menos duas opções:

Pegue o XAML na Página 1 e transforme-o em um UserControl; depois ligue os dados desse User

Control, dessa forma você acaba tendo um controle reutilizável para pôr em nossas coleções.

Crie um DataTemplate, que também dá a você um modelo reutilizável e não incorre na

sobrecarga de criar o UserControl (o que não é necessário neste caso).

Para implementar a última opção:

4.Crie um DataTemplate na seção UserControl.resources em Page.xaml.

Lembre-se que já que é um recurso, você precisa dar a ele uma chave.

Além disso, já que os UIElements no modelo de dados estão se referindo a recursos e os

staticResources devem ser definidos antes de serem usados, lembre-se de declarar o

DataTemplate no final da coleção de Recursos, perto da marca de fechamento de

</UserControl.Resources>

<DataTemplate x:Key=” PackageTemplate”>

</DataTemplate>

<UserControl.Resources>

5.Agora recorte e cole tudo do packageGrid – incluindo o próprio elemento <Grid>, no

DataTemplate...

6. Adicione uma ListBox a LayoutRoot em page.xaml. Use a propriedade ItemTemplate para dizer a

ela que use o DataTemplate que você acabou de criar.

<ListBox x:Name="packagesListbox" Width="400" ItemTemplate="{StaticResource

PackageTemplate}" />

7.Atualize o construtor em Page.xaml.cs para que o ItemsSource da ListBox seja a coleção

TestData.Packages.

this.packagesListbox.ItemsSource = TestData.Packages;

Page 115: Adotando Silverlight 2.0 - v1 - Exercicios

115

8.Execute a aplicação para ver se a Listbox funciona.

Exercício para o usuário:

Note que a listbox funciona, mas não é uma navegação muito boa. Detalhes demais para o usuário.

Como exercício, substitua o DataTemplate por algo mais adequado, como um modelo menor com

apenas Name?

[Se você fizer o exercício, lembre-se de copiar o modelo e criar um novo, não modifique o atual pois ele

será usado mais tarde].

Page 116: Adotando Silverlight 2.0 - v1 - Exercicios

116

Tarefa 3: Substituindo a Listbox por um DataGrid

Uma alternativa à substituição do modelo da caixa de listagem é usar um controle diferente, como o

DataGrid. A grade, com sua habilidade de exibir colunas, seria ótima para vermos todas as colunas de

uma vez em um formato tabular.

9. No projeto DataLab, adicione uma Referência à montagem System.Windows.Controls.Data. [Isso precisa ser feito porque o DataGrid não está nas montagens básicas do Silverlight].

<UserControl x:Class="DataLab.Task3"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Width="400" Height="300"

xmlns:data="clr-

namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data">

10. Agora adicione uma instância do DataGrid para substituir a Listbox (em LayoutRoot)

<data:DataGrid AutoGenerateColumns="True" x:Name="packagesListBox">

11. Selecione Auto-Gerar Colunas para não termos que fazer todo o trabalho.

12. Execute a aplicação e veja o DataGrid em ação.

[Não era bem o que eu esperava... teria ficado melhor se Package fosse um objeto plano, você

não acha?? Bem, é tarde demais para mudar o modelo do objeto, então vamos corrigir as

Colunas no DataGrid

Page 117: Adotando Silverlight 2.0 - v1 - Exercicios

117

Tarefa 4: Personalizando as colunas no DataGrid

É claro que o DataGrid também usa o DataTemplate para personalizar suas colunas.

Infelizmente não podemos reutilizar o Modelo de Dados que usamos para a ListBox, pois aquele é para

todos os dados e aqui no DataGrid queremos criar um por coluna.

A sintaxe para definir as colunas do DataTemplate fica assim:

<data:DataGrid.Columns>

<data:DataGridTemplateColumn Header=”Tracking No.” >

<data:DataGridTemplateColumn.CellTemplate>

<DataTemplate>

<TextBlock Text="{Binding TrackingNo}" />

</DataTemplate>

</data:DataGridTemplateColumn.CellTemplate>

</data:DataGridTemplateColumn>

1. Substitua quantas colunas do DataGrid quiser para navegar em seus dados.

Sinta-se à vontade para brincar. Note que o Header em DataGridTemplateColumn é um

ContentControl, então ele pode ter mais que texto; pode ter UIElements.

O mesmo vale para os DataTemplates para cada coluna, você pode fazer o que quiser.

Por exemplo, eu faço do DeliveredDate um DateTimePicker.

Page 118: Adotando Silverlight 2.0 - v1 - Exercicios

118

Tarefa 5 – Exibição Mestre/Detalhes usando Ligações a

partir do código

Agora que o DataGrid está mostrando os dados em linhas, você pode trazer de volta o modelo Package

único e usá-lo como uma Exibição de Detalhes para um pacote – aquele selecionado na grade.

Vamos primeiro adicionar a interface de usuário para os “detalhes”.

1.Atualize o LayoutRoot (o Grid) para ter duas linhas de altura igual

<Grid x:Name="LayoutRoot" Background="White">

<Grid.RowDefinitions>

<RowDefinition Height="*" />

<RowDefinition Height="*" />

</Grid.RowDefinitions>

2. Adicione uma propriedade anexada à grade de dados para estar no Grid.Row=”0”.

<data:DataGrid x:Name="packagesListBox" AutoGenerateColumns="False" Width="600"

Grid.Row="0">

3. Adicione um ContentControl com Grid.Row=“1” que usa o DataTemplate criado anteriormente.

<ContentControl x:Name=”details” ContentTemplate="{StaticResource

PackageTemplate}" Grid.Row="1" />

4. Nesse ponto o ContentControl está pronto mas precisa de dados.

Os dados precisam ser o SelectedItem do DataGrid. Infelizmente, no Silverlight 2, você não pode

ligar dados de um UIElement a outro, então precisamos de um objeto fictício no meio. Você

pode ligar ambos os elementos da interface de usuário a esse objeto fictício - vamos chamá-lo

de ViewModel - e dessa forma uma atualização da interface de usuário ao ViewModel será

carregada à outra interface de usuário também.

5. Adicione uma classe simples que expõe uma propriedade do tipo objeto, o nome da propriedade

é SelectedItem. A classe deve implementar INotifyPropertyChanged para notificação de

mudanças.

[Você pode ver a resposta para ter uma dica, mas tente fazer, pois você vai implementar

INotifyPropertyChanged muito quando fizer dados com o Silverlight]

Agora que o objeto de negócios ViewModel está pronto, precisamos criar uma Ligação. Neste caso

vamos fazer isso a partir do código, já que todas as nossas outras ligações foram feitas a partir do

Page 119: Adotando Silverlight 2.0 - v1 - Exercicios

119

XAML.

6. Adicione um manipulador de eventos Loaded ao construtor de sua Página, logo depois do lugar

em que estava definindo o itemsSource do DataGrid.

this.Loaded += new RoutedEventHandler(Task5_Loaded);

7. No manipulador de eventos Loaded, instancie uma instância do ViewModel. Isso pode ser feito

em escopo local.

ViewModel v = new ViewModel();

8. Agora crie uma Ligação a partir do código.

A fonte será o objeto ViewModel v criado no passo anterior. O destino será o DataGrid (ainda

chamado de packagesListBox) e a ligação será bidirecional. O código comentado abaixo deve dar

uma dica a você.

//Binding b = new Binding("SelectedItem");

//b.Source = v;

//b.Mode = BindingMode.TwoWay;

//packagesListBox.SetBinding(DataGrid.SelectedItemProperty, b);

Isso liga o selectedItem do DataGrid ao nosso objeto fictício, mas nós precisamos desse mesmo objeto

para guiar nosso ContentPresenter de detalhes.

9. Crie uma ligação com v como a Source, e nosso ContentControl de “detalhes” como o

SelectedItem como a fonte, mas desta vez para a Propriedade Content de detalhes.

10. Agora você pode executar a aplicação e conforme mudar a seleção no DataGrid, seu

Apresentador de conteúdos deve ser atualizado também.

Page 120: Adotando Silverlight 2.0 - v1 - Exercicios

120

Tarefa 6: Exercício para o usuário

Note que a seleção do DataGrid funciona bem para atualizar detalhes, mas se você fizer alterações nos

campos de detalhes (como mudar o endereço), elas não serão propagadas para a grade. Por quê??

Obviamente porque o Package não implementou ainda o INotifyPropertyChanged. Vá em frente e

implemente-o para poder ver a atualização das mudanças.

Page 121: Adotando Silverlight 2.0 - v1 - Exercicios

121

Apêndice

Seção 1: Respostas das Tarefas

Tarefa 1: Criando uma Ligação Simples usando XAML e DataContext

// No construtor de classes

this.packageGrid.DataContext = TestData.Package;

<!—Updated XAML -->

<TextBlock Style="{StaticResource leftFE}" Grid.Column="1"

Grid.ColumnSpan="3" Grid.Row="0" Text="{Binding TrackingNo}" />

<TextBox Style="{StaticResource leftFE}" Grid.Column="1" Text="{Binding

Address.Name}" TextWrapping="Wrap" Grid.ColumnSpan="3" x:Name="txtName"

Grid.Row="1"/>

<TextBox Style="{StaticResource leftFE}" Grid.Row="2" Grid.Column="1"

Text="{Binding Address.Street}" Grid.ColumnSpan="3" x:Name="txtAddress"/>

<TextBox Style="{StaticResource leftFE}" Grid.Row="3" Grid.Column="1"

Text="{Binding Address.City}" Grid.ColumnSpan="3" x:Name="txtCity"/>

<TextBox Style="{StaticResource leftFE}" Grid.Row="4" Grid.Column="1"

Text="{Binding Address.ZipCode}" x:Name="txtZip"/>

<TextBox Style="{StaticResource leftFE}" Grid.Row="4" Grid.Column="3"

Text="{Binding Address.Country}" x:Name="txtCountry"/>

<TextBox Style="{StaticResource leftFE}" Text="{Binding ShippedDate}"

x:Name="txtShipped" Grid.Row="5" Grid.Column="1" Grid.ColumnSpan="3"/>

Tarefa 2: Ligando a uma Coleção (usando um DataTemplate)

O código para ligar a caixa de listagem é:

this.packagesListbox.ItemsSource = TestData.Packages;

O XAML fica como o de abaixo, embora sua x:Class possa ser chamada de Page em vez de Task2.

<UserControl x:Class="DataLab.Task2"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

Width="400" Height="300" xmlns:DataLab="clr-namespace:DataLab"

xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

mc:Ignorable="d">

<UserControl.Resources>

Page 122: Adotando Silverlight 2.0 - v1 - Exercicios

122

<Style TargetType="TextBlock" x:Key="rightLabel">

<Setter Property="HorizontalAlignment" Value="Right" />

<Setter Property="Margin" Value="0,2,5,2" />

<Setter Property="TextWrapping" Value="Wrap" />

<Setter Property="Foreground" Value="#FF264EA7" />

</Style>

<Style TargetType="FrameworkElement" x:Key="leftFE">

<Setter Property="Margin" Value="5,2,0,2" />

</Style>

<DataTemplate x:Key="PackageTemplate">

<Grid Height="256" Width="336" x:Name="packageGrid" >

<Grid.RowDefinitions>

<RowDefinition Height="*"/>

<RowDefinition Height="*"/>

<RowDefinition Height="*"/>

<RowDefinition Height="*"/>

<RowDefinition Height="*"/>

<RowDefinition Height="*"/>

<RowDefinition Height="*"/>

</Grid.RowDefinitions>

<Grid.ColumnDefinitions>

<ColumnDefinition Width="0.286*"/>

<ColumnDefinition Width="0.238*"/>

<ColumnDefinition Width="0.254*"/>

<ColumnDefinition Width="0.222*"/>

</Grid.ColumnDefinitions>

<TextBlock Style="{StaticResource rightLabel}" Grid.Column="0"

Grid.ColumnSpan="1" Grid.Row="0" Text="Tracking No." Width="Auto" Margin="0,2,-

5,2" />

<TextBlock Style="{StaticResource rightLabel}" Grid.Column="0"

Grid.ColumnSpan="1" Grid.Row="2" Text="Address" x:Name="lblAddress"/>

<TextBlock Style="{StaticResource rightLabel}"

Grid.Column="0" Grid.ColumnSpan="1" Grid.Row="1" Text="Name" x:Name="lblName"/>

<TextBlock Style="{StaticResource rightLabel}"

Text="City" Grid.Row="3" x:Name="lblCity"/>

<TextBlock Style="{StaticResource rightLabel}"

Grid.Row="4" Text="ZipCode" x:Name="lblZip"/>

<TextBlock Style="{StaticResource rightLabel}"

Text="Country" Grid.Row="4" d:LayoutOverrides="Width" Grid.Column="2"

x:Name="lblCountry"/>

<TextBlock Style="{StaticResource rightLabel}"

Text="Shipped" x:Name="lblShipped" Grid.Row="5"/>

<TextBlock Style="{StaticResource leftFE}" Grid.Column="1"

Grid.ColumnSpan="3" Grid.Row="0" Text="{Binding TrackingNo}" />

<TextBox Style="{StaticResource leftFE}" Grid.Column="1" Text="{Binding

Address.Name}" TextWrapping="Wrap" Grid.ColumnSpan="3" x:Name="txtName"

Grid.Row="1"/>

<TextBox Style="{StaticResource leftFE}" Grid.Row="2"

Grid.Column="1" Text="{Binding Address.Street}" Grid.ColumnSpan="3"

x:Name="txtAddress"/>

<TextBox Style="{StaticResource leftFE}" Grid.Row="3"

Grid.Column="1" Text="{Binding Address.City}" Grid.ColumnSpan="3"

x:Name="txtCity"/>

Page 123: Adotando Silverlight 2.0 - v1 - Exercicios

123

<TextBox Style="{StaticResource leftFE}" Grid.Row="4" Grid.Column="1"

Text="{Binding Address.ZipCode}" x:Name="txtZip"/>

<TextBox Style="{StaticResource leftFE}" Grid.Row="4" Grid.Column="3"

Text="{Binding Address.Country}" x:Name="txtCountry"/>

<TextBox Style="{StaticResource leftFE}" Text="{Binding

ShippedDate}" x:Name="txtShipped" Grid.Row="5" Grid.Column="1"

Grid.ColumnSpan="3"/>

</Grid>

</DataTemplate>

</UserControl.Resources>

<Grid x:Name="LayoutRoot" Background="White" >

<ListBox x:Name="packagesListbox" Width="400" ItemTemplate="{StaticResource

PackageTemplate}" />

</Grid>

</UserControl>

Page 124: Adotando Silverlight 2.0 - v1 - Exercicios

124

Tarefa 3: Substituindo a Listbox por um DataGrid

<!— É assim que fica o LayoutRoot no Page.xaml -->

<Grid x:Name="LayoutRoot" Background="White" >

<data:DataGrid AutoGenerateColumns="True" x:Name=" packagesListbox">

</data:DataGrid>

</Grid>

Page 125: Adotando Silverlight 2.0 - v1 - Exercicios

125

Tarefa 4: Personalizando as colunas no DataGrid

<!— é assim que fica o XAML para o Grid do LayoutRoot inteiro -->

<Grid x:Name="LayoutRoot" Background="White">

<data:DataGrid x:Name="packagesListBox" AutoGenerateColumns="False"

Width="600">

<data:DataGrid.Columns>

<data:DataGridTemplateColumn >

<data:DataGridTemplateColumn.Header>

<StackPanel>

<TextBlock Foreground="Red" Text="Tracking No" />

</StackPanel>

</data:DataGridTemplateColumn.Header>

<data:DataGridTemplateColumn.CellTemplate>

<DataTemplate>

<TextBlock Text="{Binding TrackingNo}" />

</DataTemplate>

</data:DataGridTemplateColumn.CellTemplate>

</data:DataGridTemplateColumn>

<data:DataGridTemplateColumn Header="Name">

<data:DataGridTemplateColumn.CellTemplate>

<DataTemplate>

<TextBlock Text="{Binding Address.Name}" />

</DataTemplate>

</data:DataGridTemplateColumn.CellTemplate>

</data:DataGridTemplateColumn>

<data:DataGridTemplateColumn Header="Address">

<data:DataGridTemplateColumn.CellTemplate>

<DataTemplate>

<TextBlock Text="{Binding Address.Street}" />

</DataTemplate>

</data:DataGridTemplateColumn.CellTemplate>

</data:DataGridTemplateColumn>

<data:DataGridTemplateColumn Header="City">

<data:DataGridTemplateColumn.CellTemplate>

<DataTemplate>

<TextBlock Text="{Binding Address.City}" />

</DataTemplate>

</data:DataGridTemplateColumn.CellTemplate>

</data:DataGridTemplateColumn>

<data:DataGridTemplateColumn Header="Delivered">

<data:DataGridTemplateColumn.CellTemplate>

<DataTemplate>

<extended:DatePicker SelectedDate="{Binding DeliveredDate}" />

</DataTemplate>

</data:DataGridTemplateColumn.CellTemplate>

</data:DataGridTemplateColumn>

</data:DataGrid.Columns>

</data:DataGrid>

</Grid>

Page 126: Adotando Silverlight 2.0 - v1 - Exercicios

126

Tarefa 5: Implementando InotifyPropertyChanged (Passo 17)

public class ViewModel : INotifyPropertyChanged

{

protected object _selected;

public object SelectedItem

{

get

{

return _selected;

}

set

{

object old = _selected;

_selected = value;

if (_selected != old)

{

OnPropertyChanged("SelectedItem");

}

}

}

protected void OnPropertyChanged(string name)

{

PropertyChangedEventHandler handler = PropertyChanged;

if (handler != null)

handler(this, new PropertyChangedEventArgs(name));

}

#region INotifyPropertyChanged Members

public event PropertyChangedEventHandler PropertyChanged;

#endregion

}

Page 127: Adotando Silverlight 2.0 - v1 - Exercicios

127

Tarefa 6 - Implementando INotifyPropertyChanged (Passo 9).

b = new Binding("SelectedItem");

b.Source = v;

b.Mode = BindingMode.TwoWay;

details.SetBinding(ContentControl.ContentProperty, b);

Page 128: Adotando Silverlight 2.0 - v1 - Exercicios

128

G- Construindo um Media Player Simples

Primeiros Passos na Construção de um Media Player no Silverlight 2

Neste laboratório você vai construir um media player simples usando o controle <MediaElement> do

Silverlight. Você verá como usar a funcionalidade Play (Executar), Stop (Parar) e Pause (Pausar), bem

como manipular o progresso, o buffering e os marcadores de mídia.

Page 129: Adotando Silverlight 2.0 - v1 - Exercicios

129

Seção 1: Criando uma Página de Mídia

no Silverlight

Usando o Visual Studio, crie um novo projeto do Silverlight e chame-o de MediaLab. Lembre-se de

adicionar um Site (padrão MediaPageWeb) à solução.

Adicione o ‘Bear.wmv’ da pasta de vídeos do Windows Vista em seu PC ao projeto do Silverlight.

Adicione-o também ao projeto da Web no diretório ‘ClientBin’.

Edite o Page.xaml para adicionar um novo <MediaElement> à página:

<MediaElement Source="Bear.wmv" Width="400" Height="300"

x:Name="mel"></MediaElement>

No Site, certifique-se de que o MediaLabTestPage.aspx está definido como a página padrão para

o site. Execute a aplicação e você verá algo como a Figura 1.

Page 130: Adotando Silverlight 2.0 - v1 - Exercicios

130

Figura 1. O Media Player Simples

Você pode ver que o vídeo está sendo reproduzido sem problemas. No próximo passo, você vai

adicionar alguns Controles Play, Stop e Pause simples a ele.

Page 131: Adotando Silverlight 2.0 - v1 - Exercicios

131

Seção 2: Adicionando controles de

Reprodução à sua Mídia

O elemento de mídia do Silverlight fornece controles Play, Stop e Pause que podem ser usados para

controlar a reprodução de vídeo. Neste passo você vai adicionar uma interface de usuário simples que

sobrepõe o vídeo com os controles 'Play', 'Stop' e 'Pause', e você escreverá os manipuladores de eventos

para eles.

Adicione este XAML à interface de usuário. (Note que isso deve ser em um Canvas e não o Grid

padrão. Veja se você pode descobrir como fazer isso!). Ponha-o no lugar apropriado para que os

controles sejam escritos no vídeo. Se você digitar este código, notará que quando especifica os

eventos ‘MouseLeftButtonUp’, o Visual Studio pede que você selecione um novo manipulador

de eventos. Vá em frente e aceite, e os stubs do código serão escritos para você.

<StackPanel Orientation="Horizontal"

Canvas.Top="260" Width="400" Background="Beige">

<TextBlock x:Name="tPlay" Text="Play "

MouseLeftButtonUp="tPlay_MouseLeftButtonUp" />

<TextBlock x:Name="tPause" Text="Pause "

MouseLeftButtonUp="tPause_MouseLeftButtonUp"/>

<TextBlock x:Name="tStop" Text="Stop "

MouseLeftButtonUp="tStop_MouseLeftButtonUp"/>

</StackPanel>

Agora vá ao code-behind (Page.xaml.cs). Se o VS tiver criado os stubs de evento para você,

basta editá-los, caso contrário aqui está um exemplo de um:

private void tPlay_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)

{

}

Chame o método apropriado no MediaElement (se chama ‘mel’) para os manipuladores de

eventos Play, Stop e Pause. Veja um exemplo para o Play:

private void tPlay_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)

{

mel.Play();

}

Quando tiver terminado, execute a aplicação e você poderá executar, parar ou pausar o vídeo.

Você pode ver como isso ficaria na Figura 2.

Page 132: Adotando Silverlight 2.0 - v1 - Exercicios

132

Figura 2. Media Player com controles simples de reprodução

Page 133: Adotando Silverlight 2.0 - v1 - Exercicios

133

Seção 3: Usando o Progresso de

Download e o Progresso de Buffer

O Media Element permite acompanhar o progresso de download e de buffering atuais da mídia.

Eles são acessados usando os eventos DownloadProgressChanged e BufferingProgressChanged.

Você pode conectá-los facilmente. Vamos primeiro ver o DownloadProgressChanged.

Adicione este atributo ao seu Media Element:

DownloadProgressChanged="mel_DownloadProgressChanged"

Adicione este TextBlock ao mesmo <StackPanel> dos blocos de texto Play, Stop e Pause:

<TextBlock x:Name="tStatus" Text=""></TextBlock>

Vá ao code behind e encontre o stub de evento ‘mel_DownloadProgressChanged’. Se ele não

estiver lá, basta adicionar este código:

private void mel_DownloadProgressChanged(object sender, RoutedEventArgs e)

{

tStatus.Text = (e.Source as MediaElement).DownloadProgress.ToString();

}

A fonte do evento é o MediaElement, então você pode converter o e.Source e ter a propriedade

DownloadProgress como é mostrado.

O DownloadProgress contém um número entre 0 e 1, com 1 sendo 100% completo. Se você

executá-lo, verá o status ‘piscar’ rapidamente pois ele está fazendo o download muito rápido a

partir de seu disco rígido. Ao acessar o vídeo pela web, não será tão rápido.

Você pode formatar o valor em uma porcentagem de forma fácil – numérica ou visualmente!

Similarmente, você pode conectar o BufferingProgressChanged e visualizar a propriedade

BufferingProgress. Isso é útil se você tiver um vídeo baixado progressivo.

Page 134: Adotando Silverlight 2.0 - v1 - Exercicios

134

Seção 4: Gerenciando Marcadores de

Mídia

Neste passo vamos ver o uso do Expression Encoder para adicionar marcadores ao vídeo. Vamos

também capturar esses marcadores em nosso código do Silverlight.

Abra o Expression Encoder 2 e adicione o Bear Video (Vídeo do Urso) a ele. Você pode ver isso na Figura

3.

Selecione a guia ‘MetaData’ e abra a exibição ‘Markers’.

Arraste o playhead laranja pela linha do tempo do vídeo (você pode vê-lo logo abaixo da pré-

visualização do vídeo) e pare-o em torno da marca de 2 segundos. Não tem que ser exatamente

aí.

Clique no botão ‘Add’ na exibição de Marcadores. Você verá que um marcador é adicionado

com um Valor Desconhecido

Digite ‘Urso Fareja Água’

Page 135: Adotando Silverlight 2.0 - v1 - Exercicios

135

Em torno da marca de 4,2 segundos, adicione um novo marcador e dê a ele o valor ‘Urso Fareja

Gaivota’.

Ao redor da marca de 8,6 segundos, adicione um novo marcador e dê a ele o valor ‘Urso decide

comer gaivota’.

Ao redor da marca de 8,9 segundos, adicione um novo marcador e dê a ele o valor ‘Gaivota

decide voar para longe’.

Em torno da marca de 11,5 segundos, adicione um novo marcador e dê a ele o valor ‘Urso pensa

‘Droga’’.

Clique no botão ‘Encode’ na parte inferior da tela.

O arquivo irá para a pasta Documents\Expression\Expression Encoder.

Substitua o Bear.wmv em seu projeto pelo que você acabou de codificar.

Em seu projeto do Silverlight, adicione este Atributo ao seu Media Element

MarkerReached="mel_MarkerReached"

Em seu code-behind você pode agora capturar os marcadores que acabou de adicionar ao vídeo,

e escrevê-los para o status do vídeo – veja o código:

private void mel_MarkerReached(object sender,

TimelineMarkerRoutedEventArgs e)

{

tStatus.Text = e.Marker.Text;

}

Você pode ver os resultados aqui:

Page 136: Adotando Silverlight 2.0 - v1 - Exercicios

136

H- Silverlight e Aplicações Conectadas

Conectando suas Aplicações do Silverlight a serviços de Dados e Web

Introdução

O Silverlight 2 fornece uma funcionalidade que permite conectar sua aplicação, conforme ela é

executada no navegador, a recursos baseados em servidor. Há várias maneiras em que o Silverlight pode

se integrar a eles, desde uma simples solicitação HTTP usando um WebClient a solicitações HTTP mais

complexas usando as classes WebRequest/WebResponse, e assim por diante, até classes proxy para

consumo de serviços da Web WCF bem como serviços de dados ADO.

Neste laboratório, você vai construir aplicações que fazem o seguinte

Usam um Cliente da Web para obter Dados de Cidades de uma página da web que serão

plotados em um Mapa do Virtual Earth

Usam uma interação Web Request / Web Response para ver como os dados HTTP podem ser

postados de volta a um servidor Web

Constroem um serviço da Web WCF que fornece dados, e usam um proxy gerado no Silverlight

para consumi-lo

Constroem um serviço de Dados ADO que fornece dados, e o consomem dentro do Silverlight

A terceira seção, usando WCF, é particularmente especial – você verá como o WCF e o Silverlight

trabalham ‘melhor juntos’ para dar a você uma experiência muito melhor como desenvolvedor na

criação da aplicação ligada a dados! Quando comparado ao uso de WebRequest/WebResponse, você

tem muito mais facilidade, e escreve menos de 1/3 do código para fazer a mesma coisa.

Page 137: Adotando Silverlight 2.0 - v1 - Exercicios

137

Seção 1: Usando o WebClient para Ler

Dados Remotos de Ligação para o Silverlight

Neste exercício você vai construir um serviço POX (Plain Old XML) simples que fornece dados de cidades

quando chamado, e vai ligar conteúdos a esses dados dentro do Silverlight.

Você verá as seguintes tecnologias:

WebClient que é usado para solicitações HTTP assíncronas simples.

XDocument, XmlWriter e XmlSerializer para gerenciar Dados XML

O ItemsControl XML para ligação de dados

Tarefa 1: Crie uma aplicação do Silverlight e adicione a Classe

CityData

Crie uma nova aplicação do Silverlight e chame-a de Sample1. Lembre-se de selecionar que quer um Site

como parte da solução quando o fizer.

3. Adicione uma nova Classe, chamada CityData.cs à parte de site de sua solução.

4. Adicione variáveis de membro para CityName (seqüência de caracteres) Latitude (dobro) e

Longitude (dobro).

5. Mantenha o construtor padrão (sem parâmetros)

6. Adicione outro construtor que pega uma seqüência de caracteres e dois dobros e inicializa as

variáveis de membro

Se estiver com dificuldades, verifique os Apêndices para ver o código completo.

Page 138: Adotando Silverlight 2.0 - v1 - Exercicios

138

Tarefa 2: Crie o Serviço XML

Nesta tarefa você vai adicionar um manipulador genérico e fazê-lo retornar dados XML fictícios ao

chamador.

Adicione um novo manipulador Genérico à sua aplicação Web e chame-o de GetData.ashx

O manipulador Genérico precisará de algumas referências adicionadas a ele, portanto adicione o

seguinte código no topo da página de código Ele deve ser adicionado diretamente abaixo de

‘using System.Web’

using System.Linq;

using System.Collections.Generic;

using System.Xml;

using System.Xml.Serialization;

Seus dados de cidades serão mantidos em um objeto List<T>, então adicione a declaração a ele

no topo da classe de manipulador genérico

List<CityData> myCities = new List<CityData>();

O manipulador Genérico processará uma solicitação de entrada usando o código declarado na

função ProcessRequest. Essa função deve criar algumas novas instâncias de CityData e adicioná-

las à lista myCities. Aqui estão alguns exemplos de cidades com longitude e latitude { (London,

51.5,0), (Stratford-upon-Avon, 52.3, -1.71), (Edinburgh, 55.95, -3.16)}. Veja se consegue escrever

o código para elas.

A seguir, você escreverá o código que serializa a List<T> em XML e faz um writeback como uma

resposta ao chamador. Aqui está o código:

XmlSerializer ser = new XmlSerializer(typeof(List<CityData>));

using (XmlWriter writer = XmlWriter.Create(context.Response.OutputStream))

{

context.Response.ContentType = "text/xml";

ser.Serialize(writer, myCities);

}

Se você construiu corretamente o manipulador Genérico, pode agora chamá-lo e ver os resultados como

os da Figura 1.

Page 139: Adotando Silverlight 2.0 - v1 - Exercicios

139

Você pode notar que o nó ArrayOfCityData foi gerado para você pelo XmlWriter. Também foi dado a ele

um namespace e um XSD padrão.

Exercício: Veja se você consegue substituir os namespaces padrão.

Page 140: Adotando Silverlight 2.0 - v1 - Exercicios

140

Tarefa 3: Crie o XAML ao qual fará ligações

O ItemsControl do XAML permite definir como renderizar dados ligados de acordo com um

DataTemplate. Você pode ver aqui como especificar um XAML que defina um único TextBlock que se

liga ao campo CityName. [Isto deve estar no arquivo Page.xaml em seu projeto do Silverlight]

<ItemsControl x:Name="_cities">

<ItemsControl.ItemTemplate>

<DataTemplate>

<TextBlock FontSize="14" Height="30" Text="{Binding CityName}" />

</DataTemplate>

</ItemsControl.ItemTemplate>

</ItemsControl>

Veja se consegue atualizar isso para ter 3 TextBlocks, com os outros dois estando ligados a

Latitude e Longitude respectivamente

Page 141: Adotando Silverlight 2.0 - v1 - Exercicios

141

Tarefa 4: Crie a solicitação e o retorno de chamada do

WebClient

Antes de codificar o WebClient, é uma boa idéia definir uma porta estática para seu projeto da Web ser

executado. Para isso, selecione o projeto da Web no Solution Explorer e pressione F4 para abrir a janela

de propriedades.

Encontre a entrada ‘Use Dynamic Ports’ e defina-a como ‘False’. Salve tudo, encontre a configuração '

Port Number' e coloque ‘8001’.

Agora o URI para o manipulador que retorna XML para você pode ser determinado para estar em:

http://localhost:8001/Sample1Web/GetData.ashx

Page 142: Adotando Silverlight 2.0 - v1 - Exercicios

142

Olhe o code-behind do Page.xaml.cs para o projeto do Silverlight. Você verá um construtor

Page() que tem uma única linha (‘InitializeComponent()’). Adicione novo código a ele para criar

uma nova instância da classe WebClient e instrua-o a fazer download de uma seqüência de

caracteres do URI acima, bem como conectar um retorno de chamada completado do

manipulador de eventos.

Page 143: Adotando Silverlight 2.0 - v1 - Exercicios

143

Tarefa 5: Ligando os Dados no Retorno de Chamada

O ItemsControl que você criou na Tarefa 3 se ligará a um tipo de IEnumerable, então você precisa obter

os resultados do retorno de chamada do serviço de dados e formatá-los apropriadamente.

Quando você especificou o retorno de chamada DownloadStringCompleted na Tarefa 3, o Visual Studio

deve ter criado um manipulador de eventos clichê para você. Se não o fez, aqui está uma cópia dele

para você:

void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs

e)

{

}

Enquanto fizer esta Tarefa, adicione o código que vir a esta função.

Você notará que ela usa um DownloadStringCompletedEventArgs como parâmetro. Os dados da

seqüência de caracteres serão armazenados na propriedade Result, então você pode carregá-los a um

XDocument usando

XDocument xReturn = XDocument.Parse(e.Result);

*Note que você precisará fazer uma referência a System.Xml.Linq e adicionar um ‘using System.Xml.Linq’

ao topo de sua página de código]

Antes de continuar você precisa de uma declaração da classe CityData dentro de seu projeto do

Silverlight também, então agora é um bom momento para adicionar uma e torná-la idêntica à da Tarefa

1. Um dos novos recursos de linguagem do .NET e do Silverlight é a LINQ, que traz alguma programação

funcional ao Silverlight.

Veja como você pode usá-la para criar um IEnumerable de CityData a partir do XML retornado.

IEnumerable<CityData> cities = from city in xReturn.Descendants("CityData")

select new CityData

{

CityName = city.Element("CityName").Value,

Latitude = Convert.ToDouble(city.Element("Latitude").Value),

Longitude = Convert.ToDouble(city.Element("Longitude").Value)

};

Isso vai criar um IEnumerable de objetos CityData com n objetos, onde n é determinado pelo número de nós de CityData dentro do XML. O XML está embutido em código com 3 elementos, então esse código data a você 3 objetos CityData dentro do IEnumerable, e suas propriedades CityName, Latitude e Longitude serão baseadas nos valores dentro dos elementos XML. Agora que você tem seu IEnumerable, basta ligá-lo ao ItemsControl que, se você se lembra, se chama ‘_cities’.

Page 144: Adotando Silverlight 2.0 - v1 - Exercicios

144

_cities.ItemsSource = cities;

Agora você pode executar o projeto e verificar os resultados!

Mais Estudo

Neste exemplo seu ASHX forneceu dados embutidos em código para 1 cidade. Você consegue construí-

lo de modo que aceite parâmetros na seqüência de caracteres URI (isto é,

http://localhost:8001/Sample1Web/GetData.ashx?city=whatever)

Page 145: Adotando Silverlight 2.0 - v1 - Exercicios

145

Seção 2: Usando WebRequest /

WebResponse para obter Dados

Nesta seção do laboratório vamos ver como usar as classes WebRequest e WebResponse para ter um

controle mais refinado sobre a comunicação entre o cliente do Silverlight e o servidor Web. No exemplo

anterior você viu como o WebClient foi usado para chamar uma aplicação de servidor usando seu URI.

Você poderia passar parâmetros a ele na querystring de URI, o que seria um método perfeitamente

válido, mas para o propósito deste exercício, você usará um HTTP-POST para passar os parâmetros como

parte dos cabeçalhos HTTP, e a aplicação de servidor vai abri-los, inspecioná-los e retornar uma

resposta.

Você passará um parâmetro ‘CountryName’, que o servidor vai usar para derivar cidades nesse país e

retorná-las ao chamador.

Page 146: Adotando Silverlight 2.0 - v1 - Exercicios

146

Tarefa 1: Crie o Projeto e instale a aplicação de servidor

Crie uma nova aplicação do Silverlight chamada Sample 2. Lembre-se de selecionar que quer um Site

criado como parte dela.

Adicione uma nova classe CityData, exatamente o mesmo que fez na Seção 1.

Adicione também um Manipulador Genérico e chame-o de GetData.ashx

Certifique-se de que tem as seguintes instruções using no topo do GetData.ashx

using System;

using System.Web;

using System.Linq;

using System.Collections.Generic;

using System.Xml;

using System.Xml.Serialization;

Adicione uma declaração para uma nova List<T> de CityData

List<CityData> myCities = new List<CityData>();

O “burro de carga” dessa função é a função ProcessRequest, então o próximo passo é adicionar

uma verificação de que estamos usando um HTTP-POST

if (context.Request.HttpMethod == "POST")

{}

O HTTP-POST é baseado na comunicação de Formulários da web (não confundir com .NET

WebForms), portanto a coleção de parâmetros é armazenada em campos de Formulários. Para

obter o valor de um campo chamado ‘country’ (país), use o seguinte código:

strCountry = context.Request.Form["country"].ToString();

Escreva uma função que pegue um país e construa uma List<CityData> de várias cidades para

esse país. Aqui estão algumas cidades e suas latitudes e longitudes:

("Paris", 48.87, 2.33));

("Lourdes", 43.1, 0.05));

("Toulouse", 43.6, 1.38));

("London", 51.5, 0));

("Stratford-Upon-Avon", 52.3, -1.71));

("Edinburgh", 55.95, -3.16));

("Berlin", 52.52, 13.42));

("Munich", 48.13, 11.57));

("Hamburg", 53.58, 9.98));

A função está disponível nos apêndices se estiver encontrando problemas.

Page 147: Adotando Silverlight 2.0 - v1 - Exercicios

147

Finalmente, crie um serializador XML e faça um writeback da List<CityData> para o fluxo de

resposta. É idêntico ao que você fez na Seção 1. O código complete está disponível nos

apêndices.

Finalmente, defina as propriedades do projeto da Web para que ele use uma porta estática em vez de

portas dinâmicas, e atribua a ele a porta ‘8002’. Se você não souber fazer isso, verifique a Seção 1.

Page 148: Adotando Silverlight 2.0 - v1 - Exercicios

148

Tarefa 2: Crie a interface de usuário do Silverlight

Vamos criar uma interface de usuário simples que enviará um parâmetro a esse serviço, e obterá os

resultados da List<CityData> para depois ligá-los à interface de usuário.

Adicione um ItemsControl chamado ‘_cities’ que contém um DataTemplate que liga três

controles de TextBlock a CityName, Latitude e Longitude, respectivamente.

Adicione três botões, chamados bUk, bFrance e bGermany respectivamente, com ‘Reino Unido’,

‘França’ e ‘Alemanha’ como suas legendas.

Adicione um manipulador de eventos Click a cada um desses botões

O XAMl completo está disponível nos apêndices para referência.

Page 149: Adotando Silverlight 2.0 - v1 - Exercicios

149

Tarefa 3: Escreva a lógica da interface de usuário e da Ligação

de Dados

Antes de continuar, certifique-se de que tem as referências certas:

using System;

using System.Collections.Generic;

using System.Linq;

using System.Net;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Documents;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Animation;

using System.Windows.Shapes;

using System.IO;

using System.Xml;

using System.Xml.Linq;

Certifique-se também de que adicionou uma referência a ‘System.Net’ e ‘System.Xml.Linq’ usando ‘Add

Reference’ no Solution Explorer.

A seguir você vai escrever uma função getCitiesFromService() que chama o serviço e faz um

HTTP-POST do país para obter a List<CityData> apropriada

Aqui está o código:

private void getCitiesfromService()

{

Uri uri = new Uri("http://localhost:8002/Sample2Web/GetData.ashx");

HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);

//WebRequest request = WebRequest.Create(uri);

request.Method = "POST";

request.ContentType = "application/x-www-form-urlencoded";

request.BeginGetRequestStream(new AsyncCallback(RequestProceed), request);

}

Aqui você instalou o objeto request, e ele está chamando o serviço GetData.ashx. Depois você

começa o fluxo de solicitação (request) e especifica que quando ele estiver pronto você chamará

a função RequestProceed em um retorno de chamada.

Agora vamos ver como construí-lo.

Primeiro, a assinatura do método. Um retorno de chamada dessa natureza é vazio, e aceita um

parâmetro IASyncResult.

void RequestProceed(IAsyncResult asyncResult)

{

}

Você pode ter uma referência ao seu objeto de solicitação a partir deste asyncResult assim:

Page 150: Adotando Silverlight 2.0 - v1 - Exercicios

150

HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState;

Em seguida você deve criar um escritor de fluxo que escreva para esse fluxo de solicitação, e

escreva um valor para o parâmetro ‘country=’. Fechar o fluxo faz o writeout.

StreamWriter postDataWriter = new

StreamWriter(request.EndGetRequestStream(asyncResult));

postDataWriter.Write("country=" + strCountry);

postDataWriter.Close();

Finalmente, você deve definir um retorno de chamada para processar a resposta do serviço, para poder capturar os dados e ligá-los à interface de usuário.

request.BeginGetResponse(new AsyncCallback(ResponseProceed), request);

Agora você precisa construir o manipulador de resposta. Isso tem a mesma assinatura de

método do manipulador de resposta que você viu anteriormente.

void ResponseProceed(IAsyncResult asyncResult)

{ }

A primeira coisa que você terá que fazer nessa função é ter uma referência ao seu objeto de

solicitação novamente:

HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState;

E então precisará de uma referência à sua resposta (response).

HttpWebResponse response =

(HttpWebResponse)request.EndGetResponse(asyncResult);

A resposta é codificada em XML em uma seqüência de caracteres, então você vai ler o fluxo em

uma seqüência de caracteres:

StreamReader responseReader = new StreamReader(response.GetResponseStream());

string responseString = responseReader.ReadToEnd();

Como é XML, você vai carregar isso para um XDocument

XDocument xReturn = XDocument.Parse(responseString);

Para ligar esses dados ao ItemsSource eles precisam ser carregados para um IEnumerable.

Podemos usar a LINQ para fazer isso com facilidade:

IEnumerable<CityData> cities = from city in xReturn.Descendants("CityData")

select new CityData

{

Page 151: Adotando Silverlight 2.0 - v1 - Exercicios

151

CityName = city.Element("CityName").Value,

Latitude = Convert.ToDouble(city.Element("Latitude").Value),

Longitude = Convert.ToDouble(city.Element("Longitude").Value)

};

Finalmente, você precisa ligá-los ao ItemsControl chamado _cities. Tente isto:

_cities.ItemsSource = cities;

Mas isso não funcionaria! A razão é que o WebRequest/WebResponse atua em um thread de segundo

plano e não no thread da interface de usuário, portanto a interface de usuário não seria atualizada.

Felizmente, há uma solução simples usando o Dispatcher. Este código vai funcionar:

Dispatcher.BeginInvoke(() => _cities.ItemsSource = cities);

Para seu exercício final, escreva os manipuladores de eventos para os botões para que quando o

usuário clicar neles, a função getCitiesfromService seja chamada e os dados apropriados sejam

retornados.

Page 152: Adotando Silverlight 2.0 - v1 - Exercicios

152

Seção 3: Construindo e Ligando a um

Serviço WCF

Nas seções anteriores, você viu primeiro como construir um serviço POX simples que era acessado via

um WebClient, e depois como estendê-lo para aceitar verbos HTTP-POST usando

WebRequest/WebResponse assíncrono.

Agora você vai ver como o WCF pode ser usado para implementar esse serviço, simplificando-o e

permitindo que você tenha os aspectos seguros, transacionáveis e confiáveis do WCF, bem como a

facilidade de gerar automaticamente classes proxy cliente que manipulam a ‘conexão’ da comunicação

para você.

Além disso, você notará que os dois exemplos anteriores precisaram do modelo de dados para ser

sincronizados entre o cliente e o servidor – isto é, você precisou de uma classe CityData em ambos, e

qualquer diferença entre o código nessas classes faria sua aplicação ser interrompida.

Ao construir o serviço no WCF você vai atribuir seu serviço e suas classes de dados, e a geração do proxy

vai mantê-los em sincronia.

Page 153: Adotando Silverlight 2.0 - v1 - Exercicios

153

Tarefa 1: Crie o Projeto do Silverlight e adicione o Serviço WCF

Comece criando um novo projeto do Silverlight da forma usual e chame-o de Sample 3.

Adicione uma nova classe CityData ao seu projeto da Web e codifique-o da mesma forma que

fez nas seções anteriores.

Em seguida você vai adicionar os atributos WCF a essa classe, permitindo que o WCF a entenda,

e gerar os proxies de cliente (que você verá depois). No WCF, a declaração de classe deve ser

atribuída como [DataContract] e cada uma das variáveis de membro deve ser atribuída

como [DataMember]. Você pode encontrar a classe completa nos apêndices.

O próximo passo é adicionar um novo Serviço WCF ao seu projeto da Web e chamá-lo de

GetCities.svc

Você notará que isso adiciona três arquivos: GetCities.svc, GetCities.cs e IGetCities.cs

O IGetCities.cs define a interface de seu serviço. Abra-o e você verá uma interface padrão

contendo um único método clichê. Mude-o para corresponder ao seguinte:

[ServiceContract]

public interface IGetCities

{

[OperationContract]

List<CityData> getCities(string strCountry);

}

De forma similar aos contratos de Dados que você viu em sua classe, isso especifica os contratos

de operação para seu serviço.

Agora é hora de editar o código do serviço. Vá à classe GetCities.cs e escreva uma função que

retorna uma List<CityData> para um parâmetro ‘country’ de entrada. Você já fez isso (como

uma função auxiliar) no Cenário 2. O código completo está nos apêndices se você precisar.

O último passo é mudar seu projeto da web que usa portas dinâmicas para o uso de uma porta

estática em 8003. Faça isso, clique com o botão direito no arquivo SVC e visualize-o no

navegador para se certificar de que funciona. Note que é uma boa idéia copiar o URI para a área

de transferência, já que você precisará dele para criar o cliente de serviço na próxima tarefa.

Page 154: Adotando Silverlight 2.0 - v1 - Exercicios

154

Tarefa 2: Crie o Cliente do Silverlight e Ligue-o ao Serviço

Nesta tarefa você vai criar o mesmo cliente do Silverlight que criou na seção anterior, mas agora em vez

de escrever todo o código para manipular o WebRequest e o WebResponse, o gerador de proxy do WCF

fará isso por você! Você também verá como é mais fácil ligar o conteúdo do Silverlight aos dados

retornados, porque em vez de serializar e desserializar o XML, a List<CityData> é passada diretamente

ao chamador.

Em seu projeto do Silverlight, adicione uma nova referência de Serviço e aponte-a ao URI do

serviço que criou na Tarefa 1. Dê a ela o nome de CitiesService.

Instale o XAML para que sua interface de usuário tenha exatamente o mesmo XAML que você

usou na Seção 2, contendo um ItemsControl e 3 botões para Reino Unido, França e Alemanha.

Vá ao seu arquivo Page.xaml.cs e adicione as 3 instruções a seguir no topo da página de código -

os 2 primeiros são para o WCF, o terceiro é para seu proxy de serviço. Neste caso o namespace

Sample3 foi usado. Se você chamou o projeto de algo diferente de Sample 3, use esse nome no

lugar:

using System.ServiceModel;

using System.ServiceModel.Channels;

using Sample3.CitiesService;

Codifique os manipuladores de eventos para Clicar nos Botões. Aqui está o de ‘uk’ como

exemplo. Você deve conseguir copiar isto para os outros:

private void bUk_Click(object sender, RoutedEventArgs e)

{

getCitiesfromService("uk");

}

Isso chama uma função chamada getCitiesfromService, passando a ela uma seqüência de

caracteres. Seu próximo passo é escrever essa função, que usa o proxy de serviço para obter a

List<CityData> de volta do serviço. Como ela está usando o proxy, é muito simples - aqui está o

código:

private void getCitiesfromService(string strCountry)

{

GetCitiesClient myCitiesClient = new GetCitiesClient();

// Note que as três próximas linhas foram truncadas pelo

// Word. Elas deveriam ser uma única linha

myCitiesClient.getCitiesCompleted +=

new EventHandler<getCitiesCompletedEventArgs>

(myCitiesClient_getCitiesCompleted);

myCitiesClient.getCitiesAsync(strCountry);

}

Page 155: Adotando Silverlight 2.0 - v1 - Exercicios

155

Esse código é bastante fácil de entender. Você cria primeiro uma instância da classe proxy,

depois especifica o retorno de chamada, e então chama a função getCitiesAsync passando a ela

a seqüência de caracteres.

O retorno de chamada receberá a List<CityData> de volta, que você pode ligar ao seu

ItemsControl. Aqui está o método completo:

void myCitiesClient_getCitiesCompleted(object sender,

getCitiesCompletedEventArgs e)

{

_cities.ItemsSource = e.Result;

}

Page 156: Adotando Silverlight 2.0 - v1 - Exercicios

156

Apêndices

Seção 1: Respostas das Tarefas

Tarefa 1: Classe CityData

Aqui está o código para a Classe CityData

public class CityData

{

public string CityName { get; set; }

public double Latitude { get; set; }

public double Longitude { get; set; }

public CityData(string strCityName, double nLatitude,

double nLongitude)

{

CityName = strCityName;

Latitude = nLatitude;

Longitude = nLongitude;

}

public CityData()

{

}

}

Tarefa 2: Manipulador Genérico

Aqui está o código para o Manipulador Genérico

<%@ WebHandler Language="C#" Class="GetData" %>

using System;

using System.Web;

using System.Linq;

using System.Collections.Generic;

using System.Xml;

using System.Xml.Serialization;

public class GetData : IHttpHandler {

List<CityData> myCities = new List<CityData>();

public void ProcessRequest (HttpContext context) {

myCities.Add(new CityData("London", 51.5, 0));

myCities.Add(new CityData("Stratford-Upon-Avon", 52.3, -1.71));

myCities.Add(new CityData("Edinburgh", 55.95, -3.16));

XmlSerializerNamespaces ns = new XmlSerializerNamespaces();

ns.Add("", "");

XmlSerializer ser = new XmlSerializer(typeof(List<CityData>));

Page 157: Adotando Silverlight 2.0 - v1 - Exercicios

157

using (XmlWriter writer =

XmlWriter.Create(context.Response.OutputStream))

{

context.Response.ContentType = "text/xml";

ser.Serialize(writer, myCities,ns);

}

}

public bool IsReusable {

get {

return false;

}

}

}

Tarefa 3: XAML Ligado

Aqui está o XAML completo que descreve 2 TextBlocks ligáveis

<ItemsControl x:Name="_cities">

<ItemsControl.ItemTemplate>

<DataTemplate>

<StackPanel Orientation="Horizontal">

<TextBlock FontSize="14" Height="30" Text="{Binding CityName}" />

<TextBlock FontSize="14" Height="0" Text="{Binding Latitude}" />

<TextBlock FontSize="14" Height="0" Text="{Binding Longitude}" />

</StackPanel>

</DataTemplate>

</ItemsControl.ItemTemplate>

</ItemsControl>

Note que a altura dos TextBlocks de Latitude e Longitude foi ocultada para mantê-los invisíveis na renderização.

Tarefa 4: Criando a chamada para o Serviço POX

Aqui está o código para implementar a Tarefa 4.

public Page()

{

InitializeComponent();

WebClient wc = new WebClient();

wc.DownloadStringAsync(new

Uri("http://localhost:8001/Sample1Web/GetData.ashx"));

wc.DownloadStringCompleted += new

DownloadStringCompletedEventHandler(wc_DownloadStringCompleted);

}

Page 158: Adotando Silverlight 2.0 - v1 - Exercicios

158

Tarefa 5: Ligando os Dados no Retorno de Chamada

void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs

e)

{

XDocument xReturn = XDocument.Parse(e.Result);

IEnumerable<CityData> cities = from city in xReturn.Descendants("CityData")

select new CityData

{

CityName = city.Element("CityName").Value,

Latitude = Convert.ToDouble(city.Element("Latitude").Value),

Longitude = Convert.ToDouble(city.Element("Longitude").Value)

};

_cities.ItemsSource = cities;

}

Seção 2:

Tarefa 1: Função GetCities

Aqui está o código completo para a função getCities:

private List<CityData> getCities(string strCountry)

{

List<CityData> ret = new List<CityData>();

switch (strCountry)

{

case "france":

{

ret.Add(new CityData("Paris", 48.87, 2.33));

ret.Add(new CityData("Lourdes", 43.1, 0.05));

ret.Add(new CityData("Toulouse", 43.6, 1.38));

break;

}

case "uk":

{

ret.Add(new CityData("London", 51.5, 0));

ret.Add(new CityData("Stratford-Upon-Avon", 52.3, - .71));

ret.Add(new CityData("Edinburgh", 55.95, -3.16));

break;

}

case "germany":

{

ret.Add(new CityData("Berlin", 52.52, 13.42));

ret.Add(new CityData("Munich", 48.13, 11.57));

ret.Add(new CityData("Hamburg", 53.58, 9.98));

break;

}

default:

{

ret.Add(new CityData("London", 51.5, 0));

ret.Add(new CityData("Stratford-Upon-Avon", 52.3, -1.71));

ret.Add(new CityData("Edinburgh", 55.95, -3.16));

Page 159: Adotando Silverlight 2.0 - v1 - Exercicios

159

break;

}

}

return ret;

}

Esta é a função GetData ProcessRequest:

List<CityData> myCities;

public void ProcessRequest (HttpContext context) {

string strCountry = "";

if (context.Request.HttpMethod == "POST")

{

strCountry = context.Request.Form["country"].ToString();

myCities = getCities(strCountry);

XmlSerializerNamespaces ns = new XmlSerializerNamespaces();

ns.Add("", "");

XmlSerializer ser = new XmlSerializer(typeof(List<CityData>));

using (XmlWriter writer =

XmlWriter.Create(context.Response.OutputStream))

{

context.Response.ContentType = "text/xml";

ser.Serialize(writer, myCities, ns);

}

}

}

Tarefa 2: XAML da interface de usuário

Este é o XAML para a interface de usuário.

<UserControl x:Class="Sample2.Page"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

FontFamily="Trebuchet MS" FontSize="11"

Width="400" Height="300">

<Grid x:Name="LayoutRoot" Background="White">

<StackPanel Orientation="Vertical">

<ItemsControl x:Name="_cities">

<ItemsControl.ItemTemplate>

<DataTemplate>

<StackPanel Orientation="Horizontal">

<TextBlock FontSize="14" Height="30" Text="{Binding

CityName}" ></TextBlock>

<TextBlock FontSize="14" Height="0" Text="{Binding

Latitude}" ></TextBlock>

<TextBlock FontSize="14" Height="0" Text="{Binding

Longitude}" ></TextBlock>

</StackPanel>

</DataTemplate>

</ItemsControl.ItemTemplate>

</ItemsControl>

<Button x:Name="bUk" Content="UK" Click="bUk_Click" />

Page 160: Adotando Silverlight 2.0 - v1 - Exercicios

160

<Button x:Name="bFrance" Content="France" Click="bFrance_Click" />

<Button x:Name="bGermany" Content="Germany" Click="bGermany_Click"

/>

</StackPanel>

</Grid>

</UserControl>

Tarefa 3: Código da Página

using System;

using System.Collections.Generic;

using System.Linq;

using System.Net;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Documents;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Animation;

using System.Windows.Shapes;

using System.IO;

using System.Xml;

using System.Xml.Linq;

namespace Sample2

{

public partial class Page : UserControl

{

IEnumerable<CityData> cities;

string strCountry = "uk";

public Page()

{

InitializeComponent();

}

void RequestProceed(IAsyncResult asyncResult)

{

HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState;

StreamWriter postDataWriter = new

StreamWriter(request.EndGetRequestStream(asyncResult));

postDataWriter.Write("country=" + strCountry);

postDataWriter.Close();

request.BeginGetResponse(new AsyncCallback(ResponseProceed), request);

}

void ResponseProceed(IAsyncResult asyncResult)

{

HttpWebRequest request = (HttpWebRequest)asyncResult.AsyncState;

HttpWebResponse response =

(HttpWebResponse)request.EndGetResponse(asyncResult);

StreamReader responseReader = new

StreamReader(response.GetResponseStream());

string responseString = responseReader.ReadToEnd();

XDocument xReturn = XDocument.Parse(responseString);

IEnumerable<CityData> cities =

from city in xReturn.Descendants("CityData")

select new CityData

{

CityName = city.Element("CityName").Value,

Latitude = Convert.ToDouble(city.Element("Latitude").Value),

Longitude = Convert.ToDouble(city.Element("Longitude").Value)

Page 161: Adotando Silverlight 2.0 - v1 - Exercicios

161

};

Dispatcher.BeginInvoke(() => _cities.ItemsSource = cities);

}

private void getCitiesfromService()

{

Uri uri = new Uri("http://localhost:8002/Sample2Web/GetData.ashx");

HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(uri);

//WebRequest request = WebRequest.Create(uri);

request.Method = "POST";

request.ContentType = "application/x-www-form-urlencoded";

request.BeginGetRequestStream(

new AsyncCallback(RequestProceed), request);

}

private void bUk_Click(object sender, RoutedEventArgs e)

{

strCountry = "uk";

getCitiesfromService();

}

private void bFrance_Click(object sender, RoutedEventArgs e)

{

strCountry = "france";

getCitiesfromService();

}

private void bGermany_Click(object sender, RoutedEventArgs e)

{

strCountry = "germany";

getCitiesfromService();

}

}

}

Seção 3

Tarefa 1: Classe de Dados de Cidades

Esta é a fonte completa para a classe CitiesData com a atribuição do WCF.

using System;

using System.Data;

using System.Configuration;

using System.Linq;

using System.Web;

using System.Web.Security;

using System.Web.UI;

using System.Web.UI.HtmlControls;

using System.Web.UI.WebControls;

using System.Web.UI.WebControls.WebParts;

using System.Xml.Linq;

using System.Runtime.Serialization;

[DataContract]

public class CityData

{

[DataMember]

Page 162: Adotando Silverlight 2.0 - v1 - Exercicios

162

public string CityName { get; set; }

[DataMember]

public double Latitude { get; set; }

[DataMember]

public double Longitude { get; set; }

public CityData(string strCityName, double nLatitude, double nLongitude)

{

CityName = strCityName;

Latitude = nLatitude;

Longitude = nLongitude;

}

public CityData()

{

}

}

Classe de Serviço GetCities

Este é o código para a classe de implementação GetCities.cs para o serviço.

using System;

using System.Collections.Generic;

using System.Linq;

using System.Runtime.Serialization;

using System.ServiceModel;

using System.Text;

// OBSERVAÇÃO: Se você mudar o nome da classe “GetCities” aqui, deve atualizar

também a referência a “GetCities” no Web.config.

public class GetCities : IGetCities

{

public List<CityData> getCities(string strCountry)

{

List<CityData> ret = new List<CityData>();

switch (strCountry)

{

case "france":

{

ret.Add(new CityData("Paris", 48.87, 2.33));

ret.Add(new CityData("Lourdes", 43.1, 0.05));

ret.Add(new CityData("Toulouse", 43.6, 1.38));

break;

}

case "uk":

{

ret.Add(new CityData("London", 51.5, 0));

ret.Add(new CityData("Stratford-Upon-Avon", 52.3, -1.71));

ret.Add(new CityData("Edinburgh", 55.95, -3.16));

break;

}

case "germany":

{

ret.Add(new CityData("Berlin", 52.52, 13.42));

ret.Add(new CityData("Munich", 48.13, 11.57));

ret.Add(new CityData("Hamburg", 53.58, 9.98));

break;

}

Page 163: Adotando Silverlight 2.0 - v1 - Exercicios

163

default:

{

ret.Add(new CityData("London", 51.5, 0));

ret.Add(new CityData("Stratford-Upon-Avon", 52.3, -1.71));

ret.Add(new CityData("Edinburgh", 55.95, -3.16));

break;

}

}

return ret;

}

}