https://xamgirl.com/prism-in-xamarin-forms-step-by-step-part-1
環境
- Windows 10 1909
- Visual Studio 2019 16.4
- Xamarin.Forms 4.4.0
- Prism.Unity.Forms 7.2.0
ブランクアプリの作成
まずはブランクXamarin.Formsプロジェクトを作成します。

インストール済みパッケージの更新
パッケージの更新があればやっておきます。
Prismのインストール
NuGetマネージャでPrismを検索します。

Prism関連のパッケージが表示されますが、この中からPrism.Unity.Formsを選択します。なぜかというと、これしか知らないからです。
Unityという文字が入っていますが、ゲーム開発などで有名なUnityではありません(お約束)。
App.xamlにPrismの参照を追加
<?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:d="http://xamarin.com/schemas/2014/forms/design" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:prism="clr-namespace:Prism.Unity;assembly=Prism.Unity.Forms" mc:Ignorable="d" x:Class="ApplyPrismToXamarinFormsFromScratch.App"> <Application.Resources> </Application.Resources> </prism:PrismApplication>
追加したのはこれ。
xmlns:prism="clr-namespace:Prism.Unity;assembly=Prism.Unity.Forms"
その上で、Application要素をprism:PrismApplicationに変更する。
App.xaml.csでPrismApplicationクラスを継承する必要はない
上記サイトだと、App.xaml.cs内のAppクラスがPrismApplicationクラスを継承するようにしているが、App.xamlの変更によって親クラスがPrismApplicationになっているので、ここで継承する必要はありません。
App.xaml.csのAppクラスの親クラスはApp.xamlのルートクラスになります。
これを確認してみます。
App.xaml.cs内のAppクラスにマウスオーバーして右クリックからGo to Definition(定義へ移動?)を選択すると、移動先の候補がおそらくVisual Studioのウィンドウ下部に表示されるので、App.xaml.g.csを選択してジャンプします。
この中でAppクラスに関する定義があるので見てみると、以下のようにPrismApplicationクラスを継承するようになっているのがわかります。
public partial class App : global::Prism.Unity.PrismApplication { [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Xamarin.Forms.Build.Tasks.XamlG", "2.0.0.0")] private void InitializeComponent() { global::Xamarin.Forms.Xaml.Extensions.LoadFromXaml(this, typeof(App)); } }
メソッドの実装
PrismApplicationを継承したことによって、現時点でApp.xaml.csのAppクラスには赤い波線がついて、OnInitializedとRegisterTypesメソッドを実装するように求められています。

この2つのメソッドをAppクラスに追加します。同時にもともと定義されていたメソッド群は削除します(下のコードではコメントアウトしてありますが)。
public partial class App { //public App() //{ // InitializeComponent(); // MainPage = new MainPage(); //} //protected override void OnStart() //{ // // Handle when your app starts //} //protected override void OnSleep() //{ // // Handle when your app sleeps //} //protected override void OnResume() //{ // // Handle when your app resumes //} public App(IPlatformInitializer initializer=null):base(initializer) { } protected override void RegisterTypes(IContainerRegistry containerRegistry) { //ここではDIコンテナへの登録を記述する } protected override void OnInitialized() { InitializeComponent(); } }
コンストラクタの追加とネイティブ側からの呼び出し部分の変更
Appクラスのコンストラクタを以下のようにします.
引数でIPlatformInitializerを受け取るようにします.
public App(IPlatformInitializer initializer=null):base(initializer) { }
IPlatformInitializerについては以下の記事が詳しいです.
プラットフォームごとのネイティブ側の機能をDIによって呼び分けるための仕組みです.OSごとに異なる処理をさせたい場合に使うようです.
コンストラクタのシグニチャの変更に伴い,コンストラクタを呼び出している各プラットフォーム側にも変更を加えます.
Android
MainActivity.csを開きます.
まずはAndroidInitializerという名前でIPatformInitializerを継承したクラスを作ります.これはMainActivityクラス内に追加します.
public class AndroidInitializer : IPlatformInitializer { public void RegisterTypes(IContainerRegistry containerRegistry) { //ここでコンテナに登録 //例↓ //var builder = new ContainerBuilder(); //builder.RegisterType<GreetingService>().As<IGreetingService>().SingleInstance(); //builder.Update(container); } }
次に,MainActivityクラスのOnCreateメソッド内でAppクラスをインスタンス化している部分で,コンストラクタに上記のAndroidInitializerクラスのインスタンスを渡すようにします.
protected override void OnCreate(Bundle savedInstanceState) { TabLayoutResource = Resource.Layout.Tabbar; ToolbarResource = Resource.Layout.Toolbar; base.OnCreate(savedInstanceState); Xamarin.Essentials.Platform.Init(this, savedInstanceState); global::Xamarin.Forms.Forms.Init(this, savedInstanceState); LoadApplication(new App(new AndroidInitializer()));// <- ここ }
iOS
AppDelegate.csファイルを開きます.
Android側と同じようにIPlatformInitializerを継承したクラスを作ります.
public class iOSInitializer : IPlatformInitializer { public void RegisterTypes(IContainerRegistry containerRegistry) { } }
AppDelegateクラス内のFinishedLaunchingメソッドでAppクラスのコンストラクタを呼び出しているので,iOSInitializerのインスタンスを渡すようにします.
public override bool FinishedLaunching(UIApplication app, NSDictionary options) { global::Xamarin.Forms.Forms.Init(); LoadApplication(new App(new iOSInitializer()));// <- ここ return base.FinishedLaunching(app, options); }
ここまでで,Prismの導入が完了です.
実装
Viewを作る
共有プロジェクト側にViewsフォルダを作成します.

その中に最初のViewとしてContentPageファイルを作成します.
名前はここではMainPageとします.
ViewModelを作る
同じく共有プロジェクト側にViewModelsフォルダを作成します.
その中に先程のMainPageと対になるViewModelとしてClassファイルを作成します.
名前はここではMainPageViewModelとします.
ViewとViewModelの名前の規則は大切で,PrismはView名に対して{View名}ViewModelであることを前提としてViewとViewModelを結びつけます.
この規則はViewModelLocatorを介して変更可能ですが,今回はデフォルトの規則を使用します.
Viewをコンテナに登録する
Viewは全て事前にコンテナに登録しておく必要がある.
App.xaml.csファイル内のAppクラスのRegisterTypesメソッドで登録を行う.
protected override void RegisterTypes(IContainerRegistry containerRegistry) { containerRegistry.RegisterForNavigation<MainPage>();//規則に従ってViewModelと結び付けられる containerRegistry.RegisterForNavigation<MainPage, MainPageViewModel>();//明示的に指定 }
RegisterForNavigationメソッドに対してViewのクラスだけ渡して,ViewModelとの結びつけはPrismに任せるか,ViewとViewModelの双方を渡して明示的に結びつけることができます.
これでViewとViewModelの間でデータバインディングが可能になります.
起動時に表示するページを指定
OnInitializedメソッド内でNavigationService.NavigateAsyncメソッドでViewを指定します.
指定の仕方はいくつもあります.
protected override void OnInitialized() { InitializeComponent(); NavigationService.NavigateAsync(nameof(MainPage)); }
ViewModelへの変更
各ViewのViewModelではページ遷移などを担うNavigationServiceをコンストラクタの引数として受け取ります.これはPrismによってDIされます.
public class MainPageViewModel { INavigationService _navigationService; public MainPageViewModel(INavigationService navigationService) { _navigationService = navigationService; } }
例えばボタンがクリックされたときにページ遷移する場合は,_navigationService.GoBackAsync()メソッドで遷移するのに使われます.
次に,ViewModelのクラスにBindableBaseクラスを継承させます.
このクラスを継承することによって,データバインディングをサポートする仕組みを使えるようになります.
private string _title="テスト2"; public string Title { get => _title; set => SetProperty(ref _title, value); }
また,ViewModelのクラスにINavigatedAwareを継承させます.
これを継承することで,ページ遷移する直前とページ遷移してきた直後に処理を行う以下の2つのメソッドが追加されます.
public void OnNavigatedFrom(INavigationParameters parameters) { } public void OnNavigatedTo(INavigationParameters parameters) { }