Adotando Silverlight 2.0 - V1 - Exercicios

  • Uploaded by: Rodrigo Peçanha
  • 0
  • 0
  • May 2020
  • PDF

This document was uploaded by user and they confirmed that they have the permission to share it. If you are author or own the copyright of this book, please report to us by using this DMCA report form. Report DMCA


Overview

Download & View Adotando Silverlight 2.0 - V1 - Exercicios as PDF for free.

More details

  • Words: 28,501
  • Pages: 163
Exercícios Adotando o Silverlight 2.0 [IL400]

1

Í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 2

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 3

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 4

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 5

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

6

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. 2. 3. 4. 5.

Criando uma Aplicação Simples com Silverlight Layout, Controles e Conteúdo de Vetor Elementos e Objetos XAML Manipulação de Entrada e Eventos Modo de Tela Inteira

7

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’. 8

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 9

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

10

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"

11

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

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

13

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="clrnamespace:System.Windows.Controls;assembly=System.Windows.Controls.Extended" xmlns:data="clrnamespace: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, 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.

14

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.

15

Dentro do elemento Canvas, adicione: <Ellipse Canvas.Left="20" Canvas.Top="20" Width="220" Height="220" Fill="#FF3685BB" Stroke="#FF0F588A" StrokeThickness="10" />

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.

16

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 17

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:

Ou pode fornecer texto formatado de múltiplas linhas:

18

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

19

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:

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:

Na seção da página, após o título, adicione o seguinte: <style type="text/css"> .slPlugin { width: 640px; height: 480px; }

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.

20

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">

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

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:

22

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

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:

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:

(É 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
de fechamento, adicione o seguinte: 23



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" > Color

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"> Color Model

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"> <Slider x:Name="AlphaSlider" Margin="20,0,10,0" Maximum="255" Value="255"/> <Slider x:Name="RedSlider" Margin="20,0,10,0" Maximum="255" Value="95"/> <Slider x:Name="GreenSlider" Margin="20,0,10,0" Maximum="255" Value="158"/> <Slider x:Name="BlueSlider" Margin="20,0,10,0" Maximum="255" Value="160"/>

24

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:

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 25

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

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

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:

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

28

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 const const const

int FirstBar = -5; int LastBar = 5; double Mean = 0; 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)

29

{ }

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

30

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.

31

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: <Ellipse x:Name="ellipse" Fill="Blue" Width="100" Height="50" Canvas.Top="20" Canvas.Left="20" />

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

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.

33

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.

34

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

35

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) { }

36

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.

37

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:

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, 38

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.

39

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.

40

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.

41

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.

42

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"> <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/> <SplineDoubleKeyFrame KeyTime="00:00:01" Value="500"/>

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"> <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/> <SplineDoubleKeyFrame KeyTime="00:00:01" Value="500"> <SplineDoubleKeyFrame.KeySpline>

43

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"> <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0"/> <SplineDoubleKeyFrame KeyTime="00:00:01" Value="200"> <SplineDoubleKeyFrame.KeySpline> <SplineDoubleKeyFrame KeyTime="00:00:03" Value="500" />

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

44

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

45

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.

46

    

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.

47

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();

48

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.

49

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.

50

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.

51

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



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

53

_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; }

54

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

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

55



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 de cidades para o país selecionado, que será então ligada ao Items Presenter.

56

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

57

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"> <StackPanel x:Name="stk"> Country

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

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 = '' + Date() + ''; 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 = '' + Date() + ''; 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 59

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/enus/library/bb980118(VS.95).aspx

60

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.

61

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: <StackPanel Orientation="Horizontal" MouseLeftButtonUp="StackPanel_MouseLeftButtonUp">

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

62

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

63

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.

64

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.

65

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.

66

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.

67

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.

68

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.

69

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.

70

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.

71

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:

72

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.

73

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.

74

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> <Storyboard> <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0.5"/>

75

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


76



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> <SolidColorBrush x:Key="Background" Color="#FF003255"/> #FFF9FAFA #FFEDF1F4 #FFE2E8EF #FFAFB9C1 <SolidColorBrush x:Key="ThumbInnerRoundBorderBrush" Color="#FFFFFFFF"/> <SolidColorBrush x:Key="ThumbOuterRoundBorderBrush" Color="#FF000000"/> #CDFFFFFF #45FFFFFF #FFEAF0F0 #FFDCE5EC #FFD5DDE6 #FF798893 #CDFFFFFF #45FFFFFF #FFEAF0F0 #FFDCE5EC

