Uno Platform のススメ

## はじめに

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

最近ちょっとずつ注目されつつある (?) "Uno Platform" という .NET 向けの UI フレームワークの紹介記事です。

## Uno Platform とは

* [Uno Platform](https://platform.uno/)

Uno Platform は .NET 向けのクロスプラットフォーム UI フレームワークです。 .NET のクロスプラットフォームフレームワークとしては "Xamarin.Forms" が Xamarin 自身からリリースされていることもあり、最も有名だとは思います。

Uno Platform の最大の特徴はなんといっても **UWP の API 互換によるクロスプラットフォームを実現する** という点に尽きます。ユーザー (アプリ開発者) としては UWP アプリを作れば (理想論でいえば) それをそのまま Android / iOS に持ってくる事が可能になるわけです。

UWP 互換を目指していることから、 **見栄えのカスタマイズ性が高い** 事も特徴として挙げられます。 UWP や WPF では従来の Win32 GDI の UI コンポーネントによるレンダリングをせずに自前のレンダラーでレンダリングをしていて、ボタンなどの見栄えは (標準スタイルはありますが) 全てアプリケーション開発者が独自に定義することが可能です。 Uno Platform でも UWP と同様なカスタマイズが可能となっています。

Xamarin.Forms は各コンポーネントはプラットフォームネイティブの UI コンポーネントで構成されるので、単一コードで各プラットフォームに沿った UI をクラスプラットフォームで構築しやすいというメリットがありますが、反面設定できる項目が少なくカスタマイズ性が低いということになります。これはどちらが優れているかという事ではなくフレームワークの性質ということだと思うので、ケースによって判断すればよいと思います。

(ちなみに Uno Platform は Native Style を指定する事でプラットフォームネイティブのコンポーネントを同一画面内で混在させて使用する事が可能となっています)

Windows は (今のところは) Uno ではなくそのまま UWP アプリとしてビルドすることで対応しています。 Windows 以外のバックエンドは Xamarin (Xamarin.Android, Xamarin.iOS など) が使われています。

## Uno Platform を試す

手っ取り早く試すには Visual Studio の拡張機能で Uno のプロジェクトテンプレートをインストールすることです。

Visual Studio の機能拡張から "Uno Platform Solution Templates" をインストールします。

![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211064/07f3d610-b97b-4ca5-a52f-9af4e0486b2c.png)

インストールするとプロジェクトテンプレートから Uno Platform が選べるようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211064/5399c38d-e4ed-8ff7-90ec-08317de5cd7c.png)


完了すると各プラットフォーム向けプロジェクトを含んだソリューションが生成されます。

## Uno Platform の実装を調べる

