Fui nomeado MVP em Silverlight por mais 1 ano!

MVP_Horizontal_FullColor

Acabei de receber o comunicado oficial da Microsoft de que fui nomeado como MVP de Silverlight pelo segundo ano consecutivo.

Devo esse título a todos vocês que, assim como, eu adoram essa tecnologia. Estou muito animado com todas as novidades que a Microsoft vem anunciando e com a crescente importância que o XAML está ganhando em todas suas principais plataformas de desenvolvimento. Espero continuar fazendo parte dessa comunidade por muitos anos ainda.

Que venha mais um ano de muito XAML para todos nós!

Minhas palestras confirmadas para o TechEd 2011

TechEd Brasil 2011

Saiu a lista de palestras do TechEd Brasil 2011. Para o evento deste ano eu tenho 2 palestras confirmadas, ambas em parceria com o amigo Rodrigo Kono. As palestras serão:

WEB303 – Novidades do Silverlight 5


Venha ver o que há de novo e emocionante no Silverlight 5. Aprenda sobre os recursos para o desenvolvimento de aplicações de negócios, visualização e jogos. Nesta sessão iremos apresentar os principais recursos do Silverlight 5 assim como as atividades futuras desta tecnologia. Esteja preparado para ser produtivo com a última versão do Silverlight.
Palestrantes : Kelps Leite de Sousa e Rodrigo Kono

WPH305 – XAML Avançado para Windows Phone 7


Entenda o que é e como funciona o XAML e aprenda a aproveitar suas capacidades ao máximo, criando aplicações de alta padrão para Windows Phone 7. Nessa palestra serão abordados temas como Binding, Templates, Styles, Value Converters, Dependency e Attached Properties. Também será demonstrado como utilizar ferramentas como Visual Studio e Expression Blend para trabalhar com XAML de forma eficiente e como trabalhar em uma equipe composta de designers e desenvolvedores.
Palestrantes : Kelps Leite de Sousa e Rodrigo Kono

Assim que eu tiver mais detalhes sobre as palestas (como dias e horários) publicarei aqui. Espero vocês lá!

ClickCountTrigger para Silverlight no Expression Gallery

double-click-icon

 

Acabei de publicar uma nova trigger no Expression Gallery, chamada ClickCountTrigger.

Esta trigger serve para simplificar nossa vida quando queremos implementar duplo click em Silverlight, já que a plataforma não fornece uma implementação nativa desse evento. Decidi fazer a trigger de forma genérica e por isso implementei como click count ao invés de double click. Sendo assim, tornei possível que essa trigger seja utilizada para adicionar cliques triplos, quaduplos ou até mais em aplicações Silverlight. Eu não consigo imaginar situações onde mais do que 3 cliques façam algum sentido, mas a minha imaginação não deve limitar a sua Winking smile.

Para configurar a trigger no Expression Blend, basta escolher um controle que deverá ter algum comportamento ao receber um duplo click, adicionar uma Action a ele e depois mudar a trigger padrão dessa Action para ClickCountTrigger e configurar quantos cliques disparam a ação. Abaixo temos alguns prints mostrando a trigger configurada em um botão, em conjunto com a Action ToggleFullScreenAction. Este exemplo é do código do live sample que está na página da trigger na galeria.

Action aplicada no botão

Action ToggleFullScreenAction configurada em um Button

Janela de propriedades da Action, mostrando sua configuração e a da trigger.

Propriedades da trigger ClickCountTrigger e da action de fullscreen

Configuração da trigger e action no xaml

<Button Content="Double-Click to Toggle Full Screen">
    <i:Interaction.Triggers>
        <cnzk:ClickCountTrigger ClickCount="2">
            <cnzk:ToggleFullScreenAction />
        cnzk:ClickCountTrigger>
    i:Interaction.Triggers>
Button>

Declaração no xaml dos namespaces necessários para utilizar tanto a trigger quanto a action:

xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
xmlns:cnzk="http://schemas.cnzk.com.br/library"

Se você utilizar algumas das minhas behaviors, triggers ou actions publicadas no Expression Gallery e tiver algum tipo de feedback ou bug para reportar, pode postar um comentário aqui no blog ou na página do projeto no codeplex.


Espero que esta trigger seja útil para vocês, pois pra mim ela já está sendo.

Busca por controles na árvore visual de aplicações Silverlight e WPF

