Entre plataformas

Compartilhar código de interface de usuário entre plataformas móveis com Xamarin.Forms

Jason Smith

Com Xamarin, você pode usar C# para criar aplicativos móveis nativos excelentes e compartilhar grande parte do seu código entre plataformas. Tradicionalmente, era preciso criar uma interface de usuário separada para cada plataforma de destino. Mas com Xamarin.Forms, é possível criar uma interface de usuário que renderiza nativamente em todos eles.

Xamarin.Forms é uma camada de abstração de interface de usuário entre plataformas. Você pode usá-la para compartilhar interfaces de usuário e código back-end entre plataformas e ainda proporcionar uma experiência de interface de usuário totalmente nativa. Como são nativos, seus controles e widgets têm a aparência de cada plataforma de destino.

Xamarin.Forms é totalmente compatível com o padrão de design Model-View-ViewModel (MVVM), pelo que pode associar elementos da página a propriedades e comandos em uma classe de modelo de exibição.

Se preferir criar páginas declarativamente, você pode usar XAML, uma linguagem de marcação com recursos como dicionários de recursos, recursos dinâmicos, associação de dados, comandos, disparadores e comportamentos.

Xamarin.Forms tem uma API pequena e fácil de usar. Se precisar de acesso mais profundo à interface de usuário de uma plataforma, você pode criar exibições personalizadas e renderizador específicos da plataforma. Isto parece complicado, mas é mesmo somente uma forma de chegar a interfaces de usuário nativas e existem muitas amostras no site de Xamarin para ajudar você com isso.

Pode começar por usar quaisquer páginas, layouts e exibições prontos fornecidos com Xamarin.Forms. À medida que seu aplicativo evolui e você descobre novos casos de uso e oportunidades de design, pode ter de se apoiar no suporte de Xamarin.Forms para XAML, MVVM, renderizadores específicos da plataforma personalizados e uma variedade de outros recursos como animações e modelos de dados.

Vou fornecer uma visão geral da funcionalidade Xamarin com exemplos específicos para ilustrar como compartilhar a maior parte do seu código de interface de usuário em plataformas de destino diferentes e incorporar o código específico da plataforma quando necessário.

Introdução

Primeiro, abra o gerenciador de pacote NuGet no Visual Studio ou Xamarin Studio e verifique novas versões de Xamarin.Forms. Como não será notificado sobre novas versões somente abrindo uma solução Xamarin.Forms em IDE, verificar a existência de pacotes NuGet atualizados é a única forma de garantir que você tem as mais recentes melhorias.

Criar uma solução Xamarin.Forms Quando tiver a certeza de que tem a mais recente versão de Xamarin.Forms, crie uma solução Aplicativo em branco (Xamarin.Forms Portable).

Sua solução tem três projetos específicos da plataforma e uma biblioteca de classes portátil (PCL). Crie suas páginas na PCL. Comece por criar uma página de logon básica.

Usar C# para criar uma página Adicione uma classe a seu projeto PCL. Adicione controles (chamados “exibições” no Xamarin), conforme mostrado na Figura 1.

Figura 1 Adicionando exibições (controles)

public class LogInPage : ContentPage
{
  public LogInPage()
  {
    Entry userEntry = new Entry { Placeholder = "Username" };
    Entry passEntry =
      new Entry { Placeholder = "Password", IsPassword = true };
    Button submit = new Button { };
    Content = new StackLayout
    {
      Padding = 20,
      VerticalOptions = LayoutOptions.Center,
      Children = { userEntry, passEntry, submit }
    };
  }
}

Para mostrar a página quando um aplicativo é iniciado, abra a classe MyApp e atribua uma instância da mesma à propriedade MainPage:

public class MyApp : Application
{
  public MyApp()
  {
    MainPage = new LogInPage();
  }
}

Este é um bom momento para discutir a classe Application. Começando na v1.3.0, todos os aplicativos Xamarin.Forms conterão esta classe. É o ponto de entrada de um aplicativo Xamarin.Forms e, entre outras coisas, fornece eventos de ciclo de vida, bem como um repositório de dados persistentes (o dicionário de propriedades) para quaisquer dados serializáveis. Se precisar obter uma instância dessa classe a partir de qualquer lugar em seu aplicativo, você pode usar a propriedade estática Application.Current.