* [unoplatform/uno](https://github.com/unoplatform/uno)

チェックアウトしてきてビルドするとするソリューションに含まれるサンプルアプリが動くので、それで動作を確認します。

Xamarin.Forms では "抽象的な UI コンポーネントを定義したコアクラス群 (論理層)" と "プラットフォームに依存した具体的な実装を提供するレンダラー" に分かれていましたが、 Uno は UWP の API 丸ごと Bait and Switch で切り替えるような形のようです。本家 UWP のクラスがある意味 Xamarin.Forms の Core に相当するというイメージでしょうか。例えば UIElement は Android 用の UIElement クラスの実装が用意されています (厳密にはプリプロセッサ切り替えと partial class でソース自体はある程度共通化されている) 。

このような構造のため、 Xamarin.Forms と違って独自の Backend 実装をするのは結構ハードルが高そうです。 Xamarin.Forms の場合は抽象レイヤー (Core) とプラットフォーム実装が完全に分離されているので割と気軽 (?) に実装できます。

以降、 Android 版で実装と動作をみていきます。

次のような XAML を定義してみました。

```xml
<Page
  x:Class="App2.MainPage"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  xmlns:win="http://schemas.microsoft.com/winfx/2006/xaml/presentation">

  <Page.Resources>
    <win:Style x:Key="NativeDefaultButton" TargetType="Button" />
  </Page.Resources>
  
  <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
      <Button Width="240" Height="48">Button 1</Button>
      <Button Width="240" Height="48" Background="AliceBlue">Button 2</Button>
      <Button Width="240" Height="48" Style="{StaticResource NativeDefaultButton}">Button 3</Button>
      <Button Width="240" Height="48" Style="{StaticResource NativeDefaultButton}" Background="AliceBlue">Button 4</Button>
      <Button Width="240" Height="48">
        <Button.Template>
          <ControlTemplate>
            <Grid>
              <Ellipse Fill="LightGray"></Ellipse>
              <TextBlock
                HorizontalAlignment="Center"
                VerticalAlignment="Center"
                Text="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Content}" />
            </Grid>
          </ControlTemplate>  
        </Button.Template>
        <Button.Content>Button 5</Button.Content>
      </Button>
    </StackPanel>
  </Grid>
</Page>
```

Button を様々な見栄え設定で表示するようにしています。実行すると次のような表示になります。

| UWP | Android|
| --- | --- |
|![9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211064/84351839-dd3f-8e04-57d0-eadaacdb4986.png)|![8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211064/b2f2e34a-c467-3dc1-1af8-1ecac5897d09.png)|

特徴として

* Button 1, Button 2 は UWP (Uno) の標準デザインそのものです
* Button 3, Button 4 は **Native Style を適用することでプラットフォームネイティブのボタン** になっています
	* UWP はネイティブ = UWP なので結局同じになります
	* Android は見栄えを真似ているのではなく本当にすりかわっているのでネイティブそのものの挙動 (アニメーションとか) をします
* Button 5 は UWP 的に Template を適用して標準と違うデザインにしています

### Button クラスをみてみる

Button 自体は UWP の Button クラス (Windows.UI.Xaml.Controls.Button) です。イベントや論理的なプロパティはここに定義されています。

見栄えについては UWP の作法にのっとって ControlTemplate に指定されたテンプレートに沿って構築されます。 UWP の各コンポーネントは突き詰めると Android の View にそれぞれ変換されて表示されます。この観点からすると Xamarin.Forms と同じようにも思えますが、 Xamarin.Forms は Button をプラットフォームネイティブの Button として置き換えるので、論理的に高レイヤーでの対応になります。 Uno はレンダリング層で置き換えてるので低レイヤーでの置き換えていることになります。

Native Style についても ControlTemplate の置き換えで実現しているようです。これは Generic.Native.xaml というソースに Style のリソースが定義されていますが、

```xml
<android:Style x:Key="NativeDefaultButton"
				TargetType="Button">
	<Setter Property="Template">
		<Setter.Value>
			<ControlTemplate TargetType="Button">
				<BindableButtonEx Text="{TemplateBinding Content}"
									IsEnabled="{TemplateBinding IsEnabled}"
									Foreground="{TemplateBinding Foreground}"
									ForegroundColor="White"
									DisableWhenNoCommand="false" />
			</ControlTemplate>
		</Setter.Value>
	</Setter>
</android:Style>
```

BindableButtonEx は実体として Android.Support.V7.Widget.AppCompatButton そのものです。
つまり通常はパーツ単位で Android の View で入れ替えているのに対し、 Native Style を適用すると丸ごと Android のネイティブボタンに "View として" 置き換えているわけです。よくできています。
(これにより Native Style のボタンでは Background が変わっていないのもよくわかります。 Background が Binding されていません)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211064/75766ef7-81c9-4f68-2083-87ec7bc3ec21.png)

※全ての既定が Windows.UI.Xaml.UIElement の上の Uno.UI.UnoViewGroup クラス (Android の ViewGroup)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211064/b60d4176-7118-d616-d656-959a27b6573f.png)

## Uno Platform の展望

私が Uno に注目し始めた直接的な理由は UnoConf 2019 のライブを見た事からなのですが

* [UnoConf 2019 Recap (with Video)](https://platform.uno/unoconf-2019-recap-with-video/)

その中でも個人的に特に期待をしているのが Backend の追加、特に Skia Backend を挙げているところです (macOS は現在の master にも入ってきているようです) 。

* [Uno Platform Keynote: The Road Ahead - UnoConf 2019](https://www.youtube.com/watch?v=LacJhzVuCU4)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/211064/3269d81c-d496-7ea1-382e-7a314ec3150e.png)

(1:38:25 あたり)

Skia Backend となると、レンダリングが全て自前実装になるので、レンダリングがプラットフォーム非依存になります。するとプラットフォーム実装に関するレンダリング実装の割合がかなり削減される (Skia が対応していればよい) ことになるので Windows 7 など非 UWP の Windows での動作も期待できるのではないかと思います。 IME 対応が壁になりそうに思いますが・・・

## おわりに

Uno は WASM 対応が進んでいて、[UWP を通して Xamarin.Forms のバックエンドになったり](https://platform.uno/xamarin-forms/) して面白いこともやってるなあという感じです。積極的に情報発信もしていて、やっていこう感結構あるので新たな選択肢として期待したいです。

.NET で自前レンダラーのクラスプラットフォーム UI フレームワークとして "Avalonia" があります。

* [Avalonia](https://avaloniaui.net/)

Uno はどちらかというとモバイル寄りですが、 Avalonia はデスクトップ寄りでしょうか。こちらも見ていきたいところです。

Uno は今後次第ですが、自前レンダラーのクロスプラットフォーム UI フレームワーク、となるとどちらかが **C# の Flutter** になり得る可能性も 0 ではないかなと思いたいので要注目です。