クロスプラットフォーム

Xamarin.Forms によるモバイル プラットフォーム間での UI コードの共有

Jason Smith

Xamarin を使えば、C# で美しいネイティブ モバイル アプリを作成し、コードの大部分を複数のプラットフォームで共有できるようになります。これまでは、対象とするプラットフォームごとに UI を個別にデザインする必要がありました。しかし Xamarin.Forms であれば、UI を 1 つビルドして、すべてのプラットフォームでネイティブにレンダリングできるようになります。

Xamarin.Forms はクロスプラットフォーム UI 抽象化レイヤーです。このレイヤーを介して UI やバックエンド コードをプラットフォーム間で共有でき、しかも完全なネイティブ UI エクスペリエンスを実現できます。ネイティブな UI になるため、コントロールやウィジェットはそれぞれ対象のプラットフォームの外観に従います。

Xamarin.Forms はモデル - ビュー - ビューモデル (MVVM: Model - View - ViewModel) 設計パターンと完全な互換性があります。そのため、ビュー モデル クラスのプロパティやコマンドにページ要素をバインドできます。

宣言によってページをデザインする場合は、XAML を使用します。XAML とは、リソース ディクショナリ、動的リソース、データ バインド、コマンド、トリガー、ビヘイビアなどの機能を備えたマークアップ言語です。

Xamarin.Forms には小さく使いやすい API があります。プラットフォームのネイティブ UI の細部までアクセスする必要がある場合は、カスタム ビューやプラットフォーム固有のレンダラーを作成できます。これは複雑に思えますが、実際はネイティブ UI を作る方法とほとんど変わりません。また、Xamarin の Web サイトでは作業の助けになる十分な数のサンプルが手に入ります。

まず、Xamarin.Forms に付属する既製のページ、レイアウト、およびビューを使うところから始めます。アプリを洗練していく過程で、新しいユース ケースやデザインのチャンスが見つかります。そうなったら、XAML、MVVM、カスタム プラットフォーム固有のレンダラー、およびアニメーションやデータ テンプレートのような他のさまざまな機能に対する Xamarin.Forms のサポートを利用してみます。

今回は、さまざまな対象プラットフォームで UI コードの大部分を共有し、必要に応じてプラットフォーム固有のコードを組み込む方法を例を織り交ぜながら説明し、Xamarin の機能の概要を取り上げます。

作業の開始

まず、Visual Studio または Xamarin Studio で NuGet パッケージ マネージャーを開き、Xamarin Form の新しいリリースをチェックします。どちらの IDE で Xamarin.Forms ソリューションを開いても新しいリリースについては通知されないため、拡張機能が最新であることを確認するには、NuGet パッケージの更新状態をチェックするしかありません。

Xamarin.Forms ソリューションの作成: Xamarin.Forms が最新バージョンであることを確認したら、"Blank App (Xamarin.Forms Portable)" ソリューションを作成します。

作成したソリューションには 3 つのプラットフォーム固有のプロジェクトと 1 つのポータブル クラス ライブラリ (PCL) が含まれています。PCL でページを作成します。まず、基本的なログイン ページを作成します。

C# を使用したページの作成: PCL プロジェクトにクラスを追加します。次に、コントロール (Xamarin では "ビュー") を追加します (図 1 参照)。

図 1 ビュー (コントロール) の追加

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

アプリの起動時にページを表示するには、MyApp クラスを開いてそのインスタンスを MainPage プロパティに割り当てます。

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

ここで Application クラスについて説明しておきます。バージョン 1.3.0 から、すべての Xamarin.Forms アプリに Application クラスが含まれるようになります。このクラスが Xamarin.Forms アプリのエントリ ポイントです。特筆すべきことは、このクラスによってライフサイクル イベントと、シリアル化できるデータ用に永続的なデータ ストア (Properties ディクショナリ) が提供されることです。アプリの任意の場所でこのクラスのインスタンスを取得する必要がある場合は、静的 Application.Current プロパティを使用します。

上記の例では Application クラス内部の既定のコードを削除し、代わりにアプリ起動時に LogInPage を表示する 1 行のコードを記述しています。このコードでページ (LogInPage) を Application クラスの MainPage プロパティに割り当てているため、アプリ起動時にログイン ページが表示されます。このコードは Application クラスのコンストラクターに配置する必要があります。

このクラスでは以下の 3 つのメソッドをオーバーライドすることもできます。

  • OnStart メソッド: アプリの初回起動時に呼び出されます。
  • OnSleep メソッド: アプリがバックグラウンド状態に移行する際に呼び出されます。
  • OnResume メソッド: アプリがバックグラウンド状態から復帰する際に呼び出されます。

