Xamarin.FormsとPrismとWPFとStoryboard

この記事は [Xamarin Advent Calendar 2020](https://qiita.com/advent-calendar/2020/xamarin) の7日目の記事です。

# 登場人物たち

## Prism

いろいろ入ってるライブラリ集みたいな認識です。
個人的にはDI(Unity)とPubsubとViewModelシステムを使っています。
スタートアップは[この記事](https://blog.okazuki.jp/entry/2016/07/16/221431)で。

## WPF

いまさらWPFかい、とか言わないでください。
弊社ではWindows8.1が現役で使われてたりします。
（早く撲滅してください…）

## Storyboard

MacのStoryboardです。
ここんとこ書くなら毎回この記事です。

# 基本

皆さん、もう.NET5へ移行しますよね？してますよね？
自分はそちらで。
というわけで、テスト用のソリューションを作ります。

```shell
$ mkdir XamarinTest
$ cd XamarinTest
$ dotnet new sln
```

そして、共通で使うライブラリプロジェクトを作ってしまいましょう。

```shell
$ dotnet new classlib -n XamarinTestCommon
$ dotnet add XamarinTestCommon/XamarinTestCommon.csproj package Prism.Core
$ dotnet sln add XamarinTestCommon/XamarinTestCommon.csproj
```

何はなくとも[Prism](https://github.com/PrismLibrary/Prism)。
最近はXamarin.FormsよりPrism必須です。
あと、[Windows TerminalでGit Bash](https://qiita.com/yokra9/items/bdd0882268b308cf22ca)です。

# Xamarin.FormsとWPF

WPF用のXamarin.Formsは **.Net Framework用しかありません** 。
Xamarin.Formsの中で使われてるOpenGL用のライブラリ、[OpenTK.GLControl](https://www.nuget.org/packages/OpenTK.GLControl/)が.Net Framework用しかないからです。

つまり、 **.NET5を使うならXamarinは使わない** ということになります。

## プロジェクト作成

```shell
$ dotnet new wpf -n XamarinTest.wpf
$ dotnet add XamarinTest.wpf/XamarinTest.wpf.csproj reference XamarinTestCommon/XamarinTestCommon.csproj
$ dotnet add XamarinTest.wpf/XamarinTest.wpf.csproj package Prism.Unity
$ dotnet sln add XamarinTest.wpf/XamarinTest.wpf.csproj
```

何はなくともPrism。
そして使わないXamarin。

## WPF結論

**.NET5を念頭に置くならXamarin.Formsは入る余地なし** なのです。
Xamarin.FormsをWPFで使うなら.Net Frameworkを使いましょう。
（もしくは[MAUIを待つ](https://github.com/dotnet/maui/tree/main/src/Platform.Renderers/src/Xamarin.Forms.Platform.WPF)か、OpenTK.GLControlが.NET5に対応するのを待つか…）

# Xamarin.FormsとStoryboard

macOSでXamarin.Formsを使いつつ、Storyboardも使ってプログラムを作ります。

## プロジェクト作成

先ほどのソリューションを使いまわしましょう。

```shell
$ open XamarinTest.sln
```

何はなくともPrism。
Cocoaプロジェクトを作り、NuGetからPrism.Unity.Formsを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59270/5678e51d-3d61-7cff-4498-83120ed93c8e.png)

## Storyboardの設定を変更する

次にMain.storyboardをXcodeで開き、以下を設定します。
設定する項目は

- `Window Controller`の`is initial controller`のチェックを外す
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59270/fd3a57dc-2960-6980-d776-af31d50d0d16.png)

- `Window Controller`の`Storyboard ID`を設定する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/59270/bb92350f-7a23-ab5f-6fdc-5fdf109673b6.png)

です。

## AppDelegateを変更する

AppDelegateを以下に変更します。

```csharp
using AppKit;
using Foundation;
using Prism.Ioc;
using Prism.Unity;

namespace XamarinTest.mac
{
    [Register("AppDelegate")]
    public class AppDelegate : Xamarin.Forms.Platform.MacOS.FormsApplicationDelegate
    {
        // ** 1. **
        public override NSWindow MainWindow => MainWindowController.Window;
        public NSWindowController MainWindowController { get; }
        public CoreApp CoreApp { get; private set; }

        public AppDelegate()
        {
            // ** 2. **
            MainWindowController = NSStoryboard.MainStoryboard.InstantiateControllerWithIdentifier("MainWindowController") as NSWindowController;
            MainWindow.MakeKeyAndOrderFront(this);
        }

        public override void DidFinishLaunching(NSNotification notification)
        {
            // ** 3. **
            Xamarin.Forms.Forms.Init();
            LoadApplication((CoreApp = new CoreApp()));
        }
    }

    public class CoreApp : PrismApplication
    {
        protected override void OnInitialized()
        {
        }

        protected override void RegisterTypes(IContainerRegistry containerRegistry)
        {
        }
    }
}
```

やってることは、
1. Window関連のプロパティを設定する
2. Storyboardから`Window Controller`を読み出し、Windowを作成する
3. Xamarin.Formsを初期化する

`CoreApp`クラス内で`MainPage`を初期化していないので、Xamarin.FormsのPageは作られません。
その上でApplicationを`PrismApplication`で作ることでPrismを使用するようにします。

## Storyboard結論

こうなってしまうとViewModelは使えないので、Xamarin.Formsの利点がなくなり、Prismを使うためだけの土管になります。
すなおに`ViewController`を捨て、`CoreApp`コンストラクタ内で`MainPage`を設定しましょう。

# [MAUI](https://github.com/dotnet/maui)はよはよ！

使わなかったり土管だったりでPrism↑↑Xamarin.Forms↓↓な記事になってしまいましたが。
MAUIが出る頃には状況が変わるでしょうか。
RendererがXamarin.Forms.Platformだったりしてまだまだな感じのMAUIですが、このままフツーに乗り換えできそう。
楽しみです。