Xamarin.iOS/Android で、文字列と画像をPCLを使って共有させる方法

Xamarin.iOS/Androidアプリで、バンドルリソース処理を完全共通化できそうな仕様 – Qiita
http://qiita.com/kochizufan/items/69d69f37cf991d452226#comment-172bac67ec6257dc81c2
Embedded Resource | Xamarin
http://docs.xamarin.com/content/EmbeddedResources/

多分、PCL を使うと共通できるだろうなぁ、とは思っていたのですが、これといってよい方法が思いつかなかったのです。iOS/Android それぞれのプロジェクトに入れるしかないか(コピーあるいは共有プロジェクト)とは考えていたものの。なるほど、PCL のアセンブリから直接リソースを読みだす技がありましたね。

https://github.com/xamarin/mobile-samples/blob/master/EmbeddedResources/SharedLib/ResourceLoader.cs

のソースをざってと見ていたのですが、iOS/Android 側で GetEmbeddedResourceStream メソッドを呼び出さないといけないのが難点です。と言いますか、PCL の .NET ライブラリには、Assembly クラスの static メソッドが非常に制限されていて、なんともしようがないのです。と思っていたのですが、typeof(ResourceLoader).GetTypeInfo().Assembly という技で、通常の Assembly を取れる技を知りました。
いやいや、よくよくリソースファイル Resources.Designer.cs を覗いてみれば、

global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("XamarinPortableRes.Lib.Properties.Resources", typeof(Resources).GetTypeInfo().Assembly);

なる書き方がされていて、なーんだ。普通に使える方法じゃないですか。

■文字列リソースを追加する

Embedded Resource の例では、文字列リソースをテキストファイルから読み込んでいますが、これは普通のリソースにしたいですよね。普通に「プロジェクト」→「リソース」で文字列を追加できます。

まだ試してはいませんが、国際化による言語切り替えもこれでやると楽かと。

■文字列リソースを使う

使う側も簡単です。string 型で返してくれるので、そのまま Text プロパティなどに設定できます。

Android の場合

FindViewById(Resource.Id.textView1).Text =
    XamarinPortableRes.Lib.Properties.Resources.Message1;

iOS の場合

this.text1.Text = XamarinPortableRes.Lib.Properties.Resources.Message1;

■画像リソースを追加する

PCL プロジェクトのリソース追加は「文字列」のみに制限されています。制限されている理由はわからないのですが、無理やり突っ込んでも *.resx ファイルから *.desinger.cs に変換でダメになるので、そのままでは使い勝手が悪いです。
なので、Resources というフォルダを作って、画像ファイル(pngファイルなど)を入れます。プロパティウィンドウでビルドアクションを「埋め込まれたリソース」にしておきます。こうすると、先の「バンドルリソース処理を完全共通化~」に書いてあるように「XamarinPortableRes.Lib.Resources.MarkBlue.png」のように、ドットつながりでリソースが取れます。

このリソース名をそのまま使ってもいいのですが、文字列指定を間違えると実行エラーになるし、どうせならばインテリセンスが効くようにしたいので、少しだけ工夫します。

こんな風に、リソースを取り出すための ResourceLoader クラスを作ります。ResourceManager みたいなものです。リソース自体は1回しか読み込まないので、static で十分です。名前から検索するのは Embedded Resource の借用ですが、初回のみ GetManifestResourceNames メソッドでリソース名の一覧を取得します。

public static class ResourceLoader
{
    static internal string[] Names { get; set; }
    static internal Assembly Assembly { get; set; }

    public static System.IO.Stream GetObject(string resourceName)
    {
        if (ResourceLoader.Assembly == null)
        {
            ResourceLoader.Assembly = typeof(ResourceLoader).GetTypeInfo().Assembly;
            ResourceLoader.Names = ResourceLoader.Assembly.GetManifestResourceNames();
        }
        try
        {
            string path = ResourceLoader.Names.First(x => x.EndsWith(resourceName, StringComparison.CurrentCultureIgnoreCase));
            return ResourceLoader.Assembly.GetManifestResourceStream(path);
        }
        catch
        {
            return null;
        }
    }
}

インテリセンスが効くように Resources クラスに「手動」でプロパティを作ります。このあたりは T4 で作ってもいいし、まあ色々。これで、Stream までは取得できます。

public class Resources
{
    public static System.IO.Stream MarkBlue
    {
        get { return ResourceLoader.GetObject("MarkBlue.png"); }
    }
    public static System.IO.Stream MarkGreen
    {
        get { return ResourceLoader.GetObject("MarkGreen.png"); }
    }
    public static System.IO.Stream MarkNone
    {
        get { return ResourceLoader.GetObject("MarkNone.png"); }
    }
    public static System.IO.Stream MarkOrange
    {
        get { return ResourceLoader.GetObject("MarkOrange.png"); }
    }
    public static System.IO.Stream MarkPurple
    {
        get { return ResourceLoader.GetObject("MarkPurple.png"); }
    }
    public static System.IO.Stream MarkRed
    {
        get { return ResourceLoader.GetObject("MarkRed.png"); }
    }
}

■画像リソースを使う

画像リソースから取ってくると、System.IO.Stream を取得できますが、そのままだと扱いにくいので System.Drawing.Bitmap にしたいところなのですが、iOS/Android とそれぞれ異なる(UIImage, Android.Graphics.Bitmap)ので、ヘルパーメソッドを作っておきます。

Android の場合は、Stream から Bitmap を返すまで。

global::Android.Graphics.Bitmap ToBitmap(System.IO.Stream st)
{
    using (var mem = new System.IO.MemoryStream())
    {
        st.CopyTo(mem);
        st.Close();
        var data = mem.ToArray();
        var bmp = global::Android.Graphics.BitmapFactory.DecodeByteArray(data, 0, data.Length);
        return bmp;
    }
}

