Xamarin.Forms の XAML を F# から使うために XamlTypeProvider を使う

XFormsPreviewer では、動的にXAMLファイルをロードしてレンダリングする(内部的にはXamarin.Formsのコントロールを生成する)してプレビューを実現していますが、このコアライブラリに XamlTypeProvider があります。最初は Xamarin.Forms のプレビューだけを作るつもりだったけど、作っているうちに F# の TypeProvider と似ていることがわかり、じゃあ TypeProvider 的にしようとしたのが発端です。

■Xamrin.Forms と XAML の関係

要は、WPF や Windows Store の XAML と同じように、フロントエンドを記述する XAML と、ビルド時に自動生成される partial class とで成り立っています。この partial class のほうで、コントロールの名前による参照や、ボタンコントロールのクリックイベントなどを記述するわけで、事実上のコードビハイドですね。Silverlight の XAML の場合には、XAML 自体に C# のコードを埋め込めるので(今でもそうだと思う)XAML 単体で動かすことも可能なわけですが…Windows Store/Windows Phone 8.1 の XAML はできたかどうかわかりません。たぶん、Xamarin.Forms の XAML もできません。

で、コードビハイドを使うと、XAML で記述した View に内部動作のコードが埋め込まれるわけで、(それがコードビハイドだとしても)View と Logic が分離していないのではないか。という話なのですが、まあ、実装的には MVVM の INotifyPropertyChanged/ICommand にしてしまうか、別な方法をにしてしまうか、という具合です。ワタクシ的にはコードビハイドでもいいと思うんですけどね。うまく分離されていれば。

「うまく分離する」という点で、MVVM パターンは、View と Modelが分離できていますが、複数の View を扱うときにちょっと困ります。これは Xamarin.Forms の問題かもしれないけど、とあるビューから別のビューに移るときに、PushAsync を使いますが、この Navigation プロパティは Page にくっついて引数もページなんですよね。

page.Navigation.PushAsync(nextPage)

当たり前といえば当たり前だけど、画面遷移自体は、ページからページに移る。ViewModel の場合は Page に依存したくないので、View から本格的に ViewModel を分離させたときに Command パターン内では他のページに遷移させることができない、ってことになります。まあ、現実的な解としては ViewModel に対応する View を持たせてしまえばいいのですが。

そんな依存関係から、Xamarin.Forms も Windows Store の XAML も View=XAML に対応するコードを出力して、各種イベントやデータを保持する方法をとっています。
実は、F# の TypeProvider も同じことが言えて、実行時にビルドして Type を決めるので FsXaml のように WPF の XAML をコンバートできるけど、これは動作時にはできないのです。というのも、未知の XAML に対して、内部プロパティやイベントの判定を、先にコードビハイドに記述することはできませんよね。。まあ、方法としては、

  • XAML ダウンロード時に TypeProvider でビルドする。
  • Page に付属するプロパティは、dynamic で受けて参照させる。

ということで、実行にデータの整合性のチェックをすればよいのですが、完全にダイナミックに、というわけにはいきません。

また、Xamrin.Forms の XAML から出力するコードは C# のために、F# から使おうとするビルドエラーになります。これは Windows Store の XAML の場合も同じで、ここのコードビハイドのコードが言語依存になってしまって邪魔です。XAML 自体はコード依存していないのに。
逆に言えば、コードビハイドに全く依存しない形で、書けばバックエンドが C# であろうと F# であろうと、場合によっては Visual Basic であろうと関係なく書ける可能性があります。

■View と ViewModel を動的ににリンクさせる

通常の MVVM モデルの場合には、先の画面遷移の関係もあって、View と ViewModel は1対1の関係になっています。View(XAML)に対して、ひとつひとつ ViewModel を作ることが多いのですが、ViewModel から View への依存を完全に切り離してしまえば、ひとつの ViewModel を複数の View に対応させることができます。

コンパイル時にこれを実現するのは非常に簡単で、同じ ViewModel を使っているけど状況に応じて複数の View を切り替えることができます。ただし、「状況に応じて」切り替えられるのは、ViewModel の中ではなく、先の Page のようなコードビハイドの中になります。

ここで、View と ViewModel を完全に分離させようとすると矛盾がでます。

– XAML は、コードビハイドを出力する
– コードビハイドでのみ、画面遷移/Viewの切り替えが可能
– View は ViewModel と分離させたい
– ViewModel から画面遷移くを直接アクセスできない。

この矛盾は、XAML の仕組みがコードビハイドに依存している(partial class)ためであって、XAML 自体が徹底して View に徹していれば、うまく矛盾が解消できるような気がします。もちろん、ビヘイビアのような View 内部に存在するコード(アニメーションのようなコード)は、XAML にあるほうがよいのですが、ViewModel でもまかなえるものは、切り離してしまったほうがよいでしょう。

…という発想のもとに、XAML コード自体を完全に動的にロードさせます。動的にロードさせるわけですから、未知の XAML ファイルでも大丈夫です。いままで、リソースとして持つか、文字列としてプロジェクト内に埋め込むかしかなかったものが、インターネット上に XAML ファイルを置いて、それをダウンロードして使うことができます。まあ、作り始めて分かったのですが、ちょうど HTML がブラウザ上で動くのと同じ仕組みですね。

HTML の場合が

  • HTML でデザインコードを書く。
  • Javascript で動きのあるコードを書く。
  • Javascript はブラウザのインタープリタで動く

というように、コード部分がインタープリタにしかならないのがスピードではネックだったりします。

Xamarin.Forms 用の XAML ファイルを直接ダウンロードできるようにすることで、

  • XAML でデザインコードを書く。
  • 動作は、C# で ViewModel 内に書く。あるいは、動的リンクされるコードビハイドもどきに記述する。
  • ViewModel内 のコードは事前コンパイルで、Xamarin によりネイティブコードで動く。

ViewModel のアセンブリを動的ダウンロードできるかどうかは分からないのですが(ActiveXみたいな感じ?)、ひとまず、この方式をとることで Javascript などによるインタープリタではないネイティブの動作が可能になります(もちろん、Javascript等であっても、実行時ビルドされていればスピードは同じなんですけど)。

Xaml のパース自体が車輪の再発明っぽいので、あまりやりたくないのですが、ざっと F# で書いてみたところ 400 行程度なのでなんとか許容範囲でしょう。

■実際どう使うのか?

moonmile/XamarinXamlPreview
https://github.com/moonmile/XamarinXamlPreview

Xamarin.Forms for F# のコンテスト用に作ったので F# になっていますが、当然ながら C# でも使えます。page.Navigation.PushAsync あたりが、変なことになっているのは F# だからであって、C# だと結構素直に書けます。