No exemplo anterior, eu removi o código padrão dentro da classe de aplicativo e substitui por uma única linha de código que faz a LogInPage aparecer quando você executa o aplicativo. Aparece quando o aplicativo é executado, pois este código atribui uma página (a LogInPage) à propriedade MainPage da classe Application. Você precisa defini-la no construtor da classe Application.

Também é possível substituir três métodos nesta classe:

  • O método OnStart, que é invocado quando um aplicativo é executado pela primeira vez.
  • O método OnSleep, que é invocado quando o aplicativo está prestes a entrar em um estado de segundo plano.
  • O método OnResume, que é invocado quando um aplicativo retorna de um estado de segundo plano.

Em geral, as páginas não são muito interessantes até você as conectar a algum tipo de dados ou comportamento, portanto, vou mostrar como fazer isso.

Vincular uma página a dados Se vir o padrão de design MVVM, pode criar uma classe como a da Figura 2 que implementa a interface INotifyPropertyChanged.

Figura 2 Implementando a interface INotifyPropertyChanged

public class LoginViewModel : INotifyPropertyChanged
{
  private string usrnmTxt;
  private string passWrd;
  public string UsernameText
  {
    get { return usrnmTxt; }
    set
    {
      if (usrnmTxt == value)
        return;
      usrnmTxt = value;
      OnPropertyChanged("UsernameText");
    }
  }
  public string PassWordText
  {
    get { return passWrd; }
    set
    {
      if (passWrd == value)
        return;
      passWrd = value;
      OnPropertyChanged("PassWrd");
    }
  }
  public event PropertyChangedEventHandler PropertyChanged;
  private void OnPropertyChanged(string propertyName)
  {
    if (PropertyChanged != null)
    {
      PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
    }
  }
}

É possível vincular as exibições de sua página de logon às propriedades dessa classe, como mostrado na Figura 3.

Figura 3 Vinculando exibições às propriedades da classe

public LogInPage()
{
  Entry userEntry = new Entry { Placeholder = "Username" };
  userEntry.SetBinding(Entry.TextProperty, "UsernameText");
  Entry passEntry =
    new Entry { Placeholder = "Password", IsPassword = true };
  passEntry.SetBinding(Entry.TextProperty, "PasswordText");
  Button submit = new Button { Text = "Submit" };
  Content = new StackLayout
    {
      Padding = 20,
      VerticalOptions = LayoutOptions.Center,
      Children = { userEntry, passEntry, submit }
    };
  BindingContext = new LoginViewModel();
}

Para saber mais sobre como vincular a dados em seu aplicativo Xamarin.Forms, consulte “From Data Bindings to MVVM” no site do Xamarin em bit.ly/1uMoIUX.

Usar XAML para criar uma página Para aplicativos menores, criar suas interfaces de usuário usando C# é uma abordagem perfeitamente razoável. No entanto, à medida que seu aplicativo cresce, é possível deparar-se com a escrita de muito código repetitivo. É possível evitar esse problema usando XAML ao invés de código C#.

Adicione um item de página XAML de formulários a seu projeto PCL. Adicione uma marcação à página, como mostrado na Figura 4.

Figura 4 Adicionando marcação a uma página XAML de formulários

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
  xmlns:x="https://schemas.microsoft.com/winfx/2009/xaml"
  x:Class="Jason3.LogInPage"
  xmlns:local="clr-namespace:XamarinForms;assembly=XamarinForms"> 
    <StackLayout VerticalOptions="Center">
      <StackLayout.BindingContext>
        <local:LoginViewModel />
      </StackLayout.BindingContext>
      <Entry Text="{Binding UsernameText}" Placeholder="Username" />
      <Entry Text="{Binding PasswordText}"
        Placeholder="Password" IsPassword="true" />
      <Button Text="Login" Command="{Binding LoginCommand}" />
    </StackLayout>
</ContentPage>

O XAML na Figura 4 pode parecer familiar caso você tenha gravado aplicativos do Windows Presentation Foundation (WPF). No entanto, as marcas são diferentes, pois referem-se a tipos Xamarin.Forms. Além disso, o elemento raiz se refere a uma subclasse da classe Xamarin.Forms.Element. Todos os arquivos XAML em um aplicativo Xamarin.Forms devem fazer isso.