こんな風に画像設定をします。

this.imageBlue = FindViewById(Resource.Id.imageView1);
imageBlue.SetImageBitmap(ToBitmap(XamarinPortableRes.Lib.Resources.MarkBlue));

iOS の場合は、UIImage を作成します。

UIImage ToBitmap(System.IO.Stream st)
{
    var data = NSData.FromStream( st );
    st.Close();
    var bmp = UIImage.LoadFromData(data);
    return bmp;
}

そして、設定

this.imageBlue.Image = ToBitmap(XamarinPortableRes.Lib.Resources.MarkBlue);

本来は ToBitmap まで共通化したいところですが、それぞれ画像の扱いが異なるので、ここぐらいまで。更に Windows Store, Windows Phone を追加すると、より汎用的になるはずですが、これはあとから。

# github で、ToBitmap を Stream クラスの拡張メソッドにする方法に変えたので、説明も後で変更します。

■サンプルコード

https://github.com/moonmile/XamarinPortableRes

■実行結果


カテゴリー: Android, C#, Xamarin, iOS | Xamarin.iOS/Android で、文字列と画像をPCLを使って共有させる方法 はコメントを受け付けていません

アリスはルイスにプラダを知られたくない

#プログラマが思春期の娘に言われると傷つく一言 – Togetterまとめ
http://togetter.com/li/294232

を見ていて、「お父さんと同じ名前空間に居たくない!」と「パパのコードと一緒にビルドしないで!」が秀逸だったので C# で実現してみました。正確には名前空間じゃなくて Prada というクラス名を見せないようにします(まあ、クラス名をネストの深い名前空間に入れておけば、名前空間自体を探ることも難しいと思うのですが…これはあとで調べてみよう)。

単純に AliceRoom のアセンブリのクラスを他のアセンブリに公開したくない場合は、private なり internal を使えばよいのですが、そうなると Lolita に Prada を渡したく。Lewis(今回はお父さん役)には見せたくないけども、友達の Lolita とは共有したい場合は、InternalsVisibleTo 属性を使ってフレンド共有をします。こうすることで internal クラスが LolitaRoom から見えるという状態になります。

// Lolita クラスにのみ公開する
[assembly: InternalsVisibleTo("LolitaRoom")]

namespace AliceRoom
{
    public class Alice : Room.IPerson
    {
        public void Init()
        {
            var bag = new Prada();
            bag.Owner = "Alice";
            bag.Content = "my secret";
            this.box = bag;
        }
        public string GetBagContent()
        {
            var bag = box as AliceRoom.Prada;
            if (bag == null)
                return "NONE";
            else
                return bag.Content;
        }
    }
    /// <summary>
    /// Plada クラスは LolitaRoom にのみ公開
    /// </summary>
    internal class Prada
    {
        // Owner を設定できるのは Alice だけ
        public string Owner { get; internal protected set; }
        // コンテンツは Lolita も取得できる
        public string Content { get; set; }
    }
}

さて、もうひと息やって、Prada オブジェクトを Lolita に渡すことにします。
渡し方をどうするかというと、Alice.Post( Lolita ) みたいに渡したいですよね。実際に渡す Prada オブジェクトは、以下の Main から見えない(公開しているのは LolitaRoom アセンブリのみ)ので、何を誰に渡すのかが必要です。しかし「何を」渡すか自体は、Alice と Lolita しか知らないので、どうやって渡すかは試案のしどころ。

class Program
{
    static void Main(string[] args)
    {
        var alice = new AliceRoom.Alice();
        var lolita = new LolitaRoom.Lolita();
        var lewis = new LewisRoom.Lewis();
        // Pradaを作成
        alice.Init();

        // AliceRoom.Prada は見れない
        Console.WriteLine("alice room : {0}", 
            alice.GetBagContent());
        Console.WriteLine("lolita room: {0}", 
            lolita.GetBagContent());
        // Lolitaに渡す
        alice.Post(lolita);
        Console.WriteLine("alice room : {0}",
            alice.GetBagContent());
        Console.WriteLine("lolita room: {0}",
            lolita.GetBagContent());

        // Lwewis から AliceRoom.Prada は見れない
    }
}

仕方がないので、渡すためのインターフェースを作ります。ここでは、IPerson インターフェースを作って object 型で渡しているのですが、途中シリアライズ化する方法もあります。そういう意味では INotify を使ってもいいと思います。

namespace Room
{
    public abstract class IPerson
    {
        protected object box { get; set; }
        public void Post(IPerson dest) {
            dest.box = this.box;
            this.box = null;
        }
    }
}

あと、Prada の Owner 情報は Alice なので、Lolita が勝手に書き換えてはいけません。クラス情報は Lolita に公開するけど、Prada.Owner.set は Alice だけが使えるように「internal protected set」にしておきます。

namespace LolitaRoom
{
    public class Lolita : Room.IPerson
    {
        public void Post(Room.IPerson dest, object obj)
        {
            this.box = obj;
        }
        public string GetBagContent()
        {
            var bag = box as AliceRoom.Prada;
            if (bag == null)
                return "none";
            else
                return bag.Content;
        }
    }
}

そんな訳で「お父さんと同じ名前空間に居たくない!」を実現したのがこちら。

サンプルコードは github
https://github.com/moonmile/DontLookMyNamespace

カテゴリー: 開発, C# | アリスはルイスにプラダを知られたくない はコメントを受け付けていません

F#のコンピュテーション式をVBAを使って真似してみる…というかDSLの真似