77

#FFD5DDE6 #FF798893 #CDFFFFFF #45FFFFFF #FFF9FAFA #FFEDF1F4 #FFE2E8EF #FFC3C9CD #CDFFFFFF #45FFFFFF
<Storyboard/> <Storyboard/> <Storyboard/> <Path HorizontalAlignment="Stretch" Margin="-0.625,-0.5,-0.75,0.125" VerticalAlignment="Stretch" Stretch="Fill" StrokeThickness="1" Data="M0.12461852,-0.0002709807 L11.250342,-0.0002709807 L11.249977,9.5004301 L6.0003605,17.375883 L0.00038105118,9.7503767 z" Stroke="#FF696A6E"> <Path.Fill>

78

<Style x:Key="VoyagerSlider" TargetType="Slider"> <Setter Property="Template"> <Setter.Value> <Storyboard> <SplineDoubleKeyFrame KeyTime="00:00:00" Value="0.5"/> <Storyboard>

79

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


80



81

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.

82

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.

83

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.

84

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 { }

85

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.

86

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 //{ // double // double // double

Size ArrangeOverride(Size finalSize) currentLeft = 0; currentTop = 0; 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;

87

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

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]

88

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:

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:

89

[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].

90

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.

91

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

92

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)

93

// // // // // // // // //

{ TimelineStackPanel parent = fe.Parent as TimelineStackPanel; if (parent != null) { parent.InvalidateArrange(); } else fe.InvalidateArrange(); }

//}

94

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. enables animation, styling, binding, etc... public static readonly DependencyProperty LowProperty = DependencyProperty.Register("Low", typeof(DateTime), typeof(TimelineStackPanel), null );

This

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

95

11. Adicione uma DependencyProperty High ao nosso TimelineStackPanel (igual ao passo acima mas com Low em vez de High).

96

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(); }

97

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; }

98

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;

99

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.

100

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

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); } }

101

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)

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.

102

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 using using using using using using using using using

System; System.Net; System.Windows; System.Windows.Controls; System.Windows.Documents; System.Windows.Ink; System.Windows.Input; System.Windows.Media; System.Windows.Media.Animation; 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) { }

103

Tarefa 3: Passo 2 -- Implementando o Arrange

protected { double double double

override Size ArrangeOverride(Size finalSize) currentLeft = 0; currentTop = 0; 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"> OU <UserControl x:Class="MargiesTravel.Controls.Page" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

104

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:lcl="clr-namespace:MargiesTravel.Controls" Width="400" Height="300"> <Button Content="button" Width="200" Height="50" /> <Slider Width="300" Value="6" Height="50"/>

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 Isso habilita a animação, o estilo, a ligação, etc... public static readonly DependencyProperty BeginProperty = DependencyProperty.RegisterAttached("Begin", typeof(DateTime), typeof(TimelineStackPanel), null ); Begin.

public static DateTime GetEnd(DependencyObject obj) { return (DateTime)obj.GetValue(EndProperty);

105

} 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(); } }

106

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 Isso habilita a animação, o estilo, a ligação, etc... public static readonly DependencyProperty HighProperty = DependencyProperty.Register("High", typeof(DateTime), typeof(TimelineStackPanel), null); High.

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(); }

107

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; }

108

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

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); } }

109

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">

Tarefa 11: Exercícios para o usuário Veja o SilverlightUserControl1 na solução.

110

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 111

 

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

112

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 lblTrackingNo txtName txtCity txtCountry txtZip txtShipped

Caminho da Propriedade TrackingNo Address.Name Address.City Address.Country Address.ZipCode 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.

113

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>

5.Agora recorte e cole tudo do packageGrid – incluindo o próprio elemento , 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.

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;

114

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

115

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="clrnamespace:System.Windows.Controls;assembly=System.Windows.Controls.Data">

10. Agora adicione uma instância do DataGrid para substituir a Listbox (em LayoutRoot) 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 

116

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:

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.

117

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



2. Adicione uma propriedade anexada à grade de dados para estar no Grid.Row=”0”.

3. Adicione um ContentControl com Grid.Row=“1” que usa o DataTemplate criado anteriormente.

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 118

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.

119

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.

120

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;



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>

121