Ontem eu vi uma pergunta no fórum de Silverlight do MSDN sobre como achar todos os campos TextBox que existem dentro de uma ChildWindow. Já havia algumas respostas para a pergunta mas elas eram bem pontuais para aquele problema específico e necessitavam de várias suposições sobre a estrutura da aplicação para funcionar sem problemas (por exemplo, saber quais tipos de Panel estão sendo usados). Alguns anos atrás, quando eu comecei a fazer uma das minhas primeiras behaviors para publicar na galeria do Expression Blend, eu descobri uma classe do Silverlight (também existe no WPF) que serve justamente para permitir navergarmos na árvore visual de uma aplicação, tanto procurando controles filhos quanto pais de um determinado controle.

A behavior em questão é a que permitia que se fizesse scroll com a wheel do mouse (a rodinha) em controles que apresentassem scrollbar para aplicações feitas em Silverlight 3. Hoje essa behavior praticamente não é mais necessárias pois o Silverlight 4 já implementa esse comportamento nativamente, mas não era esse o caso na época. Para poder implementar essa função eu precisei criar um código que fosse capaz de ler toda a hierarquia visual do controle (vasculhando todos os componentes do qual o template do controle era composto), procurando por algum ScrollViewer. Se eu o encontrasse, a behavior assinava os eventos necessários do controle para que o scroll funcionasse como esperado.

Para navegar pela árvore visual, a classe que utilizei foi a VisualTreeHelper. Voltando à dúvida do fórum, eu decidi criar um método genérico que fosse capaz de encontrar todos os controles de um determinado tipo em uma hierárquia utilizando essa classe, assim estaria garantindo que não precisaria ficar colocando “if”s para cada tipo de painel diferente que aparecesse na minha frente. Sem mais delongas, segue abaixo o método que eu fiz.

public static T[] SearchUIElements(UIElement root, int maxlevel = int.MaxValue, int level = 0) 
   
where T : UIElement
{

   
var result = new List
();

   
if (root != null
) {
       
if (root is
T) {
            result.Add(root
as
T);
        }

       
if
(level < maxlevel) {
           
var childrencount = VisualTreeHelper
.GetChildrenCount(root);
           
DependencyObject
child;
           
for (var
i = 0; i < childrencount; i++) {
                child =
VisualTreeHelper
.GetChild(root, i);
               
if (child is UIElement
) {
                    result.AddRange(SearchUIElements(child
as UIElement
, maxlevel, level + 1));
                }
            }
        }
    }

   
return result.ToArray();
}

Como vocês podem ver o método não é grande e é bem simples. Ele aceita 3 parâmetros:



  • root: controle raiz a partir de onde será iniciada a busca. Por exemplo: LayoutRoot.
  • maxlevel: número máximo de níveis que a busca irá “descer” nos descendentes. Este parâmetro é opcional e o seu valor padrão é int.MaxValue, garantindo que será lida a hierarquia inteira a partir do ponto inicial.
  • level: nível atual da busca. Esse parâmetro é utilizado apenas pela própria função para controlar quando a busca atingirá o nível máximo solicitado pelo usuário.

A função é genérica. O parâmetro T serve para indicar qual tipo de controle será procurado, assim como permitir que o retorno sejá tipado corretamente. A é executada de forma recursiva, chamando a si mesma para cada novo ítem na hierarquia.


O resultado da função é sempre um array do tipo de controle solicitado. Esta função sempre retorna uma array, mesmo que seja vazio (não será retornado null).


Abaixo temos um xaml de exemplo e algumas chamada à função com a descrição do que será encontrado em cada caso.

<Grid x:Name="LayoutRoot">
    <TextBox />
    <TextBox />
    <Grid>
        <TextBox />
        <TextBox />
        <Grid>
            <TextBox />
        Grid>
    Grid>
Grid>
//acha TODOS os 5 campos TextBox 
var textboxes = SearchUIElements<TextBox
>(LayoutRoot);

//acha apenas os 2 campos TextBox de LayoutRoot
textboxes = SearchUIElements<TextBox
>(LayoutRoot, 1);

//acha 4. Os 2 acima e os 2 que estão no primeiro Grid filho
textboxes = SearchUIElements<TextBox
>(LayoutRoot, 2);

//acha TODOS os Grids a partir de LayoutRoot, inclusive ele mesmo
var grids = SearchUIElements<Grid>(LayoutRoot);        