別にコンピュテーション式を調べていたのではなくて、「ひょっとしたらコンピュテーション式とCustomOperation属性で独自の二項演算子がエミュレートできるのでは?」と思っていて探していた訳で、結果的には「できない」なんですが、その派生としてマーチン・ファウラー著のDSL本を斜め読みしたので、その関連で。

32.4 Using Multiple Builders for the calender(Java) のところまで読みながら、局所的なマクロは業務コードを組むと良く出てくるパターンで、lex/yacc まで使わなくても(いや、yacc 使ったほうが手っ取り早いときもあるんですが)アセンブラ風のコマンドが先頭にあってパラメータが続くというパターンは解析がしやすいので、作るのも解釈するのも楽です。それもあって、手軽なものならば1回こっきりの自作コードを作っては捨ててもそんなに惜しくない。と言いますか、そのあたりが「スクリプト」感覚ですよね(最近のLLという意味ではない、昔のスクリプティングという意味で)。

C#やJavaの場合にはメソッドチェーンを使いますが(LINQの場合はクエリ式で並べるというパターンのあるかと)、VBの場合は With というキーワードがあります。

With obj
 .Name = "masuda"
 .Country = "Japan"
End With

みたいな書き方で、obj.Name = “masuda” と羅列する代わりに With obj で囲む方法ですね。C言語の頃は、変数名が長くなると面倒くさいので、

Person *longlonglongobjectname ;
Person *o = longlonglongobjectname;
strcpy( o.Name, "masuda" );
strcpy( o.Contry, "Japan" );

みたいに代入したりします。いや、これだとスタックを消費してしまうから、Person &o = *longlonglongobjectname; な風に参照を使ってみたりする場合もある訳ですが、この手の方法は C# でもよくやります。まあ、C# の場合は、長い変数名とかメソッド名は、インテリセンスを使って記述できるように工夫する方法もよくやりますが。

さて、さっきの With を使う方法は、VB.NET でも使えていて、メソッドにも使えるのですが、VB.NETの場合メソッド呼び出しは必ず括弧が入るので、いまいちスクリプトっぽくないんですよね。そういう点では、F# はスクリプトっぽいし、メソッドチェーンもスクリプトっぽい。見た目は大事です。

じゃあ、どうせ書くならば VBA ということで(VB6でもOK)、メソッドを羅列してスクリプトっぽく書くと、こんな風に書けます。

Option Explicit

Sub test()
    Dim cb As New CalenderBuilder
    With cb
        .Add "DSL tutorial"
        .On__ 2009, 11, 8
        .From "09:00"
        .To__ "16:00"
        .At__ "Aarhus Music Hall"
        
        .Add "Making use of Ptterns"
        .On__ 2009, 10, 5
        .From "14:15"
        .To__ "15:41"
        .At__ "Aarhus Music Hall"
    End With
End Sub

.From とかで羅列していますが、最初の CalenderBuilder オブジェクトに対して With しているだけです。
これを実装するためには、2つのクラス(CalenderEvent、CalenderBuilder)が必要です。

CalenderEvent.cls は単純なデータクラス

Option Explicit

Public Name As String
Public year As Integer
Public month As Integer
Public day As Integer
Public startTime As String
Public endTime As String
Public location As String

CalenderBuilder.cls がビルダークラス

Option Explicit

Public items As New Collection
Public cur As CalenderEvent

Public Sub Add(title As String)
    Set cur = New CalenderEvent
    items.Add cur
    cur.Name = title
End Sub

Public Sub On__(year As Integer, month As Integer, day As Integer)
    With cur
        .year = year
        .month = month
        .day = day
    End With
End Sub

Public Sub From(s As String)
    cur.startTime = s
End Sub

Public Sub To__(s As String)
    cur.endTime = s
End Sub

Public Sub At__(s As String)
    cur.location = s
End Sub

関数名が To__ のようにアンダーバーをつけて伸ばしているのは、スクリプトにしたときにインデントを揃えるためとキーワード逃れです。From, To__ 関数の中身はサボっていますが。

じゃあ、コンピュテーション式風にIfとかWhileを再定義できるかという出来ないわけで、コンピュテーション式とは程遠い訳ですが、DSL には近いかなと思ったり。
DBの初期設定とかヒアドキュメントに書けるのが利点かと思います。コードに直接設定を書くので汎用性はないのですが、むしろ汎用性を無くして設定ファイルが散逸しないメリットを活かせます。

ちなみに、この場合はビルダークラス使わなくても、大抵の場合は以下で書く。
CalenderEvent メソッドのほうに From メソッド等を移動させて、こんな感じ。

Dim obj As CalenderEvent
Dim items As New Collection

With obj
	Set obj = New CalenderEvent
	items.Add obj
    .Name "DSL tutorial"
    .On__ 2009, 11, 8
    .From "09:00"
    .To__ "16:00"
    .At__ "Aarhus Music Hall"
         
	Set obj = New CalenderEvent
	items.Add obj
    .Name "Making use of Ptterns"
    .On__ 2009, 10, 5
    .From "14:15"
    .To__ "15:41"
    .At__ "Aarhus Music Hall"
End With
カテゴリー: 開発 | F#のコンピュテーション式をVBAを使って真似してみる…というかDSLの真似 はコメントを受け付けていません

Xamarin StuidoでF# iOS アプリ(storyboard版)を作る方法

現時点でアルファ版ですが、Xamarin Studio 5.0 だと F# で iOS/Android のアプリが組めます。ただし、現時点では F# プロジェクトのテンプレートが storyboard 無しなので、UI を作ろうとするとコードで書かなくて大変…なのですが、ちょっと工夫するとできました、という例として。
追記 2014/05/14 alpha版のXamarin Studioしか確認してないのですが、アドイン自体は Xamarin Studio 4 のほうでもインストールできます。そうすると、現状でもF# iOSアプリは可能?