一般に、データやビヘイビアーをいくつか結び付けないと興味深いページにはならないため、ここからはその方法を示します。

ページへのデータのバインド: MVVM 設計パターンを使用する場合は、INotifyPropertyChanged インターフェイスを実装するクラスを作成します (図 2 参照)。

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

ログイン ページのビューをそのクラスのプロパティにバインドします (図 3 参照)。

図 3 クラスのプロパティへのビューのバインド

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

Xamarin.Forms アプリでデータにバインドする方法については、Xamarin サイトの「MVVM へのデータ バインド」(英語) を参照してください。

XAML を使用したページの作成: 小さなアプリであれば、C# を使って UI を作成する手法がまさに合理的なアプローチです。ただし、アプリのサイズが大きくなるにつれ、同じコードを何度も繰り返し記述していることに気付きます。これを避けるには C# コードの代わりに XAML を使います。

PCL プロジェクトに Forms XAML Page アイテムを追加します。次に、ページにマークアップを追加します (図 4 参照)。

図 4 Forms XAML Page へのマークアップの追加

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

Windows Presentation Foundation (WPF) アプリを作った経験があれば、図 4 の XAML も見慣れたものでしょう。ただし、今回は Xamarin.Forms の型を参照するため、タグが異なります。また、ルート要素は Xamarin.Forms.Element クラスのサブクラスを参照します。Xamarin.Forms アプリ内すべての XAML ファイルでこれを行う必要があります。

XAML を使って Xamarin.Forms アプリのページを作成する方法については、「Xamarin.Forms 用 XAML」(英語) を参照してください。

特定のプラットフォーム向けの設計

Xamarin.Form の API の数は、他のネイティブ モバイル プラットフォームに比べて少量です。そのためページの設計が簡単になりますが、1 つ以上のプラットフォームを対象にすると、Xamarin.Forms ではビューが思ったように表示されないことがあります。

このような状況に直面したら、カスタム ビューを作成します。カスタム ビューとは Xamarin Forms で利用できる任意のビューのサブクラスにすぎません。

ページにビューを表示するには、ビューのレンダラーを拡張します。高度な状況では、カスタム レンダラーをゼロから作成することも可能です。カスタム レンダラーのコードはプラットフォーム固有になるため、共有できません。しかし、アプリにネイティブ機能やユーザビリティを取り入れる場合は、このアプローチを採用する価値があります。

カスタム ビューの作成: まず、Xamarin.Forms で利用できる任意のビューのサブクラスを作成します。以下は 2 つのカスタム ビューのコードです。

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

既存のレンダラーの拡張: 図 5 では、iOS プラットフォーム向けに Entry ビューをレンダリングするレンダラーを拡張しています。このクラスを iOS プラットフォーム プロジェクトに配置することになります。このレンダラーは基になるネイティブ テキスト フィールドの配色とスタイルを設定します。

図 5 既存のレンダラーの拡張

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

参照しているのはネイティブ API なので、このレンダラーでは希望することを何でも行うことができます。このスニペットを含むサンプルを確認するには、「Xamarin.Forms のカスタム レンダラー」(英語) を参照してください。

ゼロからのレンダラーの作成: 他のレンダラーを拡張するのではなく、まったく新しいレンダラーを作成することができます。作業量は多くなりますが、次の手順で、自身の要望をすべて満たすレンダラーを作成できます。

  • ビューのレンダラーを置き換える
  • ソリューションに新しいビュー型を追加する
  • ソリューションにネイティブ コントロールまたはネイティブ ページを追加する

たとえば、アプリの iOS バージョンのページにネイティブ UIView コントロールを追加する場合は、iOS プロジェクトにカスタム レンダラーが追加します (図 6 参照)。

図 6 カスタム レンダラーによる iOS アプリへのネイティブ UIView コントロールの追加

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

このレンダラーが一般的に見受けられるパターンなので、リスト ビューなどの仮想レイアウトに使用できます。これについては後ほど説明します。

このスニペットを含むサンプルを表示する場合は、bit.ly/xf-customrenderer を参照してください。

カスタム ビューへのプロパティの追加: プロパティをカスタム ビューに追加できますが、追加するプロパティは必ずバインド可能にし、ビュー モデル内のプロパティやその他の型のデータにバインドできるようにします。以下は 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);}
  }
}

