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


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

1. NuGetからMVVM Lightをインストールする

まずはソリューションを右クリック、「ソリューションのNuGetパッケージを管理」を選択します。

mvvmlight」で検索、全プロジェクトにチェックを入れてインストールします。
ここではAndroidしかないがiOS, UWP等ある場合も同様です。

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

必要なファイルの作成

まずはMainViewModelから。ここではMVVM関係のファイルはMvvm名前空間に入れてあります。

時刻を表示するためのサンプルです。

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のなにやらを記述しますが、今回は省略。(今度書き足します)

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を配置します。

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のコンストラクタで行います。

App.xaml.csのコンストラクタ内に一行追加
DispatcherHelper.Initialize();

これにて導入完了です。

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

これでようやくxamlから簡単にデータバインディングできるようになりました。

DataContextではなくBindingContextにLocatorをバインドします。そのためWPFと違いIntelliSenseでプロパティ名などを補完してくれません...

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>