サンプルコードは https://github.com/moonmile/XamarinFSharp にあります。

■手順

1.プロジェクトにファイルを追加で「empty iphone storyboard」を追加する。

image

2. 空の storyboard に ViewController を貼り付けて、UI を作る。
3. ViewController のクラスは、*ViewController.fs に設定しておく。
4. アクセスできるように各コントロール/ウィジットに名前を付けておく。
5. 保存をすると、*ViewController.cs と *ViewController.desinger.cs が作られるので、プロジェクトから削除しておく。
6. 対応する Outlet を手作業で *ViewController.fs に書き込む。

 [<Outlet>]
 member val edit1 : UITextField = null with get, set

7. ViewController のコンストラクタを直す。

type XamarinFSharp_iOSViewController ( handler : IntPtr ) =
inherit UIViewController ( handler )

8. AppDelegate.fs を開き Window プロパティを追加する。

 [<Register ("AppDelegate")>]
type AppDelegate () =
  inherit UIApplicationDelegate ()
  // make Window property
  override val Window = null with get, set

9. Info.plist を開き Main Interface を ‘MainStoryboard’ に設定する。

image

これをビルドして実行すると、C# と同等に動きます。

image

*ViewController.fs のコードを見ると分かるのですが、Outlet 部分が ViewController クラスの本体に入っているので、C# のように partial クラスを作って *.designer.fs に追い出したい訳なのですが、どうもうまくいかないようです。F# で部分クラスを作るのは type … with を使えばいいらしい http://stackoverflow.com/questions/4376275/c-sharp-to-f-convert-public-partial-class-device-marshalbyrefobject のですが、http://msdn.microsoft.com/en-us/magazine/hh394149.aspx を見ると none support みたいです。なんか、Outlet 部分を追い出す方法はないのかな。まあ、あるいは MVVM パターンにしてしまうか。MvvmCross に直すパターンと、Prism.Mvvm を試してみるか。

カテゴリー: F#, Xamarin, iOS | Xamarin StuidoでF# iOS アプリ(storyboard版)を作る方法 はコメントを受け付けていません

【速報】F# + Xamarin.iOS + Xamarin Studio で iPhoneアプリを作れるよ

Visual Studio 2013 上で iOS デザイナが動作しているのは Build 2014 でチラ見せしていたので、いずれは…と思っていたのですが、アルファ版がでました。

【速報】iOS Designer for Visual Studio リリース! – Xamarin 日本語情報
http://ytabuchi.hatenablog.com/entry/2014/05/07/182031

既に Xamarin Studio for Mac のほうでは Xcode Interface Builder を使わずに Xamarin Studio 内蔵の iOS Designer が動いています。アルファ版のほうは

  • Xamarin Studio 5.0
  • Xamarin.iOS 2.0

となるので同時にメジャーバージョンアップです。Xamarin Studio の UI も少し変わってきています。新しい Xamarin の目玉としては、iOS デザイナが Xamarin Studio あるいは Visual Studio 内で動くので、ビューを操作するたびに Interface Builder を動かさなくていいということと、storyboard に本格的に対応したらしく、storyboard を使わないプロジェクトテンプレートがなくなっていることです。多分、iOS デザイナを内蔵したためと思われます。旧来の Visual Studio のほうには残っているので、これが新しくインストールしたときに消えるのかどうかは不明ですが。

デザイナ自体は、田淵さんのブログにもある通り、Mac 側に Xamarin.iOS Build Host を動作させないといけないので Mac は必須です。まあ、Xamarin Studio for Mac で動かせばいいんですがね。

と、普通な? Xamarin Studio 速報はそれぐらいにして、注目したいのは F# の iOS/Android のプロジェクトテンプレートが追加されたことです。いままで、Xamarin Studio で F# が使えていたのですが、ASP.NET と NUnit だけだったのですが、今回は Android/iOS が追加されています。よっぽど物好きな(失礼)エンジニアの方が、Xamarin 社にいらっしゃるのか…NYC の F#er の有志が係っているのかは不明なのですが。いやいや fsharp.org を見ると Xamarin との結束は意外と昔からある模様です。

Visual Studio 上では、Android のプロジェクトテンプレートしかありませんが、Xamarin Studio for Mac には iOS のプロジェクトテンプレートもあります。

image

image

Mac 上で作った F# プロジェクトを Visual Studio に持ってきたら開けるかと思ったけどダメみたいですね。4925A630-B079-445D-BCD4-3A9C94FE9307 が異なるらしいので、調べると

MonoTouch and F# part I –
http://7sharpnine.com/posts/monotouch-and-fsharp-part-i/

なところで色々やっています。って、これ 2013年2月だから結構前からやってますね。

Mac 上で作ると、こんな感じで F# on iPhone シミュレータが動きます。

image

ちなみに Android F# は Visual Studio 上で作れます。

もうひとつすごいのは、New York で mobile と F# の勉強会があってそこで F# + Xamarin.iOS の発表があります。1時間半ありますが、Xamarin に入るのは 30分以降です。

[nyc-fsharp] Cross-platform mobile with F# & Xamarin with Rachel Reese on Vimeo
http://vimeo.com/94300454

デモをやっているのが、Visual Studio 2013 上なので、たぶん F# の Xamarin.iOS 拡張が動いていると思われます。F# でマインスイパーを作って iOS シミュレータ上で動かしています。この手のゲームロジックは F# のような関数言語が得意とするところで F# でがりがり組むよりも、コアなところは F# でって感じです。F# はコアってことでPCL(Portable Class Library)にして、UIをC#で書くってことを考えていたのですが、これだとUIもF#で書くと楽かも。

