Desenvolvimento - Silverlight

Comunicação Local no Silverlight

Neste artigo veremos a utilização de uma técnica interessante que nos permite a comunicação local (no mesmo computador) de aplicações Silverlight.

por Israel Aéce



Como sabemos, podemos consumir serviços WCF em aplicações Silverlight. Isso nos permite colocar alguma tarefa em um serviço na aplicação que faz a hospedagem do serviço ou até mesmo em outros locais, fora do domínio qual ela se encontra. A ideia aqui é permitir a comunicação entre a aplicação Silverlight (que roda dentro do navegador) e serviços que estão hospedados além dessa máquina.

Mas e se precisarmos de algo mais simples, como por exemplo, a comunicação entre duas aplicações Silverlight que rodam dentro do navegador em uma mesma máquina? Utilizar o WCF aqui pode ser extremamente complicado, pois exigirá desenvolvermos uma série de funcionalidades para permitir esse interação, e ainda podemos ter um grande overhead, já que a comunicação não será local e dependerá de que a conexão esteja disponível com o servidor/serviço com qual queremos dialogar.

Para esse tipo de cenário o Silverlight fornece algo bem mais simples de se trabalhar, que é uma funcionalidade conhecida como "comunicação local". A ideia aqui é criar aplicações que geram informações, e aquelas que estiverem interessadas, podem capturá-las e tratar da forma que achar mais conveniente. Esse tipo de comunicação pode ser utilizada por plugins que rodam dentro da mesma página, em páginas diferentes, estando ou não dentro do mesmo domínio.

Através das imagens abaixo podemos analisar os cenários mais comuns. Na primeira imagem (1) temos uma única página ASPX hospedando duas aplicações Silverlight, e se precisarmos efetuar a comunicação entre elas, podemos utilizar o recurso que veremos aqui. Já na segunda imagem (2), temos duas página distintas dentro de um mesmo domínio, mas cada página hospeda uma aplicação Silverlight diferente, e da mesma forma, a comunicação também será possível. Na terceira (3) e última imagem, temos duas aplicações, mas cada uma delas está hospedada em um domínio diferente, mas isso também pode ser permitido, mas falaremos mais disso abaixo.

A implementação é relativamente simples. Para utilizarmos a comunicação local entre aplicações Silverlight, vamos recorrer aos tipos que estão disponível a partir do namespace System.Windows.Messaging. As principais classes que teremos aqui são: LocalMessageSender e LocalMenssageReceiver. Como podemos perceber, a primeira classe é responsável por enviar mensagens, enquanto a segunda se encarrega de receber tais mensagens.

Aqui é importante comentarmos sobre algumas "limitações": tudo o que podemos mandar de um lado à outro deve ser, obrigatoriamente, uma string. Qualquer coisa diferente disso, exigirá um trabalho extra, como serializar o objeto em algum formato Xml ou Json. Dependendo da quantidade de informação que você levar de um lado para outro, considere o uso do Json ao invés do Xml, que é bem menos verboso, e isso pode evitar você atingir o limite máximo do tamanho da string, que deve ser menor ou igual a 40KB. Uma outra alternativa aqui é serializar as informações em formato binário e utilizar Base64 para codificá-la.

Se vamos gerar uma aplicação que gera informações, então temos que recorrer ao uso da classe LocalMessageSender. Em seu construtor ela recebe uma string que corresponde ao nome do destinatário das mensagens geradas por ela. Já o segundo parâmetro do construtor desta classe, recebe uma string contendo o domínio que receberá as mensagens. Você pode utilizar o membro estático chamado Global desta mesma classe, que corresponde à um "*", e instrui o Silverlight que qualquer domínio poderá receber as mensagens. Finalmente, essa mesma classe fornece o método chamado SendAsync, que permite o envio de uma string para aqueles que desejarem capturá-la.

Chega o momento de efetuar a configuração do destinatário. Agora entra em cena a classe LocalMessageReceiver, que recebe em seu construtor três parâmetros: uma string com o nome do destinatário, uma das opções do enumerador ReceiverNameScope e, finalmente, uma coleção de strings, que representa os domínios de quais a aplicação poderá receber mensagens. Essa classe fornece um evento chamado MessageReceived, que é disparado quando uma nova mensagem chega para a aplicação, e para capturá-la, tudo o que precisamos fazer é nos vincularmos a este evento e acessar a mensagem através da propriedade Message do argumento MessageReceivedEventArgs. Para finalizar, o método Listen é responsável por começar a monitorar as mensagens que chegam e entregá-las para o evento acima.

Depois de conhecer essas classes, podemos já fazer o uso delas nas nossas aplicações. Para exemplificar, teremos duas aplicações Silverlight, hospedadas em locais diferentes. A ideia será permitir a comunicação entre elas, e para manter a simplicidade do exemplo, tudo o que a aplicação fará ao receber a mensagem, será colocá-la em um TextBox. Abaixo temos a configuração de uma das aplicações, utilizando tudo o que vimos acima:

public partial class MainPage : UserControl
{
private LocalMessageSender sender;
private LocalMessageReceiver receiver;

public MainPage(string aplicacaoLocal, string aplicacaoRemota)
{
InitializeComponent();

this.sender = new LocalMessageSender(aplicacaoRemota, LocalMessageSender.Global);
this.receiver = new LocalMessageReceiver(
aplicacaoLocal, ReceiverNameScope.Global, LocalMessageReceiver.AnyDomain);

this.receiver.MessageReceived +=
new EventHandler<MessageReceivedEventArgs>(receiver_MessageReceived);
this.receiver.Listen();
}

private void receiver_MessageReceived(object sender, MessageReceivedEventArgs e)
{
this.Mensagens.Text += string.Format("{0}{1}", e.Message, Environment.NewLine);
}

private void EnviaMensagem_Click(object sender, RoutedEventArgs e)
{
this.sender.SendAsync(this.Mensagem.Text);
}
}

O código da outra aplicação será exatamente igual. O único detalhe ali é o construtor da classe MainPage, que recebe como parâmetros o nome local e o nome remoto. O nome remoto é utilizado no construtor da classe LocalMessageSender, pois quero enviar mensagens para este destino. Já o nome local é utilizado pelo LocalMessageReceiver, que é de onde estou interessado em receber mensagens. Mais detalhes sobre estes parâmetros na próxima seção do artigo.

Com isso em funcionamento, ao rodar as aplicações já podemos perceber o resultado. Através da imagem abaixo, vemos que o conteúdo do TextBox da aplicação 01 foi para a aplicação 02 e vice-versa.

Parametrizando as Aplicações

Acima pudemos perceber que a classe que corresponde ao controle do Silverlight (MainPage), foi parametrizada com as duas informações, que correspondem de onde a aplicação quer receber e para onde ela quer enviar informações. Esses nomes não precisam, necessariamente, corresponder a nome de aplicações. A ideia é ter um nome coerente com o tipo de informação que está sendo gerada.

Outro detalhe importante é com relação ao nome que você define para o recebedor das mensagens, pois podemos ter a mesma aplicação carregada em outra instância do navegador, e ao definir o mesmo nome, uma exceção do tipo ListenFailedException será disparada para informar que já existe um recebedor registrado com este mesmo nome.

Para evitar isso, utilizaremos a "ponte" que existe entre o Silverlight e o código HTML que o hospeda. Isso permitirá gerarmos um valor randômico via Javascript e definir na inicialização do plugin, que será encaminhada para aqueles parâmetros que vimos no construtor da classe MainPage. Para que isso funcione, temos que adicionar um novo elemento <param /> na tag object que hospeda a aplicação Silverlight (*.xap), e nomear este parâmetro como initParams. O código abaixo ilustra a adição deste novo parâmetro, com algumas informações suprimidas por questões de espaço.

<object data="data:application/x-silverlight-2," type="application/x-silverlight-2">
<param name="source" value="ClientBin/Aplicacao01.xap"/>
<param
name="initParams"
value="AplicacaoLocal=Aplicacao01,AplicacaoRemota=Aplicacao02" />
<!-- Outros Parâmetros -->
</object>

A configuração refere-se à aplicação 01. Para a aplicação 02, esses parâmetros estão invertidos, já que terão papéis diferentes.

Com isso, o valor colocado ali será entregue em formato de um dicionário ao evento Startup da classe Application, que estará acessível através do arquivo App.xaml. É neste momento que extraímos os parâmetros deste dicionário e preenchemos o construtor da classe MainPage, assim como podemos notar através do código abaixo:

private void Application_Startup(object sender, StartupEventArgs e)
{
this.RootVisual =
new MainPage(e.InitParams["AplicacaoLocal"], e.InitParams["AplicacaoRemota"]);
}

Conclusão: Neste artigo vimos a utilização de uma técnica interessante que nos permite a comuicação local (no mesmo computador) de aplicações Silverlight. Podemos utilizar esse recurso para cada vez mais criar aplicações independentes, e se mais tarde, precisarmos de interação entre elas, podemos recorrer à este recurso simples, mas que traz um grande poder as mesmas.

ComunicaoLocalSL.zip (144.66 kb)

Israel Aéce

Israel Aéce - Especialista em tecnologias de desenvolvimento Microsoft, atua como desenvolvedor de aplicações para o mercado financeiro utilizando a plataforma .NET. Como instrutor Microsoft, leciona sobre o desenvolvimento de aplicações .NET. É palestrante em diversos eventos Microsoft no Brasil e autor de diversos artigos que podem ser lidos a partir de seu site http://www.israelaece.com/. Possui as seguintes credenciais: MVP (Connected System Developer), MCP, MCAD, MCTS (Web, Windows, Distributed, ASP.NET 3.5, ADO.NET 3.5, Windows Forms 3.5 e WCF), MCPD (Web, Windows, Enterprise, ASP.NET 3.5 e Windows 3.5) e MCT.