Xamarin Studioベースで話を進めます。(Visual Studioでもだいたい同じになると思うけど)
まずForms Appを新規作成してPCLで作ります。
NuGetから以下のパッケージを追加します。
- Prism.Autofac.Forms
Views名前空間を作って、そこにMainPage.xamlを作成します。XAMLはいつも通り、ViewModelLocatorを設定しておきます。 今回は、ViewModelにMessageプロパティがあることを前提に作って見ました。あとでViewModelも作ります。
<?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:mvvm="clr-namespace:Prism.Mvvm;assembly=Prism.Forms" mvvm:ViewModelLocator.AutowireViewModel="true" x:Class="PrismAutofac.Views.MainPage"> <Label Text="{Binding Message}" HorizontalOptions="Center" VerticalOptions="Center" /> </ContentPage>
ViewModels名前空間を作って以下のようなViewModelも作ります。ここら辺は普通のPrismの世界ですね。
using System; using Prism.Mvvm; namespace PrismAutofac.ViewModels { public class MainPageViewModel : BindableBase { public string Message => "Hello world powered by Prism.Autofac.Forms"; } }
そして、App.xaml.csを以下の内容に書き換えます。
AutofacはUnityと違って登録されてないクラスのインスタンスは作ってくれないので、Unityでは必要のなかったViewModelの登録を行っています。と言ってもAssemblyをスキャンしてViewModelを一括登録する感じなので楽チンですけどね。
using Prism.Autofac; using Prism.Autofac.Forms; using Autofac; using Xamarin.Forms; using PrismAutofac.Views; using PrismAutofac.ViewModels; using System.Reflection; namespace PrismAutofac { public partial class App : PrismApplication { protected override void OnInitialized() { InitializeComponent(); this.NavigationService.NavigateAsync("MainPage"); } protected override void RegisterTypes() { // ViewModelの登録 var containerUpdater = new ContainerBuilder(); containerUpdater .RegisterAssemblyTypes(typeof(App).GetTypeInfo().Assembly) .Where(x => x.IsInNamespace("PrismAutofac.ViewModels")) .Where(x => x.Name.EndsWith("ViewModel")) .AsSelf(); containerUpdater.Update(this.Container); // Viewの登録 this.Container.RegisterTypeForNavigation<MainPage>(); } } }
追記 2017/01/09
nuitsさんから指摘いただきました。
@okazuki ViewModelの登録なんですが、こうすればいいようです。
— nuits.jp (@nuits_jp) 2017年1月8日
あと、この問題は次の6.3.0でフレーム側で対応されるようです。 https://t.co/833bZI2SUD pic.twitter.com/h2OFzeIHYz
ということなので、こういう感じでOKな上に、将来的にはこの手間もいらなくなるとか。
using Prism.Autofac; using Prism.Autofac.Forms; using Autofac; using Xamarin.Forms; using PrismAutofac.Views; using PrismAutofac.ViewModels; using System.Reflection; namespace PrismAutofac { public partial class App : PrismApplication { protected override void OnInitialized() { InitializeComponent(); this.NavigationService.NavigateAsync("MainPage"); } protected override void RegisterTypes() { // Viewの登録 this.Container.RegisterTypeForNavigation<MainPage>(); var containerUpdater = new ContainerBuilder(); // 登録されてない型もコンテナで作成する containerUpdater.RegisterSource(new Autofac.Features.ResolveAnything.AnyConcreteTypeNotAlreadyRegisteredSource()); // Serviceの登録 containerUpdater .RegisterAssemblyTypes(typeof(App).GetTypeInfo().Assembly) .Where(x => x.IsInNamespace("PrismAutofac.Services")) .Where(x => x.Name.EndsWith("Service")) .AsImplementedInterfaces() .SingleInstance(); containerUpdater.Update(this.Container); } } }
個人的に納得いかない挙動が1つあって、Viewの登録を最後に持ってくるとアプリが死ぬようになることですかね。
追記ここまで
あとは、PrismApplicationを継承するようにしてるのでApp.xamlの内容もそれに合わせて書き換えます。
<?xml version="1.0" encoding="utf-8"?> <prism:PrismApplication xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:prism="clr-namespace:Prism.Autofac;assembly=Prism.Autofac.Forms" x:Class="PrismAutofac.App"> <Application.Resources> <!-- Application resource dictionary --> </Application.Resources> </prism:PrismApplication>
実行すると、以下のようにいい感じに表示されます。
サービスとかの登録もしてみよう
サービスの登録もついでにして見ましょう。以下のようなIMessageServiceというインターフェースを定義します。
using System; namespace PrismAutofac.Services { public interface IMessageService { string GetMessage(); } }
実装も適当にやります。
using System; namespace PrismAutofac.Services { public class MessageService : IMessageService { public string GetMessage() => "Xamarin.Forms and Prism.Autofac.Forms."; } }
App.xaml.csでサービスの登録をシングルトンで行います。これもサービスが増えて来たときのことを考えてAssemblyをスキャンしてやってしまいましょう。
using Prism.Autofac; using Prism.Autofac.Forms; using Autofac; using Xamarin.Forms; using PrismAutofac.Views; using PrismAutofac.ViewModels; using System.Reflection; namespace PrismAutofac { public partial class App : PrismApplication { protected override void OnInitialized() { InitializeComponent(); this.NavigationService.NavigateAsync("MainPage"); } protected override void RegisterTypes() { // ViewModelの登録 var containerUpdater = new ContainerBuilder(); containerUpdater .RegisterAssemblyTypes(typeof(App).GetTypeInfo().Assembly) .Where(x => x.IsInNamespace("PrismAutofac.ViewModels")) .Where(x => x.Name.EndsWith("ViewModel")) .AsSelf(); // Serviceの登録 containerUpdater .RegisterAssemblyTypes(typeof(App).GetTypeInfo().Assembly) .Where(x => x.IsInNamespace("PrismAutofac.Services")) .Where(x => x.Name.EndsWith("Service")) .AsImplementedInterfaces() .SingleInstance(); containerUpdater.Update(this.Container); // Viewの登録 this.Container.RegisterTypeForNavigation<MainPage>(); } } }
MainPageViewModelを、IMessageServiceを使うように書き換えます。
using System; using Prism.Mvvm; using PrismAutofac.Services; namespace PrismAutofac.ViewModels { public class MainPageViewModel : BindableBase { private IMessageService MessageService { get; } public MainPageViewModel(IMessageService messageService) { this.MessageService = messageService; } public string Message => this.MessageService.GetMessage(); } }
実行すると、ちゃんとViewModelにMessageServiceがインジェクションされていることが確認できます。
まとめ
Unityがいなくなっても平気ではありそうだなぁと思った今日この頃でした。(Unityには頑張って欲しい)