カテゴリー: F#, Xamarin, iOS | 1件のコメント

Xamarin.iOS+MvvmCrossでstoryboardを使う方法

MvvmCrossでプロジェクトテンプレートを作ると、丁寧な設定があって便利なのですが、Xamarin.iOSで作るときに Xcode5 に対応した storyboard のほうは出してくれません。やり方がよくわからないので、できるのかできないのかすら分からないので、必要があってきちんと調べてみました。

MVVMCross support for Xamarin.iOS Storyboards – Stack Overflow
http://stackoverflow.com/questions/22126929/mvvmcross-support-for-xamarin-ios-storyboards
slodge/eh ・ GitHub
https://github.com/slodge/eh
Databinding ・ MvvmCross/MvvmCross Wiki ・ GitHub
https://github.com/MvvmCross/MvvmCross/wiki/Databinding

なところにサンプルコードがあります。

slodge/eh にあるのは、PCL を使わない方法なので、PCL を使う方法示しておきます。やってることは一緒ですね。

■XamarinMvx.Coreの作成

XamarinMvx.CoreをPCLで作成する。MvxViewModel を継承した ViewModel で、バインディングする View の名前と揃えて置く。実は揃えない方法があるのだけど(iOSの場合は、揃えない方法でバインドする)、Androidと共有することを考えるとそろえておくのが無難。「MainView」にバインドするのであれば「MainViewModel」にしておく。

public class MainViewModel : MvxViewModel
{
    public int _X = 0;
    public int X
    {
        get { return _X; }
        set { _X = value; RaisePropertyChanged(() => X); }
    }
    public int _Y = 0;
    public int Y
    {
        get { return _Y; }
        set { _Y = value; RaisePropertyChanged(() => Y); }
    }
    public int _Ans = 0;
    public int Ans
    {
        get { return _Ans; }
        set { _Ans = value; RaisePropertyChanged(() => Ans); }
    }
}

App.cs はあってもなくてもいい気がするのだが、Android と共有するために以下のままにしておく。

public class App : Cirrious.MvvmCross.ViewModels.MvxApplication
{
    public override void Initialize()
    {
        CreatableTypes()
            .EndingWith("Service")
            .AsInterfaces()
            .RegisterAsLazySingleton();

		// TODO: MainViewModelをMainViewに結び付ける
        RegisterAppStart();
    }
}

RegisterAppStartを使うと、ひとつのViewModelがひとつのViewにしかバインドできないし、じゃあ複数のViewにバインドするときはどうするの?という疑問が残るけど、ここではそのままで。slodge/eh 自体が、複数の画面遷移を使っているので参考になると思う。

■XamarinMvx.iOSの作成

Androidの場合には、*.axmlに

local:MvxBind="Text Hello"

のようにバインドしていくのだが、iOSのstoryboardの場合は、Xcodeが *.storyboard を書き換えてしまうので直接編集すると上書きされて戻ってしまう。なので、コードを使ってバインドをする。
iOS用のプロジェクト(storyboardを使う方)を作った後に、NuGetでMvvmCrossを導入する。

AppDelegate.cs を開いて、AppDelegate の継承を MvxApplicationDelegate に直す。

public partial class AppDelegate : MvxApplicationDelegate

FinishedLaunching メソッドをオーバーライドして、アプリの起動時に setup.Initialize を呼び出すようにする。base.FinishedLaunching を呼び出してはいけない。

public override void FinishedLaunching(UIApplication application)
{
    var setup = new Setup(this, this.Window);
    setup.Initialize();
}

XamarinMvx.iOSViewController.cs(ViewControllerのファイル)を開いて、MvxViewController から継承するように変更する。

public partial class XamarinMvx_iOSViewController : MvxViewController

ViewDidLoad メソッドの最初におまじないの一行(MvxViewModelRequestの呼び出し)を書いた後にバインドする。

public override void ViewDidLoad ()
{
    this.Request = new MvxViewModelRequest(null, null, new MvxRequestedBy());
    base.ViewDidLoad();

	// Perform any additional setup after loading the view, typically from a nib.
    // TODO: こうすると、View, ViewModel の名前の制約を回避できる
    var set = this.CreateBindingSet<XamarinMvx_iOSViewController, MainViewModel>();
    // TODO: コードでバインドする
    set.Bind(this.editX).To(vm => vm.X);
    set.Bind(this.editY).To(vm => vm.Y);
    set.Bind(this.textAns).To(vm => vm.Ans);
    set.Apply();
}

MvxViewModelRequestのテンプレートに渡すのは ViewModel のクラス名。これを Request に設定しておく。この行がないと、base.ViewDidLoad で例外が発生する。
CreateBindingSet では、Viewクラス、ViewModelクラスを設定する。この場合は、Viewクラスが「XamarinMvx_iOSViewController」でViewModelクラスが「MainViewModel」になる。こんな風に、名前をそろえなくて良い。
これを変数で受けて、Bind していく。Apply で複数のバインドを適用する。まあ、大抵は複数のバインドをするのでこれでOK。

ちなみに、Android の場合も同じようにできる。

protected override void OnCreate(Bundle bundle)
{
    base.OnCreate(bundle);

    // Set our view from the "main" layout resource
    SetContentView(Resource.Layout.Main);

    // axml に local:MvxBind="Text X" を書かずにコードでバインドする方法
    // iOSの場合も同じ
    // この方式にすると、ViewとViewModelの名前を統一しなくてよい。
    var set = this.CreateBindingSet<MainView, MainViewModel>();
    set.Bind(FindViewById<EditText>(Resource.Id.editText1)).To(m => m.X);
    set.Bind(FindViewById<EditText>(Resource.Id.editText2)).To(m => m.Y);
    set.Bind(FindViewById<TextView>(Resource.Id.textView1)).To(m => m.Ans);
    set.Apply();
    // TODO: 説明を簡単にするため、テキストだけバインド
    MainViewModel vm = this.ViewModel as MainViewModel;
    Button button = FindViewById<button>(Resource.Id.button1);
    button.Click += (s, e) =>
    {
        vm.Ans = vm.X + vm.Y;
    };
}