レンダラーに新しいプロパティを接続するには、レンダラーの OnElementPropertyChanged メソッドをオーバーライドし、プロパティが変化するときに実行されるコードを追加します。

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

カスタム ビューとカスタム レンダラーの作成については、「プラットフォーム別のコントロールのカスタマイズ」(英語) を参照してください。

ページ、レイアウト、およびビュー: Xamarin.Forms のビルディング ブロック

ここまで紹介した要素は、まだほんの一部です。他の要素も見てみましょう。

Xamarin.Forms アプリにはページ、レイアウト、およびビューがあります。ページは 1 つ以上のレイアウトを含み、レイアウトは 1 つ以上のビューを含みます。Xamarin でのビューは、通常コントロールと呼ばれる要素を指します。全体で見ると、Xamarin.Forms フレームワークには 5 個のページ型、7 個のレイアウト型、24 個のビュー型があります。それぞれの詳細については、xamarin.com/forms (英語) を参照してください。いくつか重要なページ型を後ほど取り上げますが、まずはアプリ内で使えるレイアウトを何種類か再確認します。Xamarin.Forms には 4 つの主要レイアウトがあります。

  • StackLayout: 子要素を 1 行に配置します。この行は縦横どちらにも向けることができます。StackLayout を入れ子にして、複雑な表示階層を作成できます。各子ビューの VerticalOptions プロパティまたは Horizontal­Options プロパティを使って、StackLayout 内でのビューの整列を制御できます。
  • Grid: 列と行にビューを配置します。このレイアウトは WPF や Silverlight のレイアウトに似ていますが、列や行の間隔を広げられる点が異なります。間隔を広げるには、Grid の RowSpacing プロパティと ColumnSpacing プロパティを使用します。
  • RelativeLayout: 他のビューとの相対的な制約を使用してビューを配置します。
  • AbsoluteLayout: 2 つの方法で子ビューの位置を決めます。1 つは絶対位置、もう 1 つは比率を利用した親ビューとの相対位置です。これは階層を分割したり階層を重ねる場合に便利です。図 7 に例を示します。

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

すべてのレイアウトに Children というプロパティがあることに注意してください。このプロパティを使用して追加のメンバーにアクセスします。たとえば、Grid レイアウトの Children プロパティを使用して列や行を追加および削除でき、列や行の幅を指定することもできます。

スクロール リストでのデータの表示

リスト ビュー フォーム (ListView) を使用して、スクロール リストにデータを表示できます。リストではセルごとのレンダラーが仮想化されるため、このビューは優れたパフォーマンスを発揮します。セルがそれぞれ仮想化されるため、カスタム レンダラーの OnElementChanged イベントを正しく処理することが重要です。カスタム レンダラーは今回最初に示したものに近いパターンを使用して、各セルまたはリスト用に作成します。

まず、セルを定義します (図 8 参照)。ListView のデータ テンプレートは、必ずルート要素としてセル使用する必要があります。

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

次にデータ ソースを定義し、新しいデータ テンプレートに ListView の ItemTemplate プロパティを設定します。データ テンプレートは上記の MyCell クラスをベースにします。

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

以下のマークアップを使用すると XAML でも同じことを実行できます。

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

ページ間の移動

ほとんどのアプリには複数のページがあるため、ユーザーがページ間を移動できるようにする必要があります。以下のページにはページ移動に関する組み込みのサポートがあり、全画面モーダル ページ表示に対応しています。

  • TabbedPage
  • MasterDetailPage
  • NavigationPage
  • CarouselPage

ページを上記の 4 つのページの子ページとして追加すれば、自由な移動が可能になります。

TabbedPage TabbedPage はタブの配列を画面上部に横切るように表示します。PCL プロジェクトに LogInPage、DirectoryPage、および AboutPage というページがあるとします。以下のコードを使用してこれらすべてを TabbedPage に追加できます。

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

この場合に重要なのは各ページの Title プロパティと Icon プロパティを設定することです。これがページ タブに表示されます。すべてのプラットフォームがアイコンをレンダリングするわけではありません。この点は、プラットフォームのタブ デザインによって異なります。

このページをモバイル デバイスで開くと、最初のタブが選択状態で表示されます。ただし、TabbedPage ページの CurrentPage プロパティを設定すると、この動作を変更できます。

NavigationPage NavigationPage によって、ページ スタックの移動とユーザー エクスペリエンスを管理します。このページは、モバイル アプリで最も一般的な種類の移動パターンを提供します。ページを追加するには以下のコードを使用します。

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

