Xamarin.Forms 用のプレビューアをアルファ版で公開

1週間ほど掛けて、おおまかなところが動いてきたのでアルファ版で公開します。
何ができるかというと、Xamarin.Forms の XAML ファイルを動的にロードして、エミュレータ上に表示させます。現在のところ、Xamarin Studio にも Visual Studio にも GUI ベースのデザイナがないので、XAML コードを手打ちしています。まあ、手打ち自体は構わないのですが、画面の表示を確認するのに、いちいちビルドしてエミュレータなどにデプロイしないと画面が分かりません。勢い、StatckLayout などの汎用的な画面を作ることが多いのですが、それにしても色とか微妙な文字の配置とかが面倒です。そこで、XAML 記述を HTTP 経由で持ってきて、エミュレータ内で再構築するプレビューツールを作りました。エミュレータ上で画面をリロード(HTTP 経由で XAML ファイルを再びロード)することで、画面の状態が更新できます。これにより、ひとまず、XAMLのコードを書く→デプロイ→デザインの確認→XAMLのコード直し、というサイクルがなくなり、XAMLのコードを書く→画面をリロードする→XAMLの手直し、の手順だけで良くなります。ちょっと、使ってみた感じでは、表示している文字変更とか色変更が手軽にできるので、Xamarin Studio で本格的なデザイナが作られるまでのツナギになるとは思います。

■ダウンロード

moonmile/XFormsPreviewer
https://github.com/moonmile/XFormsPreviewer

まだ、NuGet を作っていないので、github から直接取ってきてください

■構成

image

  • Host/XFormsPreviewerHost ? XAML データを提供する簡易ホスト
  • Sample/XFormsSampleApp — サンプルプロジェクト(XAMLファイルが含まれる)
  • Viewer/XFormsPreviewer — 簡易プレビューア
  • XFormsProvider ? Xamarin製XAMLのパーサ
  • XFormsProvider.Test ? テストプロジェクト

プレビューアが、簡易ホストを経由してサンプルプロジェクト内の XAML ファイルを表示させます。プレビューア自体は、iOS/Android/Windws Phoneエミュレータなので、適当にカスタマイズしてリロードしてください。今後はもうちょっとプレビューア/簡易ホストをもう少しリッチにしていきましょう。

■使い方

応答する XAML ファイルを指定する。
簡易ホストを書き換えて、応答する XAML ファイルを指定します。/get/item のような形式で、プレビューアから呼び出されます。

1
2
3
4
5
6
7
8
9
10
11
12
public PreviewHost()
{
    this.Port = 10150;
    this.LoadPaths = new Dictionary<string, string>();
    // TODO: change load xaml file
    // ここで、レスポンスを返す XAML ファイルを指定。
    // 作成中のプロジェクトフォルダを指定すればok.
    // 後で WPF あたりで作成してドラッグ&ドロップ.
    // mac のためにコマンドラインで使えるようにしておく。
    var basefolder = @"..\..\..\Sample\XFormsSampleApp\XFormsSampleApp\";
    this.LoadPaths.Add("0", basefolder + "MainPage.xaml");
}

簡易クライアントを管理者モードで立ち上げる。
Windows 8.1 の場合は、ローカルサーバーを立ち上げるときに指定ポートを明示的に開ける必要があります。面倒な場合は、コマンドプロンプトを管理者モードで立ち上げてください。

image

プレビューアで要求ボタンを書き換える。
新しくボタンを作ってもよいし、既存のボタンを書き換えてもよいです。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/// <summary>
/// HTTP クライアント経由で XAML ファイルをロードする
/// http://hostname:10150/get/[num] 形式で取得する
/// [num] 部分は XFormsPreviewHost と合わせる
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
async void OnClickSample0(object sender, EventArgs e)
{
    var hc = new HttpClient();
    try
    {
        var res = await hc.GetStringAsync(this.Uri + "0");
        var page = PageXaml.LoadXaml(res);
        await this.Navigation.PushAsync(page);
    }
    catch (Exception ex)
    {
        System.Diagnostics.Debug.WriteLine(ex.Message);
    }
}

呼び出しは /get/item 形式です。XAML データのローディングは非常に簡単ですPageXaml.LoadXaml(res) のように LoadXaml メソッドを呼び出すことで Page オブジェクト(内部で設定している ConentPage オブジェクトなど)を受け取ることができます。ここでは Navigation を使って PushAsync メソッドで画面遷移をさせます。これにより、ボタンを押すごとに XAML データのリロードができます。

ホスト名を書き換える。通常、エミュレータからは名前解決ができないと思うので、IP アドレスを直書きしています。このあたりは、別途設定ファイルから読み込ませる予定です。

1
2
3
4
5
6
7
8
9
10
public MainPage()
{
    InitializeComponent();
 
    this.Port = 10150;
    // TODO: Change IP address of XFormsPreviewHost.exe on machine.
    // XAML ファイルをホスティングしている PC の IP アドレスを書く。
    // エミュレータから名前解決ができれば、マシン名でもOK.
    this.Hostname = "172.16.0.9";
}

imageimage

一度作ったプレビューアは何度も再利用ができます。/get/item コマンドだけ変更しなければ、簡易ホストのほうで、自由に応答 XAML ファイルを変更できます。

Visual Studio のほうでは、XAML のインテリセンスが効かないので、Xamarin Studio で XAML ファイルを編集します。属性などをコード補間してくれるので、多少は間違いがすくないかも。

image

■Type Provider 風に提供

XFormsPreviewer/XFormsProvider/PageXaml.fs at master ・ moonmile/XFormsPreviewer
https://github.com/moonmile/XFormsPreviewer/blob/master/XFormsProvider/PageXaml.fs

パーサ自体は F# で書いています。PCL の場合 FSharp.Core の競合があって懸念点が多いのですが、各種のワーニングは無視です。警告がいやだったので、最初は C# で書いていたのですが、今後のメンテを考えると F# のほうが良いかなと(単なる趣味…かもしれません)。

すごい大ざっぱに書いているので、タグ名とプロパティ名の対応を取ってない(DTDを考慮していない…というか、そもそも Xamarin.Forms の XAML に DTD が用意されていないのが問題では?…とも思ったけど、MS製XAML http://schemas.microsoft.com/winfx/2006/xaml/presentation のほうの記述もないので、そういうものかもしれない)のですが、まあツナギなので動けばよいという考えて作っています。が、どうせならば、F# の Type Provider と互換をとりたいですよね。FsXaml な感じで、WPF 製 XAML と互換を取るぐらいまでは作る予定です。最終的には、Windows Store 製 XAML や Android の axml, Xcode の storyboard の互換までいきたい。

■できないこと

本格的にデザイナ/プレビューアとして使うには、足りない機能が満載なのですが、

  • static resource の参照ができない。
  • 外部 UI モジュールを呼び出せない。
  • x:Name を参照できない。
  • ICommand が動かない。
  • MVVM の Binding が動かない。
  • デザイン時の Data Binding がないので、ListView が表示できない。

の項目ができません。このあたりは、おいおい実装していく予定です。これを実装すると、動的に XAML をロードするだけで、本体に View がいらないという状態になって、本格的に MVVM での View と ViewModel の疎結合が実現できそうです。

カテゴリー: F#, Xamarin パーマリンク