*.axmlに書かないので、XAMLとは異なるけどバインド部分は統一できそう。ちなみに、FindViewById<EditText> のところは型指定しないと例外が発生する。

ボタンのクリックイベントは、IBActionを使ったほうが楽なので、こっちで。ICommandのほうはまた後で。

partial void clickCalc(MonoTouch.Foundation.NSObject sender)
{
    var vm = this.ViewModel as MainViewModel;
    vm.Ans = vm.X + vm.Y;
}

■実行

実行するとこんな感じ。

■サンプル

サンプルはこちら。
https://github.com/moonmile/XamarinMvx

ついでに、Android と Windows ストアプリも含めています。

■日本語の参考文献

Xamarin – マルチプラットフォーム MVVMフレームワーク「MvvmCross」を使う – Qiita
http://qiita.com/amay077/items/c4227663b5a5e540dc13
MvvmCross と Xamarin for Visual Studio で iOS, Android, Windows アプリを作る流れ – Yuta Watanabe’s Blog
http://yutawatanabe.hatenablog.com/entry/mvvmcross-xamarin-visual-studio
MvvmCross について – Xamarin 日本語情報
http://ytabuchi.hatenablog.com/entry/2014/03/03/121833

カテゴリー: Android, C#, WinRT, Xamarin, iOS | Xamarin.iOS+MvvmCrossでstoryboardを使う方法 はコメントを受け付けていません

昭和の日なので「ああっ女神さまっ」を振り返ってみる

たまたま先日立ち読みしたアフタヌーンで「ああっ女神さまっ」が最終回だったので記録的に書いておこう。

連載が始まった頃にちょうど大学生で、なんかちょうどリアルタイムな時期でした。その前の「逮捕しちゃうぞ」のあたりと、その後の「サクラ大戦」があって、江川達也の「Be Free」からの系列で、アシスタントということで、理系ということもあって初回から単行本は買っていたわけですが、同時期にあった「月下の棋士」とか「無限の住人」に比べるとコメディの具合となかなか進展しない恋の具合は「宮本君から君へ」という漫画もあったりして、まあ、江川達也風でどう行くのだろうか?という感じでした。もちろん、前作の「逮捕しちゃうぞ」の頃からバイク・車とメカニカルな描写が得意なわけで、そっち方面は猫実工大にも表れているし、変な先輩あたりは当時の大学の雰囲気もあって、そういう変な感じを許容する昭和な時代だったかなと。いやいや、後から振り返れば、バブルな時代であって、ちょうどそのまま就職すれば会社入るまえにハワイ旅行で拘束なんて時代でしたから、最初の頃はそんな雰囲気も漂っていますよね。

絵柄がミュシャ風になったのは「女神」だったからなのか、そういう路線に変更にしたのかは不思議なところですが、士郎正宗の「攻殻機動隊」が細いラインで細かく描かれていた(その前の「アップルシード」とは異なる)メカニカルなものが、車やバイクのエンジンの描写ではなく、そのままベルダンディの衣装の描写になったのかな、と推測しています。一方で江川達也は細い線で「東京大学物語」を書いていたわけで(その前は「タルるーとくん」あたりだったけ?)、考えててみれば「ジョジョの奇妙な冒険」も似た時期ですよね。そんな中でごった煮な月刊アフタヌーンがあったわけでして。

と実は40巻ぐらいまでだらだらと買っていたのですが、ちょっと追わなくなったんですよね。年齢的にもそういう漫画を読む歳ではなくなった…つーか、いや、そういう感じ読んでいる訳ではなけど、さすがにあれや、正直「いつまで続くんだろー」ぐらいな感じになっておりました。連載途中からアフタヌーン一押しになってしまって(最初からそうだったのか覚えていないのですが、あの「江川達也」のアシスタント、「逮捕~」の次の作品、ってことで期待は大きかったハズです)、そのミュシャ風のグッズに寄った時点からついていけなくなった感じ?いや、なんだろう、10次元の話あたりは覚えているし、その後のエピソードのそこそこ覚えているのですが、んー、うる星やつらの「ビューティフル・ドリーマー」を何度も見せられる既視感というか、先に進まなさというか、いや先に進まないのはうる星やつらあたりで経験済みなので、そういう楽しみ方もありなんですが、なんだろう、メカニカルなところと無理矢理つなげている趣味的なところがあったのが、いつの間にかなくなってしまったところですかね。嘘くさい理系的な薀蓄をあきらめてしまったあたりからかな。

んがしかし、25年も連載を続けられるのは、体力的にも営業的にもアレなので、それはそれ。Kindle版も売っているようなので、買い直してみるかな。

カテゴリー: 雑談 | 昭和の日なので「ああっ女神さまっ」を振り返ってみる はコメントを受け付けていません

F#でWPFアプリを作る方法

F#でGUIを扱うのはなかなか大変なのですが、できないことはありません…と思っていましたが、WPFアプリならばプロジェクトテンプレートがあるよ、ってのを最近知りました。

Visual Studio 2013 で「オンラインテープレートの検索」で「F# WPF」で検索してみます。すると、いつか F# で Windows アプリを作るテンプレートがあるんですね。

image