ユーザーを特定のページ (この場合は DirectoryPage) に移動する方法として PushAsync を使用しているのがわかります。NavigationPage ではページがスタックにプッシュされ、ユーザーが前のページに戻るときにポップされます。

NavigationPage の PushAsync と PopAsync は非同期メソッドのため、コードではその 2 つを待機する必要があり、タスクの実行中は新しいページをプッシュしたりポップすることはできません。それぞれのメソッドのタスクは、プッシュまたはポップのアニメーションが完了すると戻ります。

便宜上、Xamarin.Forms のビュー、レイアウト、およびページにはすべて Navigation プロパティが含まれています。このプロパティは、NavigationPage インスタンスの PushAsync メソッドと PopAsync メソッドを含むプロキシ インターフェイスです。このプロパティを使えば、NavigationPage インスタンスから直接 PushAsync メソッドと PopAsync メソッドを呼び出さずにページ間を移動できます。また、NavigationProperty を使って、PushModalAsync メソッドと PopModalAsync メソッドを取得することもできます。これは画面全体のコンテンツを新規のモーダル ページに置き換える場合に便利です。ビューの Navigation プロパティを使用するためにビューの親スタックに NavigationPage を含める必要はありませんが、モーダルではない PushAsync/PopAsync 操作は失敗することがあります。

設計パターンに関する注意事項: 一般的な使い方として、NavigationPages を TabbedPages に子ページとして追加し、さらに TabbedPages を MasterDetailPages の子ページとして追加する場合を考えます。特定の種類のパターンは、望まないユーザー インターフェイスを引き起こすことがあります。たとえば、ほとんどのプラットフォームでは NavigationPage の子ページとして TabbedPage を追加しないよう推奨されます。

ページでのビューのアニメーション化

ページのビューをアニメーションにすることで、いっそう魅力的なエクスペリエンスを実現できます。その方法は 2 種類あります。Xamarin.Forms に付属する組み込みアニメーションを使用する方法と、アニメーション API を使用して独自にビルドする方法です。

たとえば、ビューの FadeTo アニメーションを呼び出すことによって、フェード効果を生み出すことができます。FadeTo アニメーションはビューに組み込まれているため、簡単に使用できます。

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

await キーワードを使うと、一連のアニメーションを連鎖させることができます。各アニメーションは、直前アニメーションが完了してから実行されます。たとえば、ビューをフェード インする前に回転させることができます。

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

希望する効果を生み出すことができない場合は、完全なアニメーション API を使用します。以下のコードでは、ビューが回転している途中にフェードアウトします。

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

この例では、それぞれのアニメーションを 1 つの Animation インスタンスにまとめ、Commit メソッドを使用してアニメーションのシーケンス全体を実行しています。また、このアニメーションは具体的なビューに結び付けられていないため、任意のビューに適用できます。

まとめ

Xamarin.Forms はクロスプラットフォーム ネイティブ モバイル アプリを作成するための新しく魅力的な手法です。これを使って、iOS、Android、および Windows Phone でネイティブに表示される UI をビルドできます。さらに、プラットフォーム間でほぼすべてのコードを共有できます。

Xamarin Inc. が Xamarin と Xamarin.Forms をビルドしたことで、C# 開発者は一夜にしてモバイル開発に参入できるようになります。Windows ランタイム、WPF、または Silverlight 向けの開発経験があれば、Xamarin.Forms はクロスプラットフォームのネイティブ モバイル開発への大きな架け橋となるでしょう。Xamarin をインストールした直後から、iOS、Android、および Windows Phone の各デバイスで実行される美しいネイティブ アプリを、C# を使って手早く作成できます。


Jason Smith はサンフランシスコの Xamarin Inc. でエンジニアリング テクニカル リードを務めており、最近まで Xamarin. Forms プロジェクトを主導していました。Jason は Xamarin.Forms の主席アーキテクトの 1 人で、Xamarin Studio プロジェクトや、Xamarin Test Cloud 製作のための初期調査の一部に貢献しました。

この記事のレビューに協力してくれたマイクロソフト技術スタッフの Norm Estabrook に心より感謝いたします。
Norm Estabrook は、Visulal Studio を用いて Office 拡張やネイティブ モバイル アプリをビルドする開発者を主にサポートするコンテンツ開発者としてマイクロソフトに 10 年以上勤めています。MSDN ライブラリで数々の記事を公開し、彼の妻、2 人の子供、そして愛くるしいミニチュア シュナウザーと共にワシントン州北西で暮らしています。