Xamarin.Forms を XAML を使って書くために

list このエントリーをはてなブックマークに追加

昨日の続きを少し書き足しておきます。

Visual Studio Community 2015 で Xamarin.Forms を使う | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/7873

Xamarin.Android/iOS を使ってそれぞれのプラットフォームに向けて C# で書けるのはそれはそれで充分です。UI にしても最新機種にあわせる場合は、Java/Swift で書くだろうし、iOS のほうはタイムラグ無しで Xamarin.iOS が出ていたし、Android のほうも数週間ほどで追随してきます。おそらく、Xamarin が Microsoft に買収された今後も同じ方針でしょう。同じ方針だけど、プラットフォーム三社の思惑があるし、そこはどう動くかわからない。ただし、あまり「最新機能」にとらわれず、Android/iOS かつ UWP の3つのプラットフォームに対して同時にリリースすることを考えると Xamarin.Forms が非常に有効に働きます。v1 の場合は、実質 Android/iOS の二機種だったけど、v2 からは UWP が入ったので Android/iOS/Windows の三機種なのでメリットが大きいのです。

サンプルは XAML がコードで書かれている

Cross-Platform で PCL や Shared をすると、UI のサンプルコードも一緒についてきます。単純に画面に “Welcome to Xamarin Forms!” と表示するだけのものです。PCL(移植可能)なプロジェクトの App.cs に入っています。

public App()
{
    // The root page of your application
    MainPage = new ContentPage
    {
        Content = new StackLayout
        {
            VerticalOptions = LayoutOptions.Center,
            Children = {
                new Label {
                    XAlign = TextAlignment.Center,
                    Text = "Welcome to Xamarin Forms!"
                }
            }
        }
    };
}

見慣れたような見慣れないような、スタイルで ContentPage を構築します。WPF などでデザイナを使って XAML を書いたり、Android で AXML を使ったり、Xcode で storyboard を使ったりしていると「え?」な気分になるのですが…まあ、え?ってなります。

最初に断っておきますが、モバイル機種のような小さい画面の場合には、結構 XAML のコードベースで作業をするほうが楽だったりします。PC などの液晶モニタに対して画面が小さい(解像度は同じだったりするけど)ので、操作するボタンとかアイコンを細かく配置するよりも、おおざっぱに位置を自動計算させたほうがうまくいきます。また、Android 機種は画面がまちまちなのでそれに揃えるためにもフローレイアウトみたいな感じで作るといいですよね、って感じです。Web のグリッドシステムと同じ感じで作るほうが手早いし、Xamarin.Forms との相性もよいです。

Forms Xaml Page を追加する

これをXAML形式で書けるようにします。

  1. PCL のプロジェクトに「Forms Xaml Page」を追加します。

PCL プロジェクトに Page1.xaml が追加されます。

  1. Label の Text プロパティを「Welcome to Xamarin Forms!」に書き換える。
<?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="App4.Page1">
  <Label Text="{Binding MainText}" VerticalOptions="Center" HorizontalOptions="Center" />
</ContentPage>

Binding を使って ViewModel を使うようになっていますが、ここでは Text プロパティを直接変更します。変更はちょっとずつやると解りやすいので。

<?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="App4.Page1">
  <Label Text="Welcome to Xamarin Forms and XAML!" VerticalOptions="Center" HorizontalOptions="Center" />
</ContentPage>
  1. App.cs のコンストラクタを書き換える。

追加した XAML が表示されるように App コンストラクタを書き換えます。

public App()
{
    // The root page of your application
    this.MainPage = new Page1();
}

MainPage プロパティに表示したいページクラスを設定すれば ok です。名前が「Page1」になっています。

これをビルドして実行するだけでエミュレータ上での動作が変わります。

試しにボタンのクリックイベントを付けてみる

XAML界隈では嫌われ者のコードビハイドですが、まあ手軽に作るにはこっちのほうが便利です。

XAML を書き換えて、x:Name プロパティにボタンとラベルに名前をつけます。これで、Page1 クラスのプロパティに追加されます。

<?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="App4.Page1">
  <StackLayout>
    <Label x:Name="text1" Text="Welcome to Xamarin Forms and XAML!" />
    <Button x:Name="btn1" Text="Click Me" />
  </StackLayout>
</ContentPage>

コードビハイドを書く。

public partial class Page1 : ContentPage
{
    public Page1()
    {
        InitializeComponent();
        this.btn1.Clicked += Btn1_Clicked;
    }

    private void Btn1_Clicked(object sender, EventArgs e)
    {
        this.text1.Text = "クリックした";
    }
}

ボタンの Clicked イベントにラムダ式で書いても良いし、イベント用のメソッドを作ってよいでしょう。

実行すると、こんな感じでボタンが効くようになります。

現時点の Hyper-V のエミュレータのほうは、以下のようなエラーが出て2回目以降のコード変更が反映されません。エミュレータ再起動するか、Android 内で対象のアプリをアンインストールするかの対処が必要です。

