ちょっと未来のXamarin.Formsを試す(1.3.0 Technology Preview)



この記事は [Xamarin Advent Calendar 2014](http://qiita.com/advent-calendar/2014/xamarin) 、兼 [XAML Advent Calendar 2014](http://qiita.com/advent-calendar/2014/xaml) の9日目です。

この記事では Xamarin.Forms の時期バージョン(ver.1.3.0)で追加される機能の中から、
`Styles`、`Triggers`についてXAMLとC#のサンプル付きでご紹介します。また、アプリケーションのエントリ部分が変更されるので、その修正方法も併せてご紹介します。


* ver.1.3.0で追加される機能
  * Xamarin.Forms.Application
  * Dynamic Resource
  * Styles
  * Triggers
  * Behaviors
  * etc...

WPFなどで既に存在する機能の追加が多く、使い方も基本的に同じようです。

## XAML Advent Calendar から来た方へ

Xamarin ご存知でしょうか？よく知らない方のために軽〜くご説明いたします。

### Xamarin とは

* C#(.NET)で iOS、Android、Macアプリを開発できるSDK(有償)。
* そのSDKを作っている会社の名前。

### Xamarin.Forms とは

* ワンソースでiOS、Android、Windows Phone の3プラットフォームの画面を作成できるフレームワーク。
* BindingやMessagingをサポートしMVVMなアプリ開発が可能。
* **XAML**でViewを定義可能。(ただしコントロールは独自)


## Xamarin.Froms を更新する

    記事中の環境は Mac & Xamarin Studio(Stableチャンネル)です。
    執筆時点の Xamarin.Forms 1.3.0 はプレビュー版です、紹介した内容は正式版で変更される可能性があります。
    実行結果はiOS Simulatorのみとなっています、お許しください。


新規にソリューションを作ります。
プロジェクトのテンプレートは「Blank APP (Xamarin.Forms Portable)」を選択します。

![スクリーンショット 2014-11-29 20.34.58.jpg](https://qiita-image-store.s3.amazonaws.com/0/40121/5a11253d-cc6f-48e1-185e-398f5b93b4d0.jpeg "新規ソリューション作成")


### Xamarin.Forms の更新


さて、安定版のXamarin.FormsはNuGetマネージャからダウンロード可能ですが、ver.1.3.0は現在プレビュー版であるため通常の手順では導入できません。<del>そこで、Xamarin Forumからnupkgファイルをダウンロードし、NuGetパッケージマネージャに対してローカルディレクトリをリポジトリとして追加する必要があります。</del>nuget.orgから取得できるようになりました。

各プロジェクトの「パッケージ」ディレクトリを右クリックして「Add Packages」を選択するとパッケージマネージャが開きます。

![AddPackagesを選択.jpg](https://qiita-image-store.s3.amazonaws.com/0/40121/0de2b482-3e47-2665-c71f-ad1609ecd00c.jpeg "AddPackagesを選択")

「Show pre-release packages」をチェックして「xamarin.forms」と検索してください。
安定板の他に「-pre」サフィックスの付いたパッケージが見つかりますので最新版に更新します。

![NuGetマネージャ.jpg](https://qiita-image-store.s3.amazonaws.com/0/40121/e66a526a-e38d-8c0c-4640-9d98cbcb9f58.jpeg "NuGetマネージャ")

更新に成功するパッケージのバージョンが上がります。(更新前はver.1.2.3.x)

![更新後のパッケージバージョン.jpg](https://qiita-image-store.s3.amazonaws.com/0/40121/5bbacd22-d745-6e39-4f51-935a8794afee.jpeg "更新後のパッケージバージョン")


この作業を全てのプロジェクトで行います。


## Xamarin.Forms.Application に対応する

プロジェクトテンプレートを`Xamarin.Forms.Application`を使用するコードに修正します。
この変更により以下の機能が追加されます。

* root pageの変更を容易化
* Properties dictionaryを経由する永続的データストア
* Sleep/Resume/Start イベント
* アプリケーションレベルのresource dictionary

この対応をしないと **XAMLからのView生成が失敗** します。
どうやらView要素がアプリケーションレベルのリソースディクショナリを検索できるようにするため、XAMLからのView生成処理が変更されているようです。


### PCLプロジェクト

`App`クラスを次のように修正します。

```c#:
	public class App : Application
	{
		public App()
		{
			MainPage = new ContentPage
			{
				Content = new Label
				{
					Text = "Hello, Forms !",
					VerticalOptions = LayoutOptions.CenterAndExpand,
					HorizontalOptions = LayoutOptions.CenterAndExpand,
				},
			};
		}

//		protected override void OnSleep()
//		{
//			base.OnSleep();
//		}

//		protected override void OnResume()
//		{
//			base.OnResume();
//		}

//		protected override void OnStart()
//		{
//			base.OnStart();
//		}
    }
```

`App`クラスに`Xamarin.Forms.Application`を継承させ、最初の画面を`MainPage`プロパティに格納します。
`OnSleep`、`OnResume `、`OnStart `メソッドをoverrideするとライフサイクルイベントを捕まえられるようです。(今回は使いません)


### iOSプロジェクト

`AppDelegate`クラスを次のように修正します。

```c#:
	public partial class AppDelegate : Xamarin.Forms.Platform.iOS.FormsApplicationDelegate
	{
		public override bool FinishedLaunching(UIApplication app, NSDictionary options)
		{
			Forms.Init();
			LoadApplication(new App());
			return base.FinishedLaunching(app, options);
		}
	}
```
### Androidプロジェクト

`MainActivity`クラスを次のように修正します。

```c#:
	public class MainActivity : Xamarin.Forms.Platform.Android.FormsApplicationActivity
	{
		protected override void OnCreate(Bundle bundle)
		{
			base.OnCreate(bundle);
			Forms.Init(this, bundle);
			LoadApplication(new App());
		}
	}
```

### Windows Phoneプロジェクト

今回の手順ではWindows Phoneプロジェクトは作成されませんが一緒に解説します。

`MainPage`クラスを次のように修正します。

```c#:
	public partial class MainPage : Xamarin.Forms.Platform.WinPhone.FormsApplicationPage
	{
		public MainPage()
		{
			InitializeComponent();
			Forms.Init();
			LoadApplication(new App());
		}
	}
```

XAMLコードの修正も忘れずに。

```xml
<winPhone:FormsApplicationPage
	...
	xmlns:winPhone="clr-namespace:Xamarin.Forms.Platform.WinPhone;assembly=Xamarin.Forms.Platform.WP8"
	>
</winPhone:FormsApplicationPage>
```

<br>さあ、これでXamarin.Forms 1.3.0の新機能を使う準備が整いました。

## Styles を試す

PCLプロジェクトに新しいファイルを追加してください。ファイルの種類は「Forms」カテゴリにある「Forms ContentPage Xaml」です。ファイル名は`StylesPage`にします。

![新しいページを追加.jpg](https://qiita-image-store.s3.amazonaws.com/0/40121/0bc05e5b-5919-fd85-8118-597f97db5f64.jpeg "新しいページを追加")

### XAML

Pageのリソースディクショナリに`Style`を追加します、書き方はWPFとほぼ同じです。

```xml
<?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="XF13TPSample.StylesPage">
	<ContentPage.Resources>
		<ResourceDictionary>
			<Style x:Key="LabelStyle" TargetType="Label">
				<Setter Property="Label.TextColor" Value="White" />
				<Setter Property="Label.BackgroundColor" Value="Gray" />
				<Setter Property="HorizontalOptions" Value="Center" />
				<Setter Property="VerticalOptions" Value="Center" />
			</Style>
		</ResourceDictionary>
	</ContentPage.Resources>

	<ContentPage.Content>
		<Label
			Text="Hello Styles!"
			Style="{StaticResource LabelStyle}"
			/>
	</ContentPage.Content>
</ContentPage>
```

`x:Key`で名前を付けて`StaticResource`や`DynamicResource`として参照できるようにします。`Label`に適用したいので`TargetType`には`"Label"`を指定します。ちなみに`x:Key`のインテリセンスが効きません。
`Setter`で文字色、背景色、水平位置、垂直位置を指定しています。`Setter`のプロパティ指定は`クラス名.プロパティ名`とした方がスムーズに解決できるそうですが、`HorizontalOptions`のように省略しても反映されました。`Setter`が適用されない場合は省略せずに書いてみると良いかもしれません。
`Label`の方では`Style`プロパティでリソースディクショナリのStyleを`StaticResource`として参照しています。

今回はなるべく単純なサンプルにするため`Setter`のみとしましたが`Trigger`や`Behavior`を持たせることもできます。また、他の`Style`の内容を引き継ぐ`BasedOn`プロパティも利用できます。


### 実行結果

`App`クラスのコンストラクタ内を`MainPage = new StylesPage ();`と書き換えて実行するとこのような結果になります。Styleに記述されたプロパティ指定(文字色、背景色、水平位置、垂直位置)がきちんと反映されています。

![Styles実行結果_iOS.jpg](https://qiita-image-store.s3.amazonaws.com/0/40121/85a77eb6-4fe0-d351-3851-9700f5832d8d.jpeg "Styles実行結果_iOS")

### cs

XAMLのContentPageをC#で再現すると次のようになります。比べてみると **XAMLの方が断然分かりやすいですね！**

```c#:
			var style = new Style (typeof(Label));
			style.Setters.Add (new Setter{
				Property = Label.TextColorProperty,
				Value = Color.White
			});
			style.Setters.Add (new Setter{
				Property = Label.BackgroundColorProperty,
				Value = Color.Gray
			});
			style.Setters.Add (new Setter{
				Property = Label.HorizontalOptionsProperty,
				Value = LayoutOptions.Center
			});
			style.Setters.Add (new Setter{
				Property = Label.VerticalOptionsProperty,
				Value = LayoutOptions.Center
			});

			var label = new Label{
				Text = "Hello Styles!",
				Style = style
			};

			var page = new ContentPage
			{
				Content = label,
			};
```

## Triggers を試す

`Trigger`クラスを使ってみます。「-pre1」の時点では他に`EventTrigger`、`DataTrigger`、`MultiTrigger`があります。
Stylesのサンプルと同じように、PCLプロジェクトに新しいファイルを追加してください。ファイル名は`TriggersPage`にします。

### XAML

```xml
<?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="XF13TPSample.TriggersPage">
	<ContentPage.Content>
		<Switch
			HorizontalOptions="Center"
			VerticalOptions="Center"
			>
			<Switch.Triggers>
				<Trigger TargetType="Switch" Property="Switch.IsToggled" Value="true">
					<Setter Property="Switch.Rotation" Value="90"/>
				</Trigger>
			</Switch.Triggers>
		</Switch>
	</ContentPage.Content>
</ContentPage>
```

`Switch`クラス(≒トグルボタン)の`Triggers`プロパティの中で直接`Trigger`を定義しています。この`Trigger`は`Switch`の`IsToggled`プロパティが`true`の時に発動し、角度を90度に変更します。

※2015/2/1 追記 コメントでご指摘いただきました、Androidのテーマによっては`Switch`が表示されない場合があるようです。`HeightRequest`/`WidthRequest`を設定すると回避できるようです。

### 実行結果

`App`クラスのコンストラクタ内を`MainPage = new TriggersPage ();`と書き換えて実行するとこのような結果になります。スイッチがオンの時に向きが90度変わります。ぐりん

![TriggersSample.gif](https://qiita-image-store.s3.amazonaws.com/0/40121/44910c46-6941-25f5-4e2b-44afe093d7cd.gif "TriggersSample.gif")


### cs

こちらもXAMLをC#で再現すると次のようになります。(これ書いてて思いましたが`Switch`クラスってswitch-caseキーワードと被ってて使いづらいですね)

```c#:
			var s = new Switch {
				HorizontalOptions = LayoutOptions.Center,
				VerticalOptions = LayoutOptions.Center
			};

			var trigger = new Trigger (typeof(Switch)) {
				Property = Switch.IsToggledProperty,
				Value = true,
			};

			trigger.Setters.Add (new Setter {
				Property = Switch.RotationProperty,
				Value = 90.0
			});

			s.Triggers.Add (trigger);

			var page = new ContentPage {
				Content = s
			};
```

## まとめ

`Styles`導入によってXAMLの利用価値がグンと上がったように思います。個人的にはXAMLでアニメーションを定義できるようになったり、ResourceDictionaryを単独で定義できるようになるともっとステキだと思います。
という訳でみんなXamrinでもXAML書きましょう！

<br>この記事で作成したプロジェクトを[GitHub](https://github.com/P3PPP/XamarinForms_1_3_0_TP_Sample)に上げておきました。一から作るのが面倒な方はダウンロードして遊んでみください。

### 参考

[On Technology with Mitch Milam &raquo; 2014 &raquo; October &raquo; 09](http://www.infinite-x.net/2014/10/09/) (Xamarin.Forms.Application 対応手順)

[Xamarin.Forms 1.3.0 Technology Preview - Xamarin Forums](https://forums.xamarin.com/discussion/25612/xamarin-forms-1-3-0-technology-preview/p1)

[Xamarin.Forms 1.3.0 Pre-release 1 - Xamarin Forums](https://forums.xamarin.com/discussion/27528/xamarin-forms-1-3-0-pre-release-1/p1)