が…、WPF アプリと、WPF+MVVM アプリのプロジェクトをダウンロードしたものの、いまいちよく分からなくて途方に暮れます。WPF アプリのほうは中身が空っぽ(Emptyなのでそりゃそうなんですが)で先がわからなく、WPF+MVVMは、グリッドへの DataBind の例なんですが、ふつうの DataBind がどうするのかわかりません。

そんなわけで、上をベースにして簡単なサンプルを作りました。
moonmile/SampleFSharpUI
https://github.com/moonmile/SampleFSharpUI

これも簡単すぎて実務には足りないんですが、取っ掛かりぐらいにはなるかと。WPF + F# の組み合わせはふつうに XAML デザイナを使ってアプリを作れるので、C# と同じように作れます。ただし、コードビハインドの仕組みがないので、ボタンのクリックイベントは自前で用意する必要がありますね。以下、ざっと説明を。

■SampleFSharpUI.WPF

先の F# empty windows app をベースにして XAML にテキストボックスを並べます。テンプレートの XAML では Grid タグをつけ忘れているので、それを追加するところから。

image

MainWindow.xaml.fs のようなコードビハインドはなくて、App.fs にある

type MainWindow = XAML<"MainWindow.xaml">

が、その役割を果たします。TypeProvider.XAML でコーディング時に MainWindow.xaml を解析して、クラス(type)に割り付けます。こうしておくと、

let window = MainWindow()
...
let x = Convert.ToInt32( window.text1.Text )

な感じで x:Name=”text1” で指定した名前を、window.text1 のようにプロパティとして参照できるようになります。TypeProvider 自体がよくわからないのですが、なんかファイルか文字列を読み込んで型にして返してくれるものみたい。

ボタンのクリックイベントは

   window.button1.Click.Add( fun _ -> 
    let x = Convert.ToInt32( window.text1.Text )
    let y = Convert.ToInt32( window.text2.Text )
    let ans = x + y
    window.text3.Text <- ans.ToString()
   )

と書いていますが、window.button1.Click |> Event.add を使って

   window.button1.Click 
    |> Event.add( fun _ -> 
    let x = Convert.ToInt32( window.text1.Text )
    let y = Convert.ToInt32( window.text2.Text )
    let ans = x + y
    window.text3.Text <- ans.ToString()
    )

のように書くこともできます。まあ、普通にメソッドにしたほうがいい気も。

アプリの起動自体は

 [<STAThread>]
(new Application()).Run(loadWindow()) |> ignore

となって、最初の loadWindow を呼び出すわけですが、この書き方だとすべてグローバル変数扱いになってしまうので、実務的には適度にクラスかモジュール分けが必要ですよね。

■SampleFSharpUI.MVVM

MVVM では、INotifyPropertyChanged を継承した ViewModelBase クラスを作っておいて、DataModel クラスを作ってます。MVVM の ViewModel にあたるクラスなんですが、Model クラスがないのは気持ち悪いのでこんな名前にしています。

type DataModel() =
    inherit ViewModelBase()
    let mutable _X : int = 10
    let mutable _Y : int = 20
    let mutable _ANS : int = 30

    member this.X 
        with get() = _X
        and set(value) = 
            _X <- value
            base.OnPropertyChanged "X" 

    member this.Y
        with get() = _Y
        and set(value) = 
            _Y <- value
            base.OnPropertyChanged "Y" 

    member this.ANS
        with get() = _ANS
        and set(value) = 
            _ANS <- value
            base.OnPropertyChanged "ANS" 

プロパティの get/set の並べ方はこんな感じで。クラスの最初に mutable を置かなければいけないのと(これでよかったっけ?)、OnPropertyChanged メソッドに「メソッド名」を渡さないといけないのがいまいちですね。CallerMemberName 属性を使って、コンパイル時にチェックしたいところです。

XAML へのバインドは C# と同じで Text=”{Binding X, Mode=TwoWay}” のように書けます。この方法で C# とビューが共有できます。

type MainWindow = XAML<"MainWindow.xaml">

let _model = new DataModel()

let loadWindow() =
    let window = MainWindow()
   
    // Your awesome code goes here and you have strongly typed access to the XAML via "window"

    // 初期値
    _model.X <- 0
    _model.Y <- 0   
    _model.ANS <- 0
    window.button1.Click.Add( fun _ -> 
        // データバインドで設定
        _model.ANS <- _model.X + _model.Y 
    )
    window.Root.DataContext <- _model
    window.Root

あとは、_model オブジェクトを作って DataContext にバインドすれば OK です。XAML のデザイン時に Binding でインテリセンスを効かせたいときは、C# のときと同じようにデザイン時のバインドを使えば OK です。

d:DataContext="{d:DesignInstance {x:Type local:DataModel}, IsDesignTimeCreatable=True}"

実行結果はこんな感じ

image

■Window Store App ではどうするのか?

タイププロバイダで XAML が読めるんだから、ストアアプリの XAML も読めるはず…なんですが、F# でストアアプリを作ろうとすると XAML デザイナがうまく認識しません。さらに言うと、そのままでは、参照設定がうまく WinRT にほうにできません。どうやらコンパイル時の *.targets をうまく設定しないといけないんですよね。現時点ではうまくできてません。

XAML 用のデザイナは C:/Program Files (x86)/MSBuild/Microsoft/WindowsXaml/v12.0/ あたりを見ているので、ここに FSharp 版をつくらないとダメな模様

あとプロジェクトテンプレートのGUIDが BC8A1FFA-BEE3-4634-8014-F334798102B3 で、C:/Program Files (x86)/Microsoft Visual Studio 12.0/Common7/IDE/CommonExtensions/Microsoft/WindowsXamlFlavor/Microsoft.VisualStudio.Windows.UI.Xaml.Project.pkgdef になるので、これを参考にしながら F# 版を作らないとダメかも。

