Xamarin.Forms でイベントに Command をバインドする

Xamarin が無償化されたので色々試しています。
まずは UWP の作りかけのアプリを Xamarin.Forms に移行しようとしたところ、最初からつまずいたのでその時の対応方法を残しておきます。

# UWP での処理内容
作成中の UWP アプリでは Page の Loaded イベントに Command をバインドしています。

UWPの場合は、xaml に以下のように記述することでバインドすることができます（以下の例は ReactiveProperty を使用しています。ReactiveProperty については [このあたり](http://blog.okazuki.jp/entry/2015/02/22/212827) を参照してください）。

    <i:Interaction.Behaviors>
        <core:EventTriggerBehavior EventName="Loaded">
            <react:EventToReactiveCommand Command="{x:Bind ViewModel.LoadedCommand}"/>
        </core:EventTriggerBehavior>
    </i:Interaction.Behaviors>

# Xamarin.Forms での対応方法
Xamarin.Forms ではまず Page クラスに Loaded イベントがありません。Appearing イベントがほぼ相当するイベントのようなのでこのイベントにバインドする事を考えました。

UWP と同じように Behavior を使ってバインドする方法を調べました（Xamarin.Forms.Behaviors という
ライブラリがあったりするので、、、）が、半日程度調べてもわからなかったのでいったんあきらめて別の方法を検討しました。

検索した結果、添付プロパティを作成すればバインドする事ができそうでしたので作成して確認しました。

## 添付プロパティクラス
添付プロパティを追加するために以下のようなクラスを作成しました。

```Cs:AttachedProp.cs
using System;
using System.Windows.Input;
using Xamarin.Forms;

namespace XFAttatchedPropSample.Views
{
	/// <summary>
	/// 添付プロパティクラス
	/// </summary>
	public class AttachedProp
	{
		/// <summary>
		/// Appearing コマンドプロパティ
		/// </summary>
		public static readonly BindableProperty AppearingCommandProperty =
			BindableProperty.CreateAttached(
				"AppearingCommand",                     // プロパティ名
				typeof(ICommand),                       // プロパティの型
				typeof(Page),                           // 添付対象の型
				null,
				BindingMode.OneWay,                     // デフォルト BindingMode
				null,
				OnAppearingCommandPropertyChanged,      // プロパティが変更された時に呼び出されるデリゲート
				null,
				null);

		/// <summary>
		/// Appearing コマンドプロパティの getter
		/// </summary>
		/// <param name="bindable">バインド対象オブジェクト</param>
		/// <returns>プロパティの値</returns>
		public static ICommand GetAppearingCommand(BindableObject bindable)
		{
			return (ICommand)bindable.GetValue(AttachedProp.AppearingCommandProperty);
		}

		/// <summary>
		/// Appearing コマンドプロパティの setter
		/// </summary>
		/// <param name="bindable">バインド対象オブジェクト</param>
		/// <param name="value">プロパティに設定する値</param>
		public static void SetAppearingCommand(BindableObject bindable, Command value)
		{
			bindable.SetValue(AttachedProp.AppearingCommandProperty, value);
		}

		/// <summary>
		/// Appearing コマンドプロパティ変更
		/// </summary>
		/// <param name="bindable">バインド対象オブジェクト</param>
		/// <param name="oldValue">変更前の値</param>
		/// <param name="newValue">変更後の値</param>
		public static void OnAppearingCommandPropertyChanged(BindableObject bindable, Object oldValue, Object newValue)
		{
			var page = bindable as Page;
			if (page == null)
			{
				return;
			}

			if (newValue != null)
			{
				page.Appearing += Page_Appearing;
			}
			else
			{
				page.Appearing -= Page_Appearing;
			}
		}

		/// <summary>
		/// Appearing イベント発生
		/// </summary>
		/// <param name="sender">送信元オブジェクト</param>
		/// <param name="e">イベント引数</param>
		private static void Page_Appearing(object sender, EventArgs e)
		{
			var command = GetAppearingCommand(sender as BindableObject);
			if (command != null)
			{
				if (command.CanExecute(e))
				{
					command.Execute(e);
				}
			}
		}
	}
}
```

AppearingCommand という名前で ICommand 型の添付プロパティを作成、プロパティに値が設定されたら Appearing イベントのハンドラを登録します。
イベントが発生したらプロパティに設定されている Command を呼び出すようにしています。

## MainPage と MainPageViewModel
MainPage を新規作成（Forms Xaml Page）し、対応する ViewModel クラスも作成します。

バインドするコマンドを持った ViewModel を以下のように作成しました。
コマンドが実行された事がわかるように、実行されたらメッセージ文字列プロパティに "Loaded" を設定するようにしています。

なお、コマンドやプロパティは ReactiveProperty を使うと簡単に作成できるので ReactiveProperty を使用しています（ReactiveProperty については、、、以下略 ）。

```cs:MainPageViewModel.cs
using Reactive.Bindings;
using System;

namespace XFAttatchedPropSample.ViewModels
{
	public class MainPageViewModel
	{
		public ReactiveProperty<String> Message { get; }

		public ReactiveCommand Loaded { get; }

		public MainPageViewModel()
		{
			Message = new ReactiveProperty<string>();

			Loaded = new ReactiveCommand();
			Loaded.Subscribe(_ =>
			{
				System.Diagnostics.Debug.WriteLine("Loaded");
				Message.Value = "Loaded";
			});
		}
	}
}
```

MainPage 側では ContentPage に作成した添付プロパティを記述してコマンドをバインドします。

             vw:AttachedProp.AppearingCommand="{Binding Loaded}"

コマンドが実行された事を示すメッセージを表示するための Label も配置して、以下のように作成しました。

```xaml:MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:vw="clr-namespace:XFAttatchedPropSample.Views;assembly=XFAttatchedPropSample"
             xmlns:vm="clr-namespace:XFAttatchedPropSample.ViewModels;assembly=XFAttatchedPropSample"
             x:Class="XFAttatchedPropSample.Views.MainPage"
             vw:AttachedProp.AppearingCommand="{Binding Loaded}"
             >

  <ContentPage.BindingContext>
    <vm:MainPageViewModel />
  </ContentPage.BindingContext>

  <Label Text="{Binding Message.Value}" FontSize="36" HorizontalOptions="Center" VerticalOptions="Center" />
</ContentPage>
```

最後に App クラスの コンストラクタを作成した MainPage を呼び出すように変更しました。

		public App()
		{
			MainPage = new Views.MainPage();
		}

## 実行結果
作成したアプリを実行すると、バインドしたコマンドが呼び出されメッセージが設定される事が確認できました。

Android での実行結果
![Android での実行結果](https://qiita-image-store.s3.amazonaws.com/0/122345/be1f488f-bfa5-b856-1442-acdb2b43334b.png)

UWP での実行結果
![UWP での実行結果](https://qiita-image-store.s3.amazonaws.com/0/122345/b121da00-3b54-65ad-c032-036b503e05b4.png)

※ Mac を持っていないため iOS での動作確認は行っていません。

# まとめ
添付プロパティを作成する事でイベントに Command をバインドする事ができました。
UWP と同じような方法については、今後も継続して調査したいと思います。

この記事に記述したソースは GitHub にプロジェクトを含めてあげてあります。
https://github.com/norimakixlvi/XFAttatchedPropSample

「01.Page.Appearing」が今回の記事に相当する物です。
「02.WebView.Navigated」は追加で WebView の Navigated イベント等にも対応してあります。

なお、今回作成したコードの確認は Xamarin.Forms v2.1.0.6529 で行いました。