type MainPage(path:string) =
    inherit ContentPage()
    let mutable page:Page = null
    let mutable buttonNextPage:Button = null
    let mutable buttonStopWatch:Button = null
    let mutable buttonStopWatchDownload:Button = null
    let mutable label:Label = null
    
    let pushPageVM(rn:string, vm:ViewModelBase) =
        let xaml = ResourceLoader.GetString(rn)
        let next = ParseXaml.LoadXaml(xaml) 
        next.BindingContext <- vm
        let t = new Task( fun() -> 
            let task = page.Navigation.PushAsync(next)
            task.Start()
        )
        t.RunSynchronously()
    
    let pushPage(rn:string) =
        let xaml = ResourceLoader.GetString(rn)
        let next = ParseXaml.LoadXaml(xaml) 
        let t = new Task( fun() -> 
            let task = page.Navigation.PushAsync(next)
            task.Start()
        )
        t.RunSynchronously()

    let pushPageDL(url:string) =
        
        try 
            let hc = new HttpClient()
            let xaml = hc.GetStringAsync(url).Result
            let next = ParseXaml.LoadXaml(xaml) 
            let t = new Task( fun() -> 
                let task = page.Navigation.PushAsync(next)
                task.Start()
            )
            t.RunSynchronously()
        with
        | _ -> 
            label.Text <- String.Format(&quot;Error: cannot open {0}&quot;, url )

    do 
        let xaml = ResourceLoader.GetString(path)

        page <- ParseXaml.LoadXaml(xaml)

        buttonNextPage <- page.FindByName<Button>(&quot;buttonNextPage&quot;)
        buttonStopWatch <- page.FindByName<Button>(&quot;buttonStopWatch&quot;)
        buttonStopWatchDownload <- page.FindByName<Button>(&quot;buttonStopWatchDownload&quot;)
        label <- page.FindByName<Label>(&quot;label&quot;)
        
        buttonNextPage.Clicked.Add( fun(e) -> 
                pushPageVM(&quot;NextPage.xml&quot;, new ViewModelNextPage())
            )
        buttonStopWatch.Clicked.Add( 
            fun(e) -> pushPage(&quot;StopWatchPage.xml&quot;))

        buttonStopWatchDownload.Clicked.Add( 
            fun(e) -> pushPageDL(&quot;http://moonmile.net/up/DownLoadPage.xml&quot;))

    member this.Page 
        with get() = page

Xaml ファイルをそのままプロジェクトに埋め込むと、C# のコードビハイドを吐き出すので、拡張子を xml にしています。ViewModel 自体は、XAML を動的に生成して結びつけます。内部的にはリフレクションの嵐になっていますが気にしてはいけません。外側から使う時、ViewModel から使う時はリフレクションなどは意識しない作りになっています。普通の ViewModel を使うときと同じ要領ですね。

F# なので Command パターンのところがややこしくなっていますが、C# でも同じように書けます。データバインディングを使うので、View に依存しないように書けます。ここは MVVM と同じです。

type ViewModelStopWatch() =
    inherit ViewModelBase()

    let mutable startTime = DateTime.Now
    let mutable endTime  = DateTime.Now
    let mutable time:TimeSpan = new TimeSpan()
    let mutable task:Task = null
    let mutable taskflag = false


    /// リフレクションで iOS/Android の System.Timers.Timer を持ってくる
    /// 現在調節中
    let makeTimer (interval:int, hdr:ElapsedEventHandler) =
        let asm = Assembly.Load(AssemblyName(&quot;System&quot;))
        let t = asm.GetType( &quot;System.Timers.Timer&quot; )
        let item = System.Activator.CreateInstance(t)

        t.GetRuntimeProperty(&quot;Enabled&quot;).SetValue( item, true )
        t.GetRuntimeProperty(&quot;AutoReset&quot;).SetValue( item, true )
        t.GetRuntimeProperty(&quot;Interval&quot;).SetValue( item, interval )
        t.GetRuntimeEvent(&quot;Elapsed&quot;).AddEventHandler( item, hdr )
        t.GetRuntimeMethod(&quot;Start&quot;, null).Invoke(item, null) |> ignore
        item

    member this.Time 
        with get() =  
            time <- endTime - startTime
            String.Format(&quot;{0:00}min {1:00}sec {2:000}&quot;, time.Minutes, time.Seconds, time.Milliseconds )
    member this.StartTime
        with get() = startTime.ToString()

    member this.StartCommand 
        with get() =
            let cmd = 
                new DelegateCommand(
                    (fun (o) -> true ),
                    (fun (o) -> 
                        startTime <- DateTime.Now 
                        endTime <- startTime
                        this.OnPropertyChanged(&quot;StartTime&quot;)
                        this.OnPropertyChanged(&quot;Time&quot;)
                        // makeTimer(2000, new ElapsedEventHandler(fun(s,e)-> 
                        //     System.Diagnostics.Debug.WriteLine(&quot;time {0}&quot;, endTime ))) |> ignore
                        // )))
                        ))
            cmd

    member this.StopCommand 
        with get() =
            let cmd = 
                new DelegateCommand(
                    (fun (o) -> true ),
                    (fun (o) -> 
                        taskflag <- false
                        endTime <- DateTime.Now 
                        this.OnPropertyChanged(&quot;Time&quot;)
                    ))    
            cmd

    member this.ResetCommand 
        with get() =
            let cmd = 
                new DelegateCommand(
                    (fun (o) -> true ),
                    (fun (o) -> 
                        taskflag <- false
                        startTime <- DateTime.Now
                        endTime <- DateTime.Now 
                        this.OnPropertyChanged(&quot;StartTime&quot;)
                        this.OnPropertyChanged(&quot;Time&quot;)
                    ))    
            cmd

ここでストップウォッチを作ろうと思ったのですが、Xamarin.Android の場合、Task.Delay の扱いが Windows Forms と異なるので苦戦中です。実は WinRT も同じ現象になるので、定期的なメソッド起動はタイマーを使わないと駄目なんですよね。このあたりは、別なサンプルアプリとして実装します。

Xamarin.Forms や MvvmCross 的には Service を使うのでしょうが、今回はプラットフォーム依存のアセンブリを動的ロードして、タイマークラスをリフレクションを使って操作する、ということを試しています。結構乱暴ですが、こうすると PCL からプラットフォームへのインターフェースがいらなくなって(タイマーを起動するインターフェースが必要なくなる)依存関係が「疎」になります。実は、タイマーの扱いも、Android/iOS/WinStore とそれぞれ異なるので、工夫が必要なのですが、Device を使って回避する予定です。

ライブラリとしての XamlTypeProvider は NuGet を使って取得できます。
https://www.nuget.org/packages/Xamarin.Forms.XamlProvider/

■実行する

「Download View from Internet」ボタンをクリックすると、インターネット上から XAML コードをダウンロードしています。こうすることで、View だけを定期的に更新することが可能です。

XFormsPerviwer もそうなのですが、Xamarin.Forms の XAML を外だしにしていつでも更新できるようにしておくと、ちょこちょこと XAML を手作業で修正して、エミュレータ上で動作を確認できます。たぶん、開発効率が上がるかな、ってのと、ちょっとしたパズルゲームとか設定画面をダイナミックにロードさせると、画面の雰囲気ががらりと変わってよいかと。アプリ自体はそのままなので、手軽に変えらえるのがミソです。そう、ややこしい「審査」がいらなくなりますからね。

この仕組み自体が「審査」が通るからどうかは、近々試してみる予定です。しばらくは、プレビュー的に使うのと、XamlTypeProvider の整備ってところです。

カテゴリー: 開発 | Xamarin.Forms の XAML を F# から使うために XamlTypeProvider を使う はコメントを受け付けていません

NuGetでXamarin.Forms.XamlProvilderを公開しました

動的にXamarin.FormsのXAMLファイルをロードするときのライブラリとして使っている、Xamarin.Forms.XamlProvilder を NuGet で公開しました。

NuGet Gallery | Xamrin.Forms Xaml Provider 0.1.1
https://www.nuget.org/packages/Xamarin.Forms.XamlProvider/0.1.1

XAMLの文字列から ContentPage 等を作るので、こんな風にXAML形式のデータをC#のコード内に埋め込んで記述ができます。

public void LoadSample()
{
	string xml = @&quot;<?xml version='1.0' encoding='utf-8' ?>
		<ContentPage xmlns='http://xamarin.com/schemas/2014/forms'
				xmlns:x='http://schemas.microsoft.com/winfx/2009/xaml'
				x:Class='XFormsPreview.NewPage'>
		<StackLayout>
			<Label Text='New Page'  />
			<Button Text='Click me'  />
		</StackLayout>
		</ContentPage>&quot;;

	var page = PageXaml.LoadXaml<ContentPage>(xml);
	Assert.IsNotNull(page);
	var layout = page.Content as StackLayout;
	Assert.IsNotNull(layout);
	Assert.AreEqual(2, layout.Children.Count);

文字列から動的に変換するので、ファイルリソースをプロジェクトに埋め込ませて読み込むこともできます。

[TestMethod]
public void GridDemoPage()
{
    var fs = File.OpenText(@&quot;XamlGridDemoPage.xaml&quot;);
    var xaml = fs.ReadToEnd();
    var page = PageXaml.LoadXaml<ContentPage>(xaml);

    Assert.IsNotNull(page);
    Assert.AreEqual(&quot;Grid Demo Page&quot;, page.Title );
    Assert.AreEqual(new Thickness(0, 20, 0, 0), page.Padding);

これを HTTP 経由で拾ってくるようにしたのが XFormsPreviewer です。

async void OnClickSample0(object sender, EventArgs e)
{
    var hc = new HttpClient();
    try
    {
        var res = await hc.GetStringAsync("http://localhost:10150/get/0");
        var page = PageXaml.LoadXaml(res);
        await this.Navigation.PushAsync(page);
    }
    catch (Exception ex)
    {
        System.Diagnostics.Debug.WriteLine(ex.Message);
    }
}

当然、HTTP 経由で拾ってくるので、インターネット上に XAML ファイルを配置しておけば、アプリケーションの更新なしに、動的に Xamarin製 XAML ファイルをダウンロードして表示を変えることが可能です…が、いまのところ、プレビューの機能しかないので静的ページしか表示できません。この部分はちょうど Javascript 無しの WebView みたいなものです。

■名前を取得する

ver.0.1.1 では、x:Name で Element を参照できるようにしました。

public void TestName()
{
    string xml = @&quot;<?xml version='1.0' encoding='utf-8' ?>
<ContentPage xmlns='http://xamarin.com/schemas/2014/forms'
				xmlns:x='http://schemas.microsoft.com/winfx/2009/xaml'
				x:Class='XFormsPreview.NewPage'>
	<StackLayout>
		<Label x:Name='label1' Text='New Page'  />
		<Button  x:Name='button1' Text='Click me'  />
	</StackLayout>
</ContentPage>
&quot;;
         
    var page = PageXaml.LoadXaml<ContentPage>(xml);
    Assert.IsNotNull(page);
    var layout = page.Content as StackLayout;

    var label1 = page.FindByName<Label>(&quot;label1&quot;);
    var button1 = page.FindByName<Button>(&quot;button1&quot;);
    Assert.IsNotNull( label1 );
    Assert.AreEqual(&quot;New Page&quot;, label1.Text);
    Assert.AreEqual(&quot;Click me&quot;, button1.Text);
}

XAML内に x:Name で名前を付けておくと、Xamarin.Forms や WPFなどでは、それぞれのプロパティにしてくれますが、動的にXAMLファイルをロードする場合は直接プロパティを参照しづらいので、FindByName<Label>(“name”) のように参照します。FindByName 自体は Xamarin.Forms にもあるし、Silverlight でよく使うパターンです。
こうすると、固定で Xamarin.Forms を作ったときと同じように、各種のプロパティが設定できます。

var page = PageXaml.LoadXaml<ContentPage>(xml);
var label1 = page.FindByName<Label>(&quot;label1&quot;);
label1.Text = &quot;change display&quot;;

■BindingContent を実装中

ただし、x:Name でコントロールのオブジェクトを取ってくると、動的にロードされた XAML との接点が多くなってしまうので、もっと沿結合にするため BindingContext の部分を実装中です。MVVM パターンにしたがって、
INotifyPropertyChanged インターフェースと ICommand インターフェースが実装されていれば、ViewModel がそのまま利用できる感じになる予定です。

カテゴリー: C#, Xamarin | NuGetでXamarin.Forms.XamlProvilderを公開しました はコメントを受け付けていません

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 のような形式で、プレビューアから呼び出されます。

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

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

image

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

/// <summary>
/// HTTP クライアント経由で XAML ファイルをロードする
/// http://hostname:10150/get/[num] 形式で取得する
/// [num] 部分は XFormsPreviewHost と合わせる
/// </summary>
/// <param name=&quot;sender&quot;></param>
/// <param name=&quot;e&quot;></param>
async void OnClickSample0(object sender, EventArgs e)
{
    var hc = new HttpClient();
    try
    {
        var res = await hc.GetStringAsync(this.Uri + &quot;0&quot;);
        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 アドレスを直書きしています。このあたりは、別途設定ファイルから読み込ませる予定です。

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 | Xamarin.Forms 用のプレビューアをアルファ版で公開 はコメントを受け付けていません

Xamarin.Formsへ動的にXAMLデータをロードする…の手始め

第1回 Xamarin User Group は無事終了。プレゼン資料については後ほど解説を入れるとして、なにかと気になったので、ちょっと手を付けてみました。

Xamarin.Forms を使うとき、Phone の場合は画面が狭いので手打ちで XAML を書いてもなんとかなるのですが、タブレットのように広い画面になった時や、Xcode の autolayout みたいな感じでレイアウトしようとすると手打ち XAML だけだと結構大変です。実行しないとうまくいかないし、懇親会でもレイアウトに苦労している場面もあったりして、

  • エミュレータで実行してみないと、タグの typo が分からない。
  • エミュレータで実行してみないと、レイアウト崩れがわからない。

のは、いかんともしがたいところ。Xamarin.Forms 用のデザイナが出ればいいんですが、暫くは出る様子もないので、なんとかならんかなと。そこに、「プレビューでもあれば便利なのに」とのツイートを見つけました。

なるほど、デザイン自体は大変そうだけど、プレビューだけならなんとかなりそうです。

  1. Xamarin.Froms の XAML を手打ちする。
  2. 更新されたファイルを読み取って、デザイナに描画する。

程度の繰り返しができればよいわけで、そのサイクルが早く回れば try & error も進みそう。で、なにか方法はないかと思って、ひとつ思いついたのが、

  1. デザイン用の XAML を手打ちする。
  2. なんらかの形で iOS/Android のエミュレータにアップ。あるいは、エミュレータからプル。
  3. なんらかの形で、エミュレータ上で XAML を解析して、レイアウトを作成する。

というパターンです。プレビュー自体をエミュレータ内で動かしてしまえばよいのです。

image

幸いにして、Xamarin.Forms の場合は、プラットフォーム共通のレイアウトページとして Xamarin.Forms の ContentPage が用意されています。大抵は PCL で作るんですが、この new ContentPage したものを、iOS/Android のプラットフォーム毎に再構成しています。ならば、この new ContentPage のところをすり替えてしまえば、良いわけで実際、XAML のほうではなくて、コードでちまちまと ContentPage を作っているところを、XAML から作り込めるようにすれば良いわけです。

namespace XFormsPreview
{
    public partial class MainPage
    {
        public MainPage()
        {
            InitializeComponent();
        }

        void OnClickPreview(object sender, EventArgs e)
        {
            // var page = new NewPage();
            string xml = @"<?xml version='1.0' encoding='utf-8' ?>
<Contentpage x:class="XFormsPreview.NewPage" 
xmlns:x=http://schemas.microsoft.com/winfx/2009/xaml
xmlns="http://xamarin.com/schemas/2014/forms"> <Stacklayout> <Label Text="New Page"> <Button Text="click me"> </Stacklayout> </Contentpage> "; var page = ContentPageXaml.LoadXaml(xml); this.Navigation.PushAsync(page); } } }

こんな風に文字列で XAML を設定して LoadXml すると ContentPage ができるようにします。ここは F# の Type Provider と似た感じですね。

まだテスト中なので、XAML の文字を埋め込んでいますが、これをエミュレータの動いている PC から XAML ファイルをダウンロードできるようにすれば、XAML の修正とプレビューのサイクルが早くなります。大まかの動きの目途は立ったので、あとはちまちまと Xmarin.Forms のコントロールを移し替えていきます。車輪の再発明ような感じもするし、既にあるような気もするし、本格的なデザイナが出るまでの短い間という訳でもあるけど、ひとまず、一通り動くところまで作ってみる予定。

カテゴリー: C#, Xamarin | Xamarin.Formsへ動的にXAMLデータをロードする…の手始め はコメントを受け付けていません

Xamarin.iOS/Android から F# PCL プロジェクトを使う方法

ちょっとトリッキーなので、後から Xamarin Studio 側が修正するような気もしますが、現状の回避策として。

■Xamarin.Forms を F# で使う

Xamarin.Forms は、PCL版とShared版があります。C# では “Mobile Apps” から どちらかのプロジェクトを選びますが、F# のプロジェクトにはこれがありません。じゃあ、F# では作れないのかというとそうではなくて、単純に、

– F# で PCL プロジェクトを作成する
– F# で iOS/Android プロジェクトを作成する
– iOS/Android プロジェクトから PCL プロジェクトを参照させる

という手順で Ok なハズです。PCL プロジェクト自体は、Xamarin Studio では作れないので、Visual Studio のほうで作りますが、後で述べるようにどっち作っても構わなそうです。
それぞれのプロジェクトに NuGet から Xamarin.Formss を入れてビルドが通る状態にしておきます。

iOS/Android プロジェクトから、PLC プロジェクトを参照設定したいところですが、参照設定ができません。この現象は、Visual Studio でも同じで…というか、Visual Studio では F# の iOS/Android プロジェクトが開けないので、Xamarin Studio 上でしかできません。

ひとつの方法としては、

F#のPCLプロジェクトをC#から参照するときにunable to add a reference to project… – omanuke-ekunamoの日記
http://d.hatena.ne.jp/omanuke-ekunamo/20140108/1389171418

のように、アセンブリの方を参照します。が、Xamarin.Forms の場合は、PCL 自体にロジックが入ることが多く、デバッグが結構手間なので、プロジェクト参照をさせます。

ちなみに、
– F# で作った PCL を C# プロジェクトから参照する。
– C# で作った PCL を F# プロジェクトから参照する。

ということは可能なのですが、なぜか F# から F# PCL だけが参照できません。

■ *.fsproj を開いて直接編集する

参照元(iOS/Android プロジェクト)の *.fsproj を開いて以下を追加します。

  <ItemGroup>
    <ProjectReference Include=&quot;../../WebBrickClientFs/WebBrickClientFs/WebBrickClientFs.fsproj&quot;>
      <Project>571E0A3B-E069-4B46-BBAC-B0F060351904</Project>
      <Name>WebBrickClientFs</Name>
    </ProjectReference>
  </ItemGroup>

ProjectReference に参照先のプロジェクト(PCLプロジェクト)fsprojを設定して、Project にプロジェクトGUIDを設定します。この部分は、C#からF# PCLを参照したときと同じものです。

そうして、Xamarin Studio で開くと、警告が出た状態ではありますが、参照ができるようになります。この状態でビルドも通ります。

■ビルドの警告はなぜでるのか?

ビルドをすると、こんな風な警告が出ます。

C:/Program Files (x86)/MSBuild/12.0/bin/Microsoft.Common.CurrentVersion.targets(5,5): Warning MSB3277: 同じ依存アセンブリの異なるバージョン間で、解決できない競合が見つかりました。 これらの参照上の競合は、ログの詳細度が詳細に設定されている場合にビルド ログにリストされます。 (MSB3277) (WebBrickClientFs.Android)

これは、それぞれのプロジェクトに入っている FSharp.Core のバージョンが異なるからなんですね。

Visual Stuido で作った F# PCL では、Reference Assemblies/Microsoft/FSharp/.NETCore/$(TargetFSharpCoreVersion)/FSharp.Core.dll を参照していますが、Android のほうでは、Reference Assemblies/Microsoft/Framework/MonoAndroid/v1.0/FSharp.Core.dll を参照しています。
.NETCoreのほうは、3.3.1 なのですが、MonoAndroid のほうは 2.3.1 なんですよね。これがずれてしまっているようです。ちなみに MonoTouch にあるのは 2.9.9 なのでこれもバージョンが違っています。おそらく、このバージョンが揃わない限り、先の警告が出てしまうようです。

■バージョンを揃えることができるのか?

試しに Xamarin.Forms.Core のバージョンを見ると PCL/Android/iOS ともに 1.1.1.0 になっています。おそらく、これを揃えればいいのでしょうが、現状の配布状況(Nuget にもいくつかの Fsharp.Core があります)をみると、揃えるのは難しそうですね。自前でビルドをすると、バージョンが揃うのかなと。このあたりは別途調べてみます。せめて、F# 3.0 なのか F# 3.1 なのかがわかるといいんだけど。

カテゴリー: F#, Xamarin | Xamarin.iOS/Android から F# PCL プロジェクトを使う方法 はコメントを受け付けていません

BrickPi + LOGO Mindstorms EV3 をゲームコントローラーでコントロールする(後編)

手っ取り早く動かすには、先の あしがらがらくた研究所 by いしわたむねお さんのところから、RPi_DualShock3.c を取得してビルドをします。動作確認ができたら、BrickPiNet をインストールして試します。.NET版なので、apt-get install Mono をしないとダメなんですよね…というか、RasPi のレポジトリに 4.5 の Mono が入っていたか確認してないので、これはあとで確認。手元のものは 12時間かけてビルドをしたものなので、ちょっとお試しという訳にはいかないので。そのうち、パッケージを作りましょう(つーか、パッケージの作り方から学ばないとダメなんだけど)。

■C# からジョイススティックを操作する

C言語でIOCTLしないと駄目かと思ったんですが、Linuxでは /dev/input/js0 にアクセスさえすれば、ボタンやジョイスティックの状態が取れることを知りました linux/joystick.h に詳しい情報があります。
このデバイスは、そのまま C# のファイルストリームでも使えるので、こんな感じに書けます。

Raspberry Pi Series ? Mono C# USB Joystick Handlermpolaczyk | software, robotics, engineering & geeky stuff… | mpolaczyk | software, robotics, engineering & geeky stuff…
http://mpolaczyk.pl/raspberry-pi-mono-c-joystick-handler/
C# で joystick を利用する
https://gist.github.com/moonmile/7c44e5130617c7a76a87

自前のほうは、Dualshock3 専用になっています。どうやら各社のジョイスティック/ゲームパッド事に番号が異なっていて、実地で検証しないとだめなそうです。アプリケーションのほうが対応するというパターンらしいです。

public struct js_event
{
    public UInt32 time;    /* event timestamp in milliseconds */
    public Int16 value;    /* value */
    public byte type;      /* event type */
    public byte number;    /* axis/button number */
    public override string ToString()
    {
        return string.Format("time:{0} val:{1} type:{2} num:{3}", time, value, type, number);
    }
}

type でボタン(0x01)かジョイスティック(0x02)をチェックして、number でボタンの位置を調べます。ジョイステックの場合は0がニュートラル(中央)で、スティックを倒すとそれぞれ +/- に振れます。
ここでは、まだ作っていないのですが、Dualshuck3 ではコントローラ自体の傾き検知が入っています。23,24,25,26番にデータが入って来るのですが、どの軸なんだろう、ってのは後で調べましょう。

public bool SELECT { get { return Button0; } }
public bool LeftStick { get { return Button1; } }
public bool RightStick { get { return Button2; } }
public bool START { get { return Button3; } }
public bool Up { get { return Button4; } }
public bool Right { get { return Button5; } }
public bool Down { get { return Button6; } }
public bool Left { get { return Button7; } }
public bool L2 { get { return Button8; } }
public bool R2 { get { return Button9; } }
public bool L1 { get { return Button10; } }
public bool R1 { get { return Button11; } }
public bool Triangle { get { return Button12; } }
public bool Circle { get { return Button13; } }
public bool Cross { get { return Button14; } }
public bool Square { get { return Button15; } }

public int LeftAxisX { get { return Axis0; } }
public int LeftAxisY { get { return Axis1; } }
public int RightAxisX { get { return Axis2; } }
public int RightAxisY { get { return Axis3; } }

■BrickPiNet.dll を使う

そんな訳で、BrickPiNet https://github.com/moonmile/BrickPiNet を作成中です。

/dev/input/js0 を read して、ちまちまと調べていけばジョイスティックの操作はできるのですが、ボタンを離したタイミングとか、同時押しの状態とかが面倒ですよね。
そのあたり、状態が変かしたときにイベントを発生するのが、BPiJoystick です。

using System;
using Moonmile.BrickPiNet;

namespace Sample
{
    class simplebot_joystick
    {
        BPiMotor motor1, motor2;
        BPiJoystick js;

        public void main()
        {
            BPi.Setup();
            BPi.AutoUpdate = true;
            this.motor1 = new BPiMotor() { Port = BrickPi.PORT_B, Enabled = true };
            this.motor2 = new BPiMotor() { Port = BrickPi.PORT_C, Enabled = true };

            js = new BPiJoystick();
            js.OnJoystickChanged += js_OnJoystickChanged;
            js.Setup();

            BPi.Timeout = 5000;
            this.Go();
        }

        void js_OnJoystickChanged(object sender, JoystickEventArgs e)
        {
            int ly = e.Joystick.LeftAxisY;
            int ry = e.Joystick.RightAxisY;
            Console.WriteLine("joystick {0} {1}", ly, ry);
	    int sp1 = ly/(32767/200);
	    int sp2 = ry/(32767/200);
	    move_bot( sp1, sp2 );

        }

        void move_bot(int sp1, int sp2)
        {
            this.motor1.Speed = sp1;
            this.motor2.Speed = sp2;
        }
        void Go()
        {
            var k = Console.ReadKey();
        }
    }
}

BPiJoystick を new して、OnJoystickChanged を登録すれば、ジョイスティックが変化したときだけイベントが発生します。現在のところ、ジョイスティックとボタンの状態変化が一緒になって飛んでくるので、ボタンだけ使いたいときに不便なのですが…まあ、ひとまず、これでジョイスティックを使ったロボットが簡単に作れます。

■さらに、もうひとつのモーターを使うために

これは次の課題ですが、BrickPi/LEGO mindstorms EV3 には、モーター用のポートが4つついています。現在、車輪用に2つ使っていますが、他にもモーターを使えばクレーン用とかに使えます。これはあとで実験してみましょう。他に接触センサーとかカラーセンサーもついているので、それも確認して実装していきます。

また、GPIO を直接操作することでサーボも動かせます。このあたりは、wiringPi http://wiringpi.com/ を利用して BrickPiNet から制御できるようにしていきます。

カテゴリー: RaspberryPi | BrickPi + LOGO Mindstorms EV3 をゲームコントローラーでコントロールする(後編) はコメントを受け付けていません

BrickPi + LOGO Mindstorms EV3 をゲームコントローラーでコントロールする(前編)

Raspberry Pi + BrickPi + LOGO Mindstorms EV3 をジョイスティックコントローラで遠隔制御します。まあ、単純に Raspberry Pi に Bluethooth を接続して、これを Dualshock3 で使えるようにするだけなのですが。

image

工作と小物のがらくた部屋: Raspberry Pi で Dualshock3 (Bluetooth接続ゲームコントローラ)
http://junkroom2cyberrobotics.blogspot.jp/2013/03/raspberry-pi-dualshock3-bluetooth.html
RapiroをPS3のDUALSHOCK3で動かしてみる | Makuake(マクアケ)NOTE クラウドファンディングブログ
http://www.makuake.com/blog/crowdfunding/rapiro-dualshock/

USB 接続すれば簡単なのですが、遠隔操作をするとなると2つの方法があります。Bluetooth を使う方法と WiFi を使う方法ですね。ワイヤレスコントローラ (DUALSHOCK3) は 4千円ちょっとと結構な値段がするのですが(RasPi 以上という噂もw)、まあ初回の実験だし確実につなげておきたいのでこれにしました。SBDBTのPSコントローラ用ファーム更新!凄いですよ!いろいろなゲームパッドで無線・有線による操作が可能になりました: ROBOMIC(ブログ) を見ると SBDBT というのを使えば、普通のコントローラも無線化できそうなのでが、これは今度の課題で。

Bluethooth での接続手順は、先の2つのブログに書いてあるのですが、自分用に書き写しておきます。Bluetooth は PLANEX Bluetooth USBアダプター Ver.4.0+EDR/LE(省エネ設計)対応 BT-Micro4 を使っています。最初は他の人が RasPi で動作確認できたものを使うのが無難ですね。たぶん、大抵のものは動くとは思うのですが、無線LAN で失敗したので、先のブログの人が使っているものと同じものを購入しました。

■Bluethooth の準備

apt-get で bluethooth パッケージをインストールします。

sudo apt-get install bluetooth bluez-utils bluez-compat bluez-hcidump
sudo apt-get install libusb-dev libbluetooth-dev

うまく認識ができていると

/etc/init.d/bluetooth status

で確認ができます。

image

usb の状態は lsusb で確認が可能です。

lsusb 

image

Bus 001 Device 005: ID 0a12:0001 Cambridge Silicon Radio, Ltd Bluetooth Dongle (HCI mode) ってのが、Bluetooth の USB ドングルです。 Bus 001 Device 004 のほうは WiFi の USB ドングルです。ゲームコントローラで遠隔制御はするのですが、ログ出力は初期制御のために WiFi をつなげています。電流的にどうなんだろうなぁ、という心配もありますが。一応、問題なく動いています。

■QtSixA のインストール

QtSixA is the Sixaxis Joystick Manager をインストールします。Sixaxis/DualShock3 を Linux に割り当てられるそうなので、RasPi でも OK です。SourceForge からダウンロードするのですが、http://sourceforge.net/projects/qtsixa/files/QtSixA%201.5.1/ にある QtSixA-1.5.1-src.tar.gz が最新です(といえ、2011年10月に開発が止まっていますが)。

tar -xvf QtSixA-1.5.1-src.tar.gz
cd QtSixA-1.5.1/sixad
make
sudo make install

すると、Dualshock3 を受け入れる sixad ってのがビルド&インストールされます。QtSixA-* 直下で make をすると pyuic4 コマンドがないというエラーが出るので、sixad フォルダでビルドします。Raspberry Pi 用の Debian に qgis, python-qgis がないからみたいですね。これは特に不要で、sixad とペアリング用の sixpair のみが必要です。

cd QtSixA-1.5.1/utils
make
sudo make install

■Dualshock3 とペアリングする

Daulshock3 と Raspberry Pi を USB コードで接続して、sixpair を動かします。

sudo sixpair

image

うまくいくと Bluethooth の MAC IDが取れます(たぶん USB のほう)。初回だけ USB ケーブルをつないで、あとからは Dualshock3 コントローラの「PS」ボタンでペアリングができます。

■動作させる

sixad で bluetooth の待ち受けをして、Dualshock3 で PS ボタンを押します。

sixad -start

image

(たぶん)Dualshock3 の MAC が表示されて接続状態になります。

動作確認自体は、/dev/input/js0 を表示させることで、確認ができます。

sudo cat /dev/input/js0

image

ボタンを押したり、ジョイスティックを動かしたりすると信号が流れていくのがわかります。

動作確認自体は、意外に簡単! LinuxでジョイスティックをC++から使う方法 – akihiko’s tech note からサンプルコードをビルドして動かしています。/dev/input/js0 をオープンして中身を表示させます。

image

■sixad の終了

sixad 自体は Ctrl+C で終わらせるのですが、そのままでは bluetooth が解放されないようです。

image

sixad -stop で終了させると、Bluetooth が解放されます。

ただし、cat /dev/input/js0 を見ると、いつまでもデータが送られてくる(Dualshock3を傾けるとデータが入っているのがわかる)のでペアリング自体は続いてしまっているようです。仕方がないので、Bluetooth の USB ドングルを抜いて止めるのですが、なんかいい方法はないですかね?

で、お次は、/dev/input/js0 を読み込んで、EV3 のモーターを動かすプログラムを書きます。

カテゴリー: RaspberryPi | BrickPi + LOGO Mindstorms EV3 をゲームコントローラーでコントロールする(前編) はコメントを受け付けていません

QEMU で Raspberry Pi をエミュレートする

Raspberry Pi は CPU が ARM なので、クロスコンパイラ環境が必須なのですが結構手間なので、エミュレータ環境を使って、その中で Mono や OpenCV をビルドすると良い。

QEMU – Emulating Raspberry Pi the easy way (Linux or Windows!)
http://xecdesign.com/qemu-emulating-raspberry-pi-the-easy-way/

そんな訳で、上記からエミュレータをダウンロードして解凍すると、あっさりと動きます。「QEMU Raspberry Pi」で検索するとぼろぼろと出てくるので、これが定番だったんだ。

■初期環境を作る

Raspberry PiをQEMUでエミュレートする方法 | 意識低い開発者のBlog
http://blog.ymyzk.com/2013/12/raspberry-pi-qemu/
Raspberry Pi のイメージファイルを拡張する | 意識低い開発者のBlog
http://blog.ymyzk.com/2013/12/raspbian-image-resize/

にある手順に従って、

  • /etc/ld.so.preload を開いて #/usr/lib/arm-linux-gnueabihf/libcofi_rpi.so のようにコメントアウトする。
qemu-system-arm -kernel kernel-qemu ^
    -cpu arm1176 -m 256 -M versatilepb -no-reboot ^
    -serial stdio ^
    -append "root=/dev/sda2 panic=1 rootfstype=ext4 rw init=/bin/bash" ^
    -hda D:isoRaspberryPi2014-06-20-wheezy-raspbian.img
vi /etc/ld.so.preload
  • RasPi のイメージファイルが 2GB 程度なので +16G 程度追加しておく。
qemu-img resize 2014-06-20-wheezy-raspbian.img +16GB

こうすると、ひとまず QEMU 上で Raspberry Pi が動作します。

■ネットワークを構築する

単純にコンパイルするだけならば上記の設定で十分なのですが、今回は apt-get をして Mono や OpenCV の環境を構築するので、外部ネットワークの接続が必須です。これは、TAP 型で外部接続させます。

オープンギャラリー:QEMUゲスト間ネットワーク接続環境
http://www.os-museum.com/qemutapnet/qemutapnet.htm

Windows環境の場合は、

OpenVPN をインストール http://www.openvpn.jp/

  • ネットワーク接続の名前を「TAP32」のように設定しやすいものに変更。
  • 「ネットワークと共有センター」→「アダプターの設定の変更」で「ブリッジ接続する」
    私の環境では Hyper-V が入っているため vEthernet とブリッジしていますが、普通はイーサネット(PCが接続しているLANポート)を使うと思われます。

image

起動時に -net tap.iframe=TAP32 の様に設定

qemu-system-arm -kernel kernel-qemu ^
	-cpu arm1176 -m 1012 -M versatilepb -no-reboot ^
	-serial stdio ^
	-append "root=/dev/sda2 panic=1 rootfstype=ext4 rw" ^
	-net nic -net tap,ifname=TAP32 ^
	-hda 2014-06-20-wheezy-raspbian.img

■制限

  • メモリが -m 256 で 256MB しか設定できません。それ以上を設定しても無視される。QEMU の制限かも
  • OpenVPN の接続スピードが 10Mpbs になっている。今となっては相当遅いので、何か別のローカルネットワークの手段を考えたほうがいいかも(方法が分からん)。git などからソースダウンロードすると非常に遅いので、別途 Debian でダウンロードしたものをコピーすると良い。
  • 実行速度は、実機とほとんど変わらない。Mono をビルドすると10時間程度かかりました。まあ、ビルドしている間に実機がふさがれるのを防ぐのとバックアップがとりやすい(*.img ファイルそのまま保存すればOK)ので、これはこれで使い勝手がよいかと。
カテゴリー: RaspberryPi | 2件のコメント

Raspberry Pi 用のクロス環境を考察する

Mono を Raspberry Pi でビルドすると、12時間かかってしまうので非常に手間。考えてみれば、Raspberry Pi の CPU は ARM なのだから、Android のクロスコンパイル環境を持ってくれば動くのでは?と思ったのが発端です。

まだ調査中なので、どの環境がよいかは後でまとめます。

■方法

最初は、Linux 上にクロスコンパイラ環境を用意して、ビルドした後に Raspberry Pi にもっていけば、と思ったのですが、いくつか方法が選べることに思い至りました。

  • Linux Debian wheezy のクロスコンパイラ環境で Mono をビルドして、Raspberry Pi にインストール
  • Linux 上の ARM 仮想環境を作って、ビルドしたのち、Raspberry Pi にインストール
  • Android 実機を使ってビルドしたのち、Raspberry Pi にインストール
  • Raspberry Pi 自体をエミュレートする環境を作る

Android の実機を使うのは、ARM 仮想環境(エミュレータ)よりもスピードが速いだろう、って思いつきなのですが、手元の CPU スピードからいけば多少 ARM エミュレーターが遅くても RasPi よりは断然早いだろう、という想像です。Android 実機/エミュレータ に対しては adb で shell を動かすことができるから gcc さえ入れ込めば、とも思ったのですが、Android 上でこれができるのか?というのと Mono から配布されているソースパッケージを Android の環境に合わせるのは結構手間ではないか?との想定から、現実的なところは、Debian 上のクロスコンパイラか、Raspberry Pi のエミューレター環境ですね。エミュレーター環境とはいえ、IO をエミュレートしたいわけではなくて、Raspberry Pi の Debian 環境にそろえたいだけなので、単純に ARM CPU で動作する Linux 環境を作ればよいだけです。

■クロスコンパイラ環境を Debian 上に作る

debian wheezy 上でのクロスコンパイラのビルド を参考にして、クロスコンパイル環境を構築。

GCC_TARGET=armel
dpkg-buildpackage -b -uc -us

のところで、Error になってしまったので、後で調査。

■Scratchbox を使う

Mono:ARM – Mono を見ると、ScratchBox を使う方法が示されている。Scratchbox – ARMクロス開発環境 を見ると仮想環境を作る方法らしい。./configure を直すのは確かに大変なので、直接 ARM の仮想環境を作って、其の中に Gcc の入れ込んでしまうのがよい。

ここの環境に Raspberry Pi の Debian が入ればいいのだが?これができるかどうか分からない。

■qemuを使う

と、ここまで書いて

Raspberry Pi • View topic – Re: Emulating Raspberry Pi in Windows the easy way
http://www.raspberrypi.org/forums/viewtopic.php?f=26&t=5743 

のところに RasPi のエミュレーターがあった。http://sourceforge.net/projects/rpiqemuwindows/ qemu-system-arm.exe を使うらしい。QEMU は Linux 上で動くものが多いけど、Windows 上で動くのであれば、これでいいかも。

と、あっけなく起動できた w これでいいかも。RasPi の ディスクイメージをそのまま使っているので 2GB に制限されているけど、8GB 程度のイメージを作ればそれで使えるのでは?ちょっと試してみよう。

image

 

 

■参考

日記/2014-03-28/debian wheezy 上でのクロスコンパイラのビルド – BAK
http://jr0bak.homelinux.net/~imai/pukiwiki/pukiwiki.php?%C6%FC%B5%AD%2F2014-03-28%2Fdebian%20wheezy%20%BE%E5%A4%C7%A4%CE%A5%AF%A5%ED%A5%B9%A5%B3%A5%F3%A5%D1%A5%A4%A5%E9%A4%CE%A5%D3%A5%EB%A5%C9
Mono:ARM – Mono
http://www.mono-project.com/Mono:ARM
Scratchbox – cross compile enviroument
http://homepage2.nifty.com/SECS/scratchbox/index.html
QEMUでARM環境を手にいれる | 團長の小部屋
http://wwwdantyo.wordpress.com/qemu%E3%81%A7arm%E7%92%B0%E5%A2%83%E3%82%92%E6%89%8B%E3%81%AB%E3%81%84%E3%82%8C%E3%82%8B/

カテゴリー: RaspberryPi | 6件のコメント

Xamarin の FormsGallery を XAML で書き直してみる

Xamarin.Forms のサンプルに FormsGallery があります。Xamarin.Forms で使っているひと通りのコントロールが載っているので、どんなものがあるのか見るのに便利なのです。ただ、中のコードは、C# でひとつひとつのコントロールを作るスタイルになっているので、XAML で作る場合にはどうするのか、いまひとつ解りづらい…ので、コンバートしてみました。

https://github.com/moonmile/XFormsGallery

ところどころ未完成なところがあるのですが、ひとまず1日で出来上がったところまで。
共通プロジェクトは PCL を使っています。XAML を素直に共通化するだけであれば PCL で良いでしょう。WinStore/WinPhone のユニバーサルプロジェクトのようにコードビハイドに手を加える場合には、共有プロジェクトを使うと良いかと。

■注意

現時点の Xamarin.Formsのバージョンは「1.1.1.6206」になっているのですが、Visual Stuido 2013 に含まれているテンプレートでは、「1.0.6186」になっています。ほとんど変わらないですが TableView の概観がちょっと違うので、Xamarin の Sample のように、最新のバージョンに合わせています。プロジェクトごとに NuGet で最新にしてください。

以下、ざっと XAML にするときに手順を書き下しておきます。

■App.cs を書き換える

プロジェクトテンプレートで作ると、PCL プロジェクトの App.cs にページを作るコードがあります。ここをごっそり削って、Page クラスのインスタンスを返します。ただし、サンプルの場合には特殊でナビゲーションを使っています。この場合は特別に NavigationPage のインスタンスを作成します。

public class App
{
    public static Page GetMainPage()
    {
        return new NavigationPage(new HomePage());
    }
}

■簡単な LabelDemoPage を作る

ラベルだけが表示されるページはこんな感じです。

<?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?>
<ContentPage xmlns=&quot;http://xamarin.com/schemas/2014/forms&quot;
					   xmlns:x=&quot;http://schemas.microsoft.com/winfx/2009/xaml&quot;
					   x:Class=&quot;XFormsGallery.LabelDemoPage&quot;>
	<ContentPage.Padding>
		<OnPlatform x:TypeArguments=&quot;Thickness&quot;>
			<OnPlatform.iOS>10,20,10,5</OnPlatform.iOS>
			<OnPlatform.Android>10,0,10,5</OnPlatform.Android>
			<OnPlatform.WinPhone>10,0,10,5</OnPlatform.WinPhone>
		</OnPlatform>
	</ContentPage.Padding>
	<StackLayout>
		<Label Text=&quot;Label&quot; Font=&quot;50&quot; HorizontalOptions=&quot;Center&quot;></Label>
		<Label Font=&quot;Large&quot; VerticalOptions=&quot;CenterAndExpand&quot;>
			<Label.Text>Xamarin.Forms is a cross-platform natively backed UI toolkit abstraction that allows developers to easily create user interfaces that can be shared across Android, iOS, and Windows Phone.</Label.Text>
		</Label>
	</StackLayout>						
</ContentPage>

OnPlatform のところは、OSによってコードを切り替えらるところで、元のサンプルの Device.OnPlatform にあたります。Top だけ切り替えられるような気もするのですが、うまくいかなかったので Thickness まるごと切り替えます。iOS だけ 20 ドット広げてあるのは、iPhone のステータスバー用です。ただし、ナビゲーターの場合は自動でステータスバーに対応しているらしく、これはあまり意味がありません。全画面表示するページを作るときに使う技ですね。

元ネタの LabelDemoPage を見ると、次のようになるので、解りやすいような解りにくいような。

    class LabelDemoPage : ContentPage
    {
        public LabelDemoPage()
        {
            Label header = new Label
            {
                Text = "Label",
                Font = Font.BoldSystemFontOfSize(50),
                HorizontalOptions = LayoutOptions.Center
            };

            Label label = new Label
            {
                Text =
                    "Xamarin.Forms is a cross-platform natively " +
                    "backed UI toolkit abstraction that allows " +
                    "developers to easily create user interfaces " +
                    "that can be shared across Android, iOS, and " +
                    "Windows Phone.",

                Font = Font.SystemFontOfSize(NamedSize.Large),
                VerticalOptions = LayoutOptions.CenterAndExpand
            };

            // Accomodate iPhone status bar.
            this.Padding = new Thickness(10, Device.OnPlatform(20, 0, 0), 10, 5);

            // Build the page.
            this.Content = new StackLayout
            {
                Children = 
                {
                    header,
                    label
                }
            };
        }
    }

XAML 自体は、Xamarin Studio 上で手書きをします。Visual Studio 上でも書けないことはないのですが、タグのコード補完が効かないので、非常にやりにくいです。本当はデザイナが欲しいところでしょうが、スマートフォンのような小さな画面の場合には自由度が少ないので、ほとんど StackLayout か Grid を使ってしまうのであまり関係ないでしょう。ですが、iPad や Surface のようなタブレットPC のレイアウトをするときにはデザイナは必須ですよね。このあたりは、今後どうするのか不明です。

Windows Phone(SilverLightですが)の XAML に直す時に ConvertPageToUIElement メソッドを呼び出しているので、この逆変換ができれば Visual Studio 上で XAML のデザインができるようになるかもしれません。

■HomePage ページを作る

トップページは、メニュー用のページです。
TableView でリスト表示をしているのですが、この親子関係が解りづらいところです。

<?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot; ?>
<ContentPage xmlns=&quot;http://xamarin.com/schemas/2014/forms&quot;
				xmlns:x=&quot;http://schemas.microsoft.com/winfx/2009/xaml&quot;
				x:Class=&quot;XFormsGallery.HomePage&quot;
				Title=&quot;XAML Forms Gallery&quot;>
	<TableView Intent=&quot;Menu&quot;>
		<TableView.Root>
			<TableSection Title=&quot;Views for Presentation&quot;>
				<TextCell Text=&quot;Label&quot; x:Name=&quot;cellLabel&quot; ></TextCell>
				<TextCell Text=&quot;Image&quot; x:Name=&quot;cellImage&quot;></TextCell>
				<TextCell Text=&quot;BoxView&quot; x:Name=&quot;cellBoxView&quot;></TextCell>
				<TextCell Text=&quot;WebView&quot; x:Name=&quot;cellWebView&quot;></TextCell>
				<TextCell Text=&quot;Map&quot; x:Name=&quot;cellMap&quot;></TextCell>
			</TableSection>
			<TableSection Title=&quot;Views that Initiate Commands&quot;>
				<TextCell Text=&quot;Button&quot; x:Name=&quot;cellButton&quot; ></TextCell>
				<TextCell Text=&quot;SearchBar&quot; x:Name=&quot;cellSearchBar&quot;></TextCell>
			</TableSection>
...

ただし、元ネタの HomePage.cs を覗いてみると、TableView <- TableView.Root <- TableRoot <- TableSection <- TextCell <- TextCell の関係が解るので、そのまま書き写しています。XAML では TableRoot を省略しても動作するようです(TableView.Rootのほうは省略できないんですよね。ここは Children にして欲しかった)。

this.Title = "Forms Gallery";
this.Content = new TableView
    {
        Intent = TableIntent.Menu,
        Root = new TableRoot
        {
            new TableSection("Views for Presentation")
            {
                new TextCell
                {
                    Text = "Label",
                    Command = navigateCommand,
                    CommandParameter = typeof(LabelDemoPage)
                },

                new TextCell
                {
                    Text = "Image",
                    Command = navigateCommand,
                    CommandParameter = typeof(ImageDemoPage)
                },

タップなどの操作は非常に制限されています。労力を減らすたか、これから実装するのか解りませんが、TextCell の場合は Command パターンで呼び出しをしています。ただし、主要なボタンコントロールなどには Clicked イベントが用意されているので、そのままコードビハイドで書くことができます。


WPFやWinStoreで XAML を手書きしたことがある人ならば、デザイナがなくても大体想像して書けるかなという感じですね。Xamarin.Forms の DTD は無いようなので、親子関係はマニュアル http://iosapi.xamarin.com/?link=N%3aXamarin.Forms を見る必要があるのですが、まあ元ネタを見比べていけば、これも想像で書ける範囲かと思います。

カテゴリー: C#, Xamarin | Xamarin の FormsGallery を XAML で書き直してみる はコメントを受け付けていません