Agora que eu já mostrei como faz, você acha que consegue fazer uma função semelhante que navegue ao contrário na hierárquia? (procurando nos pais de um controle até chegar na raíz da aplicação…). Fica o desafio. Winking smile

Estratégia para lidar com callbacks assíncronos em Silverlight

Recentemente, conversando com alguns desenvolvedores no trabalho e verificando algumas perguntas publicadas nos fóruns do MSDN, eu notei que ainda há uma dificuldade muito grande tanto de compreensão quanto de implementação para trabalhar com métodos assincronos. Isso se torna um problema particularmente importante em aplicações Silverlight pois todas as chamadas a web services, RIA services, web requests, etc são obrigatoriamente feitos de forma assincrona, não havendo opções para executar essas mesmas operações da forma sincrona e linear à qual a maioria dos desenvolvedores está acostumada.

Acontece que desenvolvimento assincrono não é difícil e, depois que você aprende e se acostuma, você acaba percebendo que suas aplicações passam a funcionar muito melhor. Sim, não vou argumentar aqui contra o fato de que é necessário se acostumar e que começo seja realmente algo estranho, mas posso garantir que demora pouco tempo para se acostumar e os benefícios são muitos.

Há muitas abordagens e estratégias possíveis para desenvolvimento assíncrono e eu vou apresentar aqui uma delas que é bem simples e que eu usei em praticamente todos os projetos Silverlight em dos quais participei. Essa abordagem não envolve o uso de nenhum framework ou biblioteca externa e pode ser utilizada tranquilamente também em projetos que não sejam Silverlight.

Digamos que você precisa obter o html de uma página web por algum motivo. Uma forma de fazer isso seria criando uma nova instância de WebClient, assinando o evento DownloadStringCompleted e depois chamando o método DownloadString passando a url. Ok, não é difícil, mas é um código repetitivo que poderia facilmente ser reaproveitado ao invés de ser copiado por toda sua aplicação em todo lugar onde você precisar fazer download de uma página. O que eu costumo fazer para esse tipo de chamada é criar um método estático em uma classe utilitária e simplesmente chamar esse método passando, nesse caso, minha url e um ponteiro de callback. É mais fácil mostrar:

public static void HttpGet(string url, Action<string, Exception> callback) {
   
if (!string
.IsNullOrWhiteSpace(url)) {
       
var client = new WebClient
();
        client.DownloadStringCompleted += (sender, e) => {
           
if (callback != null
) {
                callback(e.Result, e.Error);
            }
        };
        client.DownloadStringAsync(
new Uri(url));
    }
}

Quais são as vantagens desse método:



  • para executá-lo não é necessário instanciar nenhuma classe
  • é facil de reutilizar
  • permite que a lógica da minha aplicação fique um pouco mais simples, já que não me obriga a assinar nenhum evento no meu código

Para executar esse método, eu posso usar 2 abordagens.


Abordagem 1 – Delegar o retorno para outro método. Nessa abordagem eu chamo o método HttpGet passando a url desejada e o ponteiro de um método que será executado quando o request for concluído.

private void LoadData() {
    HttpGet(
"http://kelps.net"
, DataLoaded);
}

private void DataLoaded(string data, Exception
error) {
   
if (error == null
) {
       
//utiliza os dados retornados na variável "data"
    }
}

Abordagem 2 – Utilizar uma expressão lambda para criar um método anônimo inline no meu código, ao invés de criar uma função separada apenas para processar os dados retornados.

HttpGet("http://twitter.com/kelps", (data, error) => {
   
if (error == null
) {
       
//utiliza os dados retornados na variável "data"
    }
});

A única diferença de funcionamento entre as 2 abordagens acima é que na segunda seria possível utilizar variáveis que estiverem no mesmo escopo da chamada que está sendo feita, ao passo que na primeira seria necessário que essas variáveis fossem globais da classe para que isso funcione. Nos projetos em que trabalho eu costumo utilizar ambas as abordagens, de acordo com o que faz mais sentido em cada situação. Expressões lambda são bem concisas e compactas, mas são claras para qualquer desenvolvedor.


Este foi apenas um pequeno exemplo de como trabalhar com chamadas assincronas sem ficar se perdendo com assinaturas e liberação de eventos. Há outras formas mais complexas e robustas de lidar com isso mas a minha intenção hoje era simplesmente mostrar como dá pra trabalhar de forma simples com código assíncrono, mesmo sem utilizar nenhuma biblioteca externa.