Para saber mais sobre como usar XAML para criar páginas em um aplicativo Xamarin.Forms, consulte “XAML for Xamarin.Forms” no site do Xamarin em bit.ly/1xAKvRN.

Design para uma plataforma específica

Xamarin.Forms tem um número relativamente pequeno de APIs em comparação a outras plataformas móveis nativas. Isso facilita a criação de suas páginas, mas às vezes o Xamarin.Forms não renderiza uma exibição exatamente da maneira desejada em um ou mais destinos de sua plataforma.

Caso se depare com esta barreira, basta criar uma exibição personalizada, que é somente uma subclasse de qualquer exibição disponível no Xamarin.Forms.

Para exibir o modo de exibição em uma página, estenda o renderizador de exibição. Em casos mais avançados, você pode até mesmo criar um renderizador personalizado do zero. O código de renderizador personalizado é específico para uma plataforma, pelo que é possível compartilhá-lo. Mas esta abordagem pode valer o custo para introduzir recursos nativos e usabilidade no aplicativo.

Criar uma exibição personalizada Primeiro, crie uma subclasse de qualquer exibição disponível no Xamarin.Forms. Há código para duas exibições personalizadas:

public class MyEntry : Entry {}
public class RoundedBoxView : BoxView {}

Estender um renderizador existente A Figura 5 estende o renderizador que renderiza a exibição Entrada da plataforma iOS. Deve colocar esta classe no projeto da plataforma iOS. Este renderizador define a cor e o estilo do campo de texto nativo subjacente.

Figura 5 Estendendo um renderizador existente

[assembly: ExportRenderer (typeof (MyEntry), typeof (MyEntryRenderer))]
namespace CustomRenderer.iOS
{
  public class MyEntryRenderer : EntryRenderer
  {
    protected override void OnElementChanged
      (ElementChangedEventArgs<Entry> e)
    {
      base.OnElementChanged (e);
      if (e.OldElement == null) {
        var nativeTextField = (UITextField)Control;
        nativeTextField.BackgroundColor = UIColor.Gray;
        nativeTextField.BorderStyle = UITextBorderStyle.Line;
      }
    }
  }
}

É possível fazer quase tudo o que desejar em seu renderizador, pois está referenciando APIs nativas. Se desejar ver a amostra que contém este trecho, consulte “Xamarin.Forms Custom Renderer” em bit.ly/1xTIjmR.

Criar um renderizador do zero É possível criar um renderizador completamente novo que não estenda quaisquer outros renderizadores. Terá mais trabalho, mas faz sentido criar um se quiser fazer uma destas coisas:

  • Substituir o renderizador de uma exibição.
  • Adicionar um novo tipo de exibição à sua solução.
  • Adicionar um controle nativo ou página nativa à sua solução.

Por exemplo, se desejar adicionar um controle UIView nativo a uma página na versão iOS de seu aplicativo, é possível adicionar um renderizador personalizado a seu projeto iOS, como mostrado na Figura 6.

Figura 6 Adicionando um controle UIView nativo a um aplicativo iOS com um renderizador personalizado

[assembly: ExportRendererAttribute(typeof(RoundedBoxView),
  typeof(RoundedBoxViewRenderer))]
namespace RBVRenderer.iOS
{
  public class RoundedBoxViewRenderer :
    ViewRenderer<RoundedBoxView,UIView>
  {
    protected override void OnElementChanged(
      ElementChangedEventArgs<RoundedBoxView> e)
    {
      base.OnElementChanged(e);
      var rbvOld = e.OldElement;
      if (rbvOld != null)
      {
        // Unhook any events from e.OldElement here.
      }
      var rbv = e.NewElement;
      if (rbv != null)
      {
        var shadowView = new UIView();
        // Set properties on the UIView here.
        SetNativeControl(shadowView);
        // Hook up any events from e.NewElement here.
      }
    }
  }
}

O padrão geral que aparece neste renderizador garante que você possa usá-lo em layouts virtualizados tais como uma exibição de lista, que abordaremos mais tarde.

Se desejar ver a amostra que contém este trecho, consulte bit.ly/xf-customrenderer.

Adicionar propriedades a uma exibição personalizada É possível adicionar propriedades a uma exibição personalizada, mas certifique-se que os torne vinculáveis para que possa vinculá-los a propriedades em um modelo de exibição ou a outros tipos de dados. Eis uma propriedade vinculável na exibição personalizada RoundedBoxView:

public class RoundedBoxView : BoxView
{
  public static readonly BindableProperty CornerRadiusProperty =
    BindableProperty.Create<RoundedBoxView, double>(p => p.CornerRadius, 0);
  public double CornerRadius
  {
    get { return (double)base.GetValue(CornerRadiusProperty);}
    set { base.SetValue(CornerRadiusProperty, value);}
  }
}

Para conectar novas propriedades a seu renderizador, substitua o método OnElementPropertyChanged do renderizador e adicione código que seja executado quando a propriedade muda:

protected override void OnElementPropertyChanged(object sender,    
  System.ComponentModel.PropertyChangedEventArgs e)
{
  base.OnElementPropertyChanged(sender, e);
  if (e.PropertyName ==
    RoundedBoxView.CornerRadiusProperty.PropertyName)
      childView.Layer.CornerRadius = (float)this.Element.CornerRadius;
}

Para saber mais sobre a criação de exibições personalizadas e renderizadores personalizados, consulte “Customizing Controls for Each Platform” em bit.ly/11pSFhL.

Páginas, layouts e exibições: Os elementos constitutivos do Xamarin.Forms

Apresentei alguns elementos, mas existem muitos mais. Este seria um bom momento para visitá-los.

Os aplicativos Xamarin.Forms contêm páginas, layouts e exibições. Uma página contém um ou mais layouts e um layout contêm uma ou mais exibições. O termo "exibição" é usado no Xamarin para descrever o que pode ser usado para invocar um controle. No total, a estrutura do Xamarin.Forms contém cinco tipos de página, sete layouts e 24 tipos de exibição. É possível ler mais sobre eles em xamarin.com/forms. Visitarei alguns tipos de página importantes mais tarde, mas primeiro despenderei de um momento para rever alguns dos layouts que é possível usar em seu aplicativo. Xamarin.Forms contém quatro layouts primários:

  • StackLayout: O StackLayout posiciona elementos filho em uma única linha que pode ser orientada vertical ou horizontalmente. É possível aninhar StackLayouts para criar complexas hierarquias visuais. É possível controlar como as exibições são organizadas em um StackLayout usando as propriedades VerticalOptions e HorizontalOptions de cada exibição filho.
  • Grade: A Grade organiza as exibições em linhas e colunas. Este layout é semelhante ao que você obtém com WPF e Silverlight, com a exceção de que é possível adicionar espaçamento entre linhas e colunas. Você faz isso usando as propriedades RowSpacing e ColumnSpacing da Grade.
  • RelativeLayout: O RelativeLayout posiciona exibições usando as restrições em relação a outras exibições.
  • AbsoluteLayout: O AbsoluteLayout permite posicionar as exibições filho de duas formas: em uma posição absoluta ou proporcionalmente em relação ao pai. Pode ser útil se você planejar criar hierarquias divididas e sobrepostas. A Figura 7 mostra um exemplo.

Figura 7 Usando o AbsoluteLayout

void AbsoluteLayoutView()
{
  var layout = new AbsoluteLayout();
  var leftHalfOfLayoutChild = new BoxView { Color = Color.Red };
  var centerAutomaticallySizedChild =
    new BoxView { Color = Color.Green };
  var absolutelyPositionedChild = new BoxView { Color = Color.Blue };
  layout.Children.Add(leftHalfOfLayoutChild,
    new Rectangle(0, 0, 0.5, 1),
    AbsoluteLayoutFlags.All);
  layout.Children.Add(centerAutomaticallySizedChild,
    new Rectangle(
    0.5, 0.5, AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize),
    AbsoluteLayoutFlags.PositionProportional);
  layout.Children.Add(
    absolutelyPositionedChild, new Rectangle(10, 20, 30, 40));
}

Observe que todos os layouts lhe dão uma propriedade chamada Children. É possível usar essa propriedade para acessar membros adicionais. Por exemplo, é possível usar a propriedade Children do layout da Grade para adicionar e remover linhas e colunas, bem como especificar intervalos de linha e coluna.

Mostrar dados em uma lista de rolagem

