Xamarin.FormsプロジェクトにMVVM Lightを導入する(必要クラスを自動生成してくれないため)

MVVM LightにはXamarin.Androidのテンプレートはあるけど、データバインディングが手書きの上コードビハインドだらけなので、Formsを利用したい。でも公式ページ通りに導入しても必要なLocatorクラスなどを自動生成してくれない。よって今回は手動で導入する手順をまとめました。

##1. NuGetからMVVM Lightをインストールする
まずはソリューションを右クリック、「**ソリューションのNuGetパッケージを管理**」を選択します。
![xamarinmvvmlight1.png](https://qiita-image-store.s3.amazonaws.com/0/68848/43d14bfe-4684-4c06-b769-0488031ca1a6.png)

「**mvvmlight**」で検索、全プロジェクトにチェックを入れてインストールします。
ここではAndroidしかないがiOS, UWP等ある場合も同様です。
![xamarinmvvmlight2.png](https://qiita-image-store.s3.amazonaws.com/0/68848/ac4096a6-c455-4849-6abb-f523bff46838.png)

規約承認などをして出力ウィンドウに===Finished===と出たら完了です。

##必要なファイルの作成
まずは**MainViewModel**から。ここではMVVM関係のファイルはMvvm名前空間に入れてあります。  
時刻を表示するためのサンプルです。

```c#:MainViewModel.cs
using System;
using System.Threading.Tasks;
using GalaSoft.MvvmLight;
using GalaSoft.MvvmLight.Threading;

namespace FormsMvvm.Mvvm
{
    public class MainViewModel : ViewModelBase
    {
        private bool _runClock;

        private string _clock;
        public string Clock
        {
            get
            {
                return _clock;
            }
            set
            {
                Set(ref _clock, value);
            }
        }


        public MainViewModel()
        {
            StartClock();
        }

        public void StartClock()
        {
            _runClock = true;

            Task.Run(
                async () =>
                {
                    while (_runClock)
                    {
                        DispatcherHelper.CheckBeginInvokeOnUI(
                            () =>
                            {
                                Clock = DateTime.Now.ToString("HH:mm:ss");
                            });

                        await Task.Delay(1000);
                    }
                });
        }

        public void StopClock()
        {
            _runClock = false;
        }
    }
}

```

次に**ViewModelLocator**。通常はコンストラクターでModelのなにやらを記述しますが、今回は省略。(今度書き足します)

```c#:ViewModelLocator.cs
using System.Diagnostics.CodeAnalysis;
using GalaSoft.MvvmLight.Ioc;
using CommonServiceLocator;

namespace FormsMvvm.Mvvm
{
    public class ViewModelLocator
    {
        [SuppressMessage("Microsoft.Performance",
            "CA1822:MarkMembersAsStatic",
            Justification = "This non-static member is needed for data binding purposes.")]
        public MainViewModel Main
        {
            get
            {
                return ServiceLocator.Current.GetInstance<MainViewModel>();
            }
        }

        public static bool UseDesignTimeData
        {
            get
            {
                return false;
            }
        }

        static ViewModelLocator()
        {
            ServiceLocator.SetLocatorProvider(() => SimpleIoc.Default);

            SimpleIoc.Default.Register<MainViewModel>();
        }

        public static void Cleanup()
        {
        }
    }
}
```

xaml上でViewModelLocatorを参照できるようにするために、**App.xaml**にViewModelLocatorを配置します。

```xml:App.xaml
<?xml version="1.0" encoding="utf-8" ?>
<Application xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="FormsMvvm.App"
             xmlns:vm="clr-namespace:FormsMvvm.Mvvm"> <!--ここと-->
	<Application.Resources>
        <vm:ViewModelLocator x:Key="Locator" /> <!--ここ-->
    </Application.Resources>
</Application>
```
最後にDispatcherHelperの初期化を**App.xaml.csのコンストラクタ**で行います。

```c#:App.xaml.csのコンストラクタ内に一行追加
DispatcherHelper.Initialize();
```
これにて導入完了です。

##データバインディングする

これでようやくxamlから簡単にデータバインディングできるようになりました。  
DataContextではなく**BindingContext**にLocatorをバインドします。そのためWPFと違いIntelliSenseでプロパティ名などを補完してくれません...

```xml: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:d="http://schemas.microsoft.com/expression/blend/2008"
             xmlns:local="clr-namespace:Exp_XamarinMvvmForms"
             xmlns:fab="clr-namespace:FuryTechs.FloatingActionButton;assembly=FuryTechs.FloatingActionButton"
             xmlns:androidWidget="clr-namespace:Android.Widget;assembly=Mono.Android;targetPlatform=Android"
             x:Class="Exp_XamarinMvvmForms.MainPage"

             BindingContext="{Binding Main, Source={StaticResource Locator}}"> <!--これを忘れずに-->
    <StackLayout>
        <Label Text="{Binding Clock}"
               FontSize="70" />
    </StackLayout>
</ContentPage>
```