04-06 11:01:12.659 D/OpenGLRenderer( 2235): Use EGL_SWAP_BEHAVIOR_PRESERVED: true
04-06 11:01:12.661 D/GLHostConnection( 2235): Waiting for host to establish connection for PID 2235 (App4.Droid)
04-06 11:01:12.662 D/GLHostConnection( 2235): HostConnection::get() New Host Connection established 0x9c319f40, tid 2235
04-06 11:01:12.687 D/GLHostConnection( 2235): Waiting for host to establish connection for PID 2235 (App4.Droid)
04-06 11:01:12.711 D/GLHostConnection( 2235): HostConnection::get() New Host Connection established 0x9c6e3050, tid 2260
04-06 11:01:12.713 I/OpenGLRenderer( 2235): Initialized EGL, version 1.4
04-06 11:01:12.719 W/EGL_emulation( 2235): eglSurfaceAttrib not implemented

MVVM化する

このままコードビハイドで書いていっても良いのですが、どうせなので Binding を使って MVVM 化します。Clicked の ICommand はそのままにして、Label のほうだけ。

  1. ViewModel クラスを追加する。

PCL プロジェクトに ViewModel のクラスを追加します。いろいろ引き継いできた BindableBase を継承します。
ラベルに表示する文字列を Text プロパティで連携させます。

using System;
using System.ComponentModel;
using System.Runtime.CompilerServices;

namespace App4
{
    class MyViewModel : BindableBase
    {
        private string _text;
        public string Text
        {
            get { return _text; }
            set { this.SetProperty(ref this._text, value); }
        }
    }
    public abstract class BindableBase : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;
        protected bool SetProperty<T>(ref T storage, T value, [CallerMemberName] String propertyName = null)
        {
            if (object.Equals(storage, value)) return false;

            storage = value;
            this.OnPropertyChanged(propertyName);
            return true;
        }
        protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
        {
            var eventHandler = this.PropertyChanged;
            if (eventHandler != null)
            {
                eventHandler(this, new PropertyChangedEventArgs(propertyName));
            }
        }
    }
}
  1. XAML で Binding を使う

Label の Text プロパティを Binding に変えます。

<?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="App4.Page1">
  <StackLayout>
    <Label Text="{Binding Text}" />
    <Button x:Name="btn1" Text="Click Me" />
  </StackLayout>
</ContentPage>

  1. ViewModel を BindingContext に割り当てる

いろいろ XAML+MVVM でどのプロパティが使われているのかが混乱していますが、Xamarin.Forms の場合は BindingContext という名前になっています。

public partial class Page1 : ContentPage
{
    public Page1()
    {
        InitializeComponent();
        this.btn1.Clicked += Btn1_Clicked;
        _vm = new MyViewModel();
        this.BindingContext = _vm;
    }
    MyViewModel _vm;
    private void Btn1_Clicked(object sender, EventArgs e)
    {
        _vm.Text = "MVVM でクリックした";
    }
}

これで無事 MVVM 化ができます。

本家のサンプル

本家のサンプルが github にあるので、ごっそりダウンロードして試してみるとよいです。

GitHub – xamarin/xamarin-forms-samples: Sample apps built using the Xamarin.Forms framework
https://github.com/xamarin/xamarin-forms-samples

これを何処に使うのか?

まあ、一般的な話で言えば Android/iOS のアプリを同時に作れるという話が順当です。が、もうちょっと話を飛躍させると、こんな感じになります。

「FlashAir+Arduino鉄道模型制御-1」 by 綾瀬ヒロ│MakersHub
https://makershub.jp/make/1030

綾瀬ヒロさんも Xamarin を使っていますが、この手のタブレット操作画面を作るのに Xamarin.Forms が有効なのです。スマートフォンじゃなくて、iPad や Surface、大き目の Android タブレット(候補としては Amazon タブレットぐらいしかないのですが)で操作系のアプリを作ろうと思うと、スマートフォンのような小さな画面とは異なった作りが必要になります。かつ、タッチパネル使える/必須なので、操作としてはスマートフォンっぽい感じで使えるのが良い訳です。
UWP が使えるので、Windows IoT Core on Raspberry Pi でも使えますよね(実際使える)。なので、このあたり UWP だけで閉じていれば、Surface, IoT Core 画面でデバッグができる&操作が同じにできるという話なのですが、もうちょっと Android/iPad に範囲を広げると Xamarin.Forms で画面を構成するのは結構有効です。IoT 絡みになれば、スマートフォンの通信や加速度センサーだけでなく、具体的に自作したセンサーの類を使えるわけですから、UI まわりをさっくりと作る(ある意味で、スマートフォンの最新機能には依存しない)ことができます。高めではありますが、Android 組み込みボードのほうでも Xamarin が使えるようになるとよいかなと思ったりしてます。

そのあたりを含めて、入門的に手を付けるのがお薦めです。

カテゴリー: 開発, Xamarin パーマリンク

Xamarin.Forms を XAML を使って書くために への1件のフィードバック

  1. 匿名 のコメント:

    Xamarin.Formの資料を探していました。
    分かりやすくて、助かりました。

コメントは停止中です。