É possível mostrar dados em uma lista de rolagem usando um formulário de exibição de lista (chamado ListView). Esta exibição funciona bem pois os renderizadores para cada célula na lista são virtualizados. Como cada célula é virtualizada, é importante que você trate corretamente o evento OnElementChanged de quaisquer renderizadores personalizados criados cria para células ou listas usando um padrão semelhante ao mostrado anteriormente.

Primeiro, defina uma célula, como mostrado na Figura 8. Todos os modelos de dados de ListView devem usar uma Célula como o elemento raiz.

Figura 8 Definindo uma célula para uma ListView

public class MyCell : ViewCell
{
  public MyCell()
  {
    var nameLabel = new Label();
    nameLabel.SetBinding(Label.TextProperty, "Name");
    var descLabel = new Label();
    descLabel.SetBinding(Label.TextProperty, "Description");
    View = new StackLayout
    {
      VerticalOptions = LayoutOptions.Center,
      Children = { nameLabel, descLabel }
    };
  }
}

Em seguida, defina uma fonte de dados e ajuste a propriedade ItemTemplate da ListView como um novo modelo de dados. O modelo de dados é baseado na classe MyCell criada anteriormente:

var items = new[] {
  new { Name = "Flower", Description = "A lovely pot of flowers." },
  new { Name = "Tuna", Description = "A can of tuna!" },
  // ... Add more items
};
var listView = new ListView
{
  ItemsSource = items,
  ItemTemplate = new DataTemplate(typeof(MyCell))
};

É possível fazê-lo em XAML usando a seguinte marcação:

<ListView ItemsSource="{Binding Items}">
  <ListView.ItemTemplate>
    <ViewCell>
      <StackLayout VerticalOptions="center">
      <Label Text="{Binding Name}" />
      <Label Text="{Binding Description}" />
      </StackLayout>
    </ViewCell>
  </ListView.ItemTemplate>
</ListView>

A maioria dos aplicativos contém mais de uma página, pelo que precisará habilitar usuários para navegar de uma página para outra. As páginas a seguir têm suporte interno navegação entre páginas e suportam a apresentação de páginas modal em tela inteira:

  • TabbedPage
  • MasterDetailPage
  • NavigationPage
  • CarouselPage

É possível adicionar suas páginas como filhos a qualquer uma destas quatro páginas e obter a navegação gratuitamente.

A TabbedPage A TabbedPage mostra uma matriz de guias na parte superior de uma tela. Supondo que seu projeto PCL contém páginas nomeadas LogInPage, DirectoryPage e AboutPage, é possível adicioná-las a uma TabbedPage usando o código seguinte:

var tabbedPage = new TabbedPage
{
  Children =
  {
    new LoginPage { Title = "Login", Icon = "login.png" },
    new DirectoryPage { Title = "Directory", Icon = "directory.png" },
    new AboutPage { Title = "About", Icon = "about.png" }
  }
};

Nesse caso, é importante definir as propriedades Title e Icon de cada página para que apareça algo nas guias da página. Nem todas as plataformas renderizam ícones. Depende da criação da guia da plataforma.

Se abrir esta página em um dispositivo móvel, a primeira guia aparece como selecionada. No entanto, é possível alterá-lo definindo a propriedade CurrentPage da página TabbedPage.

A NavigationPage Uma NavigationPage gerencia a navegação e a experiência do usuário de uma pilha de páginas. Esta página oferece o tipo mais comum de padrão de navegação de aplicativos móveis. Eis como adicionar suas páginas à mesma:

var loginPage = new LoginPage();
var navigationPage = new NavigationPage(loginPage);
loginPage.LoginSuccessful += async (o, e) => await
  navigationPage.PushAsync(new DirectoryPage());

Observe o uso de PushAsync como uma forma de os usuários navegarem até uma página específica (neste caso, a DirectoryPage). Em uma NavigationPage, você “empurra” páginas para uma pilha e, em seguida, “indica”-as quando os usuários navegam para a página anterior.

Os métodos PushAsync e PopAsync de uma NavigationPage são assíncronos, pelo que seu código deve esperar por eles e não empurrar nem indicar quaisquer páginas novas enquanto a tarefa está em execução. A tarefa de cada método retorna depois de a animação de um push ou indicação concluir.