<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 TargetType="FrameworkElement" x:Key="leftFE"> <Setter Property="Margin" Value="5,2,0,2" />

122



123

Tarefa 3: Substituindo a Listbox por um DataGrid

124

Tarefa 4: Personalizando as colunas no DataGrid <StackPanel> <extended:DatePicker SelectedDate="{Binding DeliveredDate}" />

125

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 }

126

Tarefa 6 - Implementando INotifyPropertyChanged (Passo 9). b = new Binding("SelectedItem"); b.Source = v; b.Mode = BindingMode.TwoWay; details.SetBinding(ContentControl.ContentProperty, b);

127

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.

128

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">



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.

129

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.

130

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">



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.

131

Figura 2. Media Player com controles simples de reprodução

132

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:



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.

133

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’ 134

       

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:

135

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.

136

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.

137

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 using using using



System.Linq; System.Collections.Generic; System.Xml; System.Xml.Serialization;

Seus dados de cidades serão mantidos em um objeto List, então adicione a declaração a ele no topo da classe de manipulador genérico List myCities = new List();





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 em XML e faz um writeback como uma resposta ao chamador. Aqui está o código: XmlSerializer ser = new XmlSerializer(typeof(List)); 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.

138

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.

139

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]



Veja se consegue atualizar isso para ter 3 TextBlocks, com os outros dois estando ligados a Latitude e Longitude respectivamente

140

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 141



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.

142

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 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’. 143

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

144

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.

145

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 using using using using using



System; System.Web; System.Linq; System.Collections.Generic; System.Xml; System.Xml.Serialization;

Adicione uma declaração para uma nova List de CityData List myCities = new List();



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

146



Finalmente, crie um serializador XML e faça um writeback da List 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.

147

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

148

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 using using using using using using using using using using using using using

System; System.Collections.Generic; System.Linq; System.Net; System.Windows; System.Windows.Controls; System.Windows.Documents; System.Windows.Input; System.Windows.Media; System.Windows.Media.Animation; System.Windows.Shapes; System.IO; System.Xml; 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 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: 149

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 cities = from city in xReturn.Descendants("CityData") select new CityData {

150

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.

151

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.

152

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

153

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 é 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 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 (myCitiesClient_getCitiesCompleted); myCitiesClient.getCitiesAsync(strCountry); }

154





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 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; }

155

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 myCities = new List(); 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));

156

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 <StackPanel Orientation="Horizontal">

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); }

157

Tarefa 5: Ligando os Dados no Retorno de Chamada void wc_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e) { XDocument xReturn = XDocument.Parse(e.Result); IEnumerable 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 getCities(string strCountry) { List ret = new List(); 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));

158

break; } } return ret; }

Esta é a função GetData ProcessRequest: List 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)); 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"> <StackPanel Orientation="Vertical"> <StackPanel Orientation="Horizontal"> <Button x:Name="bUk" Content="UK" Click="bUk_Click" />

159

<Button x:Name="bFrance" Content="France" Click="bFrance_Click" /> <Button x:Name="bGermany" Content="Germany" Click="bGermany_Click" />
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 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 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)

160

}; 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 using using using using using using using using using using using

System; System.Data; System.Configuration; System.Linq; System.Web; System.Web.Security; System.Web.UI; System.Web.UI.HtmlControls; System.Web.UI.WebControls; System.Web.UI.WebControls.WebParts; System.Xml.Linq; System.Runtime.Serialization;

[DataContract] public class CityData { [DataMember]

161

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 using using using using using

System; System.Collections.Generic; System.Linq; System.Runtime.Serialization; System.ServiceModel; 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 getCities(string strCountry) { List ret = new List(); switch (strCountry) { case "france": { ret.Add(new ret.Add(new ret.Add(new break; } case "uk": { ret.Add(new ret.Add(new ret.Add(new break; } case "germany": { ret.Add(new ret.Add(new ret.Add(new break; }

CityData("Paris", 48.87, 2.33)); CityData("Lourdes", 43.1, 0.05)); CityData("Toulouse", 43.6, 1.38));

CityData("London", 51.5, 0)); CityData("Stratford-Upon-Avon", 52.3, -1.71)); CityData("Edinburgh", 55.95, -3.16));

CityData("Berlin", 52.52, 13.42)); CityData("Munich", 48.13, 11.57)); CityData("Hamburg", 53.58, 9.98));

162

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; } }

163

Related Documents


More Documents from ""