カテゴリー: F# | 1件のコメント

C#によるiOS、Android、Windows対応のアプリ開発 — 予約中

C#によるiOS、Android、Windows対応のアプリ開発
http://www.amazon.co.jp/dp/4822298345/

表紙がまだできていませんが、Amazon で予約開始です。そのうち日経BPさんのサイトにも載ると思います。サブタイトルがないけど「Xamarin + Visual Studio で作成する~」みたいな感じですね。Xmarin + Visual Studio で iOS アプリ、Android アプリ、Windows ストアアプリを同時並行で作っていきます。

image

概要はAmazon に書いてある「内容紹介」の通りです、もっと具体的に言えば、MVP Community Camp 2014 で発表しました。 | Moonmile Solutions Blog で作ったサンプルが元です…と言いますから、執筆途中の原稿のちらみせがスライド資料の発表になります。

更に具体的に言うと http://www.moonmile.net/blog/archives/category/dev/xamarin のカテゴリにある内容が 280 ページ(まだ、途中なので確定ではありませんが)に詰まっていると思ってください。ブログのほうでは調査も含めてぱらぱらと書き散らかしていきましたが、モバイル機器(iPhone/iPad、Android、Windowsタブレット)でどうやったら共有して開発できるだろうか?という指針あるいは方向性になります。途中で Build 2014、Windows 8.1 Update、Visual Studio Update 2 RC、すまべん@関東 が挟まっているので、それも込み。榎本さんの記事 を参考にしつつ重ならないように注意しながら内容を絞り込んでいます。 Windows Phone 8.1 のほうは時期的に間に合わなかったのでさわりの部分だけ、将来的に Xamarin Studio で共有プロジェクトができると Xamarin.iOS/Android も含めたユニバーサルアプリができる可能性があります(ユニバーサルアプリで使っているSharedプロジェクトをXamarin.iOSで使う で手作業でプロジェクトファイルを書き変えると代替はいけるかと。Xamarin Studio for Mac の対応待ちになるのですが)。

そんな訳では、発売日は 5/29 だそうです。ええ、de:code の日ですね。ってことは、de:code 行かねばならぬのかーと、営業的に捻出中です。ええ、word の稟議書を眺めながら(自営だし)。

カテゴリー: Xamarin | C#によるiOS、Android、Windows対応のアプリ開発 — 予約中 はコメントを受け付けていません

Windows Phone 8.1 と Xamarin.iOS/Android で PCL を使う方法

moonmile/XamarinXmlSerialize なところの説明を少し。手元のシリアライザ練習用に作ったものですが、ついでにユニバーサルアプリにを追加して Xamarin.iOS/Android から PCL を参照させてみました。

image

こんな風な、ソリューションの構成になっているところで

image

こんな風な PCL を作ります。普通に作ると Windows Phone 8.1 と Xamarin.iOS/Android の両方はチェックできないのですが、いずれ「できる」だろうってことで先取りします。

方法は簡単です。以前やってみた PCL のターゲットフレームワークに Xamarin をインストールせずに Xamarin.Android を追加する | Moonmile Solutions Blog の方法を使います。PCL の参照設定の「.NET」を選択してプロパティウィンドウを見ると、パスが出てきます。

image

C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETPortable\v4.5\Profile\Profile111

このパスをエクスプローラーで開いて、SupportedFrameworks フォルダに Xamarin.iOS.xml と Xamarin.Android.xml をコピーするだけです。この2つのファイルは Profile7 あたりにあります。Profile111 は、今回の VS2013 Update 2 RC でできたフォルダで、まだ Xamarin.iOS/Android が対応していないだけ(と思われる)です。将来的には対応されるでしょう。

image

そうすると、Xamarin.iOS/Android から PCL を参照できるようになるので、Windows Phone 8.1 とも共有が可能です。

先のサンプルでは、もう少し手を入れていて、Shared フォルダに BasicMain.xaml のコードビハインドを分離させています。

image

こんな風に、partial にしておいて、ボタンイベントだけをこっちの共有プロジェクトに書き出します。Windows ストアと Windows Phone の xaml には、「Click=”ClickLoad”」のようにイベントを設定するので、複数のビューとコードビハイドがうまく分離できますね、という方法です。考えてみたら、ファイルのリンクの追加でもできた話なのですが、まあ共有プロジェクトのほうが効果的かと。

image

で、共有プロジェクトは現在、Windows ストアと Windows Phone にしか出てきませんが、ユニバーサルアプリで使っているSharedプロジェクトをXamarin.iOSで使う | Moonmile Solutions Blog な方法を使うと、Xamarin.iOS/Android からも使えます。これも先行きは Xamarin で対応されるでしょう。となると、メソッド名と引数の並びが同じであればいいわけで、うまくやると、iOS の Storyboard と Android の axml と共有できるかもしれません(MvvmCross でもうまくいくかも)。

Android の場合は、「void ClickLoad(object sender, EventArgs e)」となるので引数の型が EventArgs の型だけ異なります。iOS の場合は IBAction の関係から「partial void clickCalc(MonoTouch.Foundation.NSObject sender)」となるので、引数の型が全く違います。まあ、#if ディレクティブで区切ればいいのですが、いちいち #if で分岐させるのも面倒ですよね。C/C++ ならば #define マクロで逃れるところですが、C# だとそれもできない。VB6 の頃のようにボタンクリックイベントから、別のクラスあるいはCOMを逐一呼び出すよう名な形にはしたくない。このあたりはちょっと考えどころ。

カテゴリー: WinRT, Xamarin | Windows Phone 8.1 と Xamarin.iOS/Android で PCL を使う方法 はコメントを受け付けていません