Para sua conveniência, todas as exibições, layouts e páginas do Xamarin.Forms contêm uma propriedade Navigation. Esta propriedade é uma interface proxy que contém os métodos PushAsync e PopAsync de uma instância NavigationPage. É possível suar essa propriedade para navegar até uma página em vez de invocar os métodos PushAsync e PopAsync na instância NavigationPage diretamente. Também é possível usar NavigationProperty para chegar aos métodos PushModalAsync e PopModalAsync. É útil se desejar substituir o conteúdo de toda a tela por uma nova página modal. Não precisa ter uma NavigationPage na pilha pai de uma exibição para usar a propriedade Navigation de uma exibição, mas as operações PushAsync/PopAsync não modais podem falhar.

Uma observação sobre padrões de design Como uma prática geral, considere adicionar NavigationPages como filhos a TabbedPages e TabbedPages como filhos a MasterDetailPages. Certos tipos de padrão podem causar uma experiência de usuário indesejável. Por exemplo, a maioria das plataformas desaconselha a adição de uma TabbedPage como filho de uma NavigationPage.

Animar exibições em uma página

É possível criar uma experiência mais envolvente animando as exibições em uma página, o que pode ser feito de duas maneiras. Escolha animações internas que vêm com o Xamarin.Forms ou crie um por conta própria usando a API de animação.

Por exemplo, poderia criar um efeito de esmaecimento invocando a animação FadeTo de uma exibição. A animação FadeTo se baseia em uma exibição pelo que é fácil de usar:

async Task SpinAndFadeView(View view)
{
  await view.FadeTo(20, length: 200, easing: Easing.CubicInOut);
}

É possível encadear uma série de animações usando a palavra-chave await. Cada animação é executada após a conclusão da anterior. Por exemplo, é possível girar uma exibição mesmo antes de a tornar centrar com esmaecimento:

async Task SpinAndFadeView(View view)
{
  await view.RotateTo(180);
  await view.FadeTo(20, length: 200, easing: Easing.CubicInOut);
}

Se tiver problemas a alcançar o efeito desejado, é possível usar a API de animação completa. No código seguinte, a exibição esmaece a metade da rotação:

void SpinAndFadeView(View view)
{
  var animation = new Animation();
  animation.Add(0, 1, new Animation(
    d => view.Rotation = d, 0, 180, Easing.CubicInOut));
  animation.Add(0.5, 1, new Animation(
    d => view.Opacity = d, 1, 0, Easing.Linear));
  animation.Commit(view, "FadeAndRotate", length: 250);
}

Este exemplo compõe cada animação em uma instância Animation única e, em seguida, executa toda a sequência da animação usando o método Commit. Como esta animação não está vinculada a uma exibição específica, é possível aplicar a animação a qualquer uma.

Conclusão

Xamarin.Forms é uma nova forma emocionante de criar aplicativos móveis entre plataformas. Use para criar uma interface de usuário que renderize nativamente entre iOS, Android e Windows Phone. É possível compartilhar praticamente todo seu código entre plataformas.

A Xamarin Inc. criou Xamarin e Xamarin.Forms para possibilitar que os desenvolvedores de C# passem ao desenvolvimento móvel praticamente da noite para o dia. Se já desenvolveu para Windows Runtime, WPF ou Silverlight, considerará o Xamarin.Forms como uma união fácil ao mundo do desenvolvimento móvel nativo entre plataformas. É possível instalar Xamarin hoje e começar usando C# de imediato para criar aplicativos nativos maravilhosos que sejam executados em dispositivos iOS, Android e Windows Phone.


Jason Smith é um líder técnico de engenharia na Xamarin Inc. em São Francisco, liderando atualmente o projeto Xamarin.Forms. Ele era um dos principais arquitetos do Xamarin.Forms, antes do qual contribuiu para o projeto Xamarin Studio e participou da pesquisa inicial que levou à criação de Xamarin Test Cloud.

Agradecemos ao seguinte especialista técnico da Microsoft pela revisão deste artigo: Norm Estabrook
Norm Estabrook é um desenvolvedor de conteúdo na Microsoft há mais de 10 anos, com foco especial em ajudar os desenvolvedores a criar extensões do Office e aplicativos móveis nativos usando o Visual Studio. Ele publicou vários artigos para a biblioteca MSDN e reside no noroeste de Washington com sua esposa, dois filhos e um Schnauzer com deficiência visual muito amoroso