昔からXAMLのMVVMライブラリで自分に合うものがなく、作ろうと何度かトライ(しては飽きた)していたのですが、とうとうまともに使えるレベルにまで持って行けたので、先日宣伝を垂れ流しました。
Epoxy 0.10.0をリリースしました。AvaloniaとUnoとWinUIに新たに対応してます https://t.co/1soUDgcRVn
— Kouji Matsui (@kekyo2) February 21, 2021
Epoxy は、.NETの様々なXAML環境で使うことが出来る、MVVM (Model-View-ViewModel)を楽に実装するための補助ライブラリです。特徴を抜粋すると:
- 非同期処理 (async-await) を安全に書くことが出来るように配慮しています。
- C# 8.0でサポートされた、null許容参照型を使えます。
- 小さなライブラリで、理解しやすいAPIです。
- プラットフォーム標準以外のフレームワークやライブラリに依存していません。
- 大げさにならない、最小の手間とコストで Model-View-ViewModel 設計を実現します。Viewにコードビハインドを書かずに済むことが着地点ですが、そのために煩雑な処理を記述しなければならなくなる事を避ける方針です。
- MVVMビギナーが躓きそうな部分に焦点を当てています。
- ほかのフレームワークライブラリ(例: ReactiveProperty)と組み合わせて使えるように、余計な操作や暗黙の前提を排除しています。
Epoxyの具体的な解説や動画などは、GitHubのREADMEに書いておいたので、そちらを参照してもらえればと思います(現在はまだありませんが、スタートガイドを書くか撮るかしようかなとは思っています)。フィードバックがあると嬉しいです。
(ここまで、前振りとEpoxyの宣伝)
今や、.NETでXAML環境と言うとかなり選択肢が増えていて、観測しているだけでも以下のようなものがあります:
- WPF (Windows Presentation Foundation)
- UWP (Universal Windows platform / 旧Windows Store App)
- Xamarin Forms
- Avalonia
- Uno platform
- WinUI 3
- MAUI
(多分、たまたまXamarin Forms公開直前に作ってプチバスってしまったジョークGtk XAML環境、のようなものなら、探せばいくらでも出てきそうです)
EpoxyをこれらのXAML環境で同じように使えるようにするため、移植を試みてみました。MAUIは理由があって取りやめたのですが、それ以外の環境では動作するようになりました。
この記事では、移植作業によって分かった、各XAML環境の現状の差異をまとめてみようと思います。対象読者は、現在のXAML環境の選択肢について知りたい人や、XAML環境の知識のアップデートをしたい人、各XAML環境の差異を知りたい人です。
但し、Epoxyの移植を下敷きにしているため、フレームワークアーキテクチャの情報に偏っていることに注意して下さい。一般的にXAMLに期待されることとして、リッチなユーザーインターフェイスの実現、という視点がありますが、そこはすっぽり抜けていると思います。
なお、この記事の情報は2021年2月時点のものです。
XAMLについて
そもそもXAMLとは、という話を一応しておきます。XAMLはユーザーインターフェイスの定義をXMLで記述するものです。生まれた頃(.NET Framework 3.0)は、丁度XMLがデータ記述形式として広く用いられるようになった頃で、Windows Formsの後継技術として生まれました。
Windows Formsは、画面上(Visual Studio)で、ユーザーインターフェイスのボタンやテキストボックスなどを視覚的に配置することが出来て、その結果(例えば座標とか色とか)が、C#のソースコードとして自動的に生成・変更され、ビルド時に一緒にコンパイルされることでGUIアプリケーションが完成する、というものでした。ここで:
- コントロールの合成に難がある。例えば、リストビュー内にボタンやドロップダウンリストを配置するとかは、難易度が高い
- 時々、自動編集されたソースコードが壊れる。逆に、手動で定義するのは現実的ではない
- すべてのコントロールがWindows HWNDベース(ウインドウを管理する最小単位)のためにパフォーマンスが悪い
と言う問題があり、後継のUIフレームワーク、つまりWPFを作ることになったのではないかと思います。
XMLで記述することで、1と2は解決(2の自動編集は、今はRoslynがあるので、難易度は高いがXMLでなくとも実現出来ると言えるかも)し、HWNDによる単一のウインドウ上で、WPFコントロールをすべて自前で描画することにより3を解決した(場合にとってはDirectXを使う)、という感じです。
以下にXAMLで記述したユーザーインターフェイスの例を示します:
<Window x:Class="EpoxyHello.Wpf.Views.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="EpoxyHello.Wpf" Height="450" Width="800"> <ListBox ItemsSource="{Binding Items}"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel Margin="5"> <Image Margin="5,3,5,0" Source="{Binding Image}" Stretch="UniformToFill" /> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Window>
この記事で挙げるXAML環境には:
- XMLの階層構造で、ユーザーインターフェイスの論理的な構造を表す
- コントロールの合成が可能。例えば(意味があるかどうかは別として)ボタンの表面に文字だけではなくテキストボックスを配置したりみたいな無茶も簡単にできる
- コントロールのプロパティがインスタンス基準のものの他に、定義を後付けすることが出来る「添付プロパティ」という概念がある
- 測定(Measure)と配置(Arrange)、と呼ばれる、ユーザーインターフェイス要素の配置位置を計算する、2段階のステップが組み込まれている
- データバインディングと呼ばれる、コントロールと対になる情報を提供するインスタンスを結びつける機能がある
と言うような、共通の特性があります。逆に言えば、これら以外の部分は、それぞれのXAML環境によってバラバラと言えます。
(余談ですが、ユーザーインターフェイス以外の環境にXAMLを適用した例がありますが、あまりにマイナーであり、Epoxyとは関係ないため、本記事では省きます)
WPF (Windows Presentation Foundation)
そもそものMVVMの始まりは、WPFからでした。それも(私見ですが)はじめからMVVMの概念があったわけではなく、WPFのデータバインディングの機能・構造を体系化するとこうなる、というような後付けのアーキテクチャ概念だったと思います。
(それが、MVVMをやってると歯がゆい事がある理由だと思っています)
歴史はともかく、MVVMの基本的な構造は殆どこのWPFを基準にしています。私も、WPFを触っていた期間は長いため、まずWPF向けにEpoxyを作って、そこから移植を開始しました。
WPFは3.0の頃から、ほとんど構造は変わっていません。現在は、.NET Framework 4.8が最終バージョンであり、このバージョンがそのまま動く(事になっている)のが、.NET Core 3.0で移植されたWPFです。当初、.NET Coreへの移植という事から、WPFがマルチプラットフォーム対応したのではないか?という誤解がありましたが、あくまで.NET Core 3.0 on Windowsのみがターゲットです。
「マルチプラットフォーム対応は無理だったけど、もうすぐ.NET Frameworkがカンストしちゃうので、何とかWindowsだけでも動くようにしたから、とりあえず.NET Coreに移行してね?」という意図が感じられました。そういうわけで、今でもWPFを選択するという事は、Windowsでしか動作しないGUIアプリケーションを作ることと同義になります。なお、.NET 5においても(恐らくは.NET 6においても)WPFはWindows上でのみ動作します。
(ダメ出ししておくと、WPFを他のプラットフォームに移植するには、C++/CLIで書かれたコードを移植しなければならず、そこを全部リライトするか、またはまずVisual C++をターゲットの環境向けに移植して公開するところから始める必要があるので、UWPとその後継にあれだけリソースをつぎ込んでいるのを見るに、今後も移植は無いだろうなと思います)
余談ですが、WPFは成功したのでしょうか?私のように、次世代UIとの売り言葉に乗って、早くからWPFを触ってきた技術者にとっては、とりあえず無くてはならないフレームワークになっていると思います。一方で、2020年越えの現在でも、Windows Formsを使い続けるユーザーは居て、WPFやXAML環境の何れかに乗り換えようという気配はあまりありません(Epoxyはそのようなユーザーの事も考えて作っています)。
個人的に思うのは、WPFは段階的に移行できるAPIや手段を用意しなかったのが痛いのと、設計概念の変更に踏み込んだのに、その事を詳しく解説せず、コントロールインターフェイスの表面的に似ているところばかり強調していた(個人の感想)ことが大きいです。これは、次節のUWPでも同じことを繰り返しているなと思います。おそらく、今Windows Formsに留まっているデベロッパーは、このままなすすべなく経過するか、どうせ同じ苦労を味わうなら、もっと別の選択肢を選ぼう(.NETに限らず。今はいろいろ選択肢がありますね?)、と思っているような気がします。
EpoxyははじめにWPFで作りましたが、同じXAMLのインフラ(構造)を使うのだから、他のプラットフォームへの移植はそこまで難しくないだろうとは思っていました。それは、部分的に正しく、部分的に間違ってもいました。以降では、各XAML環境の簡単な紹介と、差についてまとめます。
UWP (Universal Windows Platform)
UWPとその前身のWindows Store Appは、Windows 8でアプリケーションのモダン化を目指したものです。スマートフォンアプリケーションが、検証されて配布されることで一定の安心感を得られるのと同じように、MicrosoftがUWPで作られたアプリケーションを検証して公開することで、ユーザーが簡単に安全に利用できるようにしようとしました。
が、様々な理由から、UWPが市民権を得ているとは言い難い状況です。EpoxyをUWPに移植するにあたり、技術的には、以下のような課題がありました:
- UWPの構造が、WinMDというCOMをベースとしたメタデータインターフェイスで定義され、WPFの「ほぼ普通の.NETクラスベース」と異なる。具体例だが、WPFでは普通のメソッド実装だったのが、UWPで明示的インターフェイス実装として定義されていて、キャストしないとアクセス出来ない場面があった。他にも、C#が普通に使用できない情報が含まれている事がある(イベントのaddメソッドが値を返し、removeメソッドの引数が要求する型が異なるなんて想像したことがあるだろうか?)。Epoxyの移植作業以前にもちょくちょくこのような定義を見かけているので、割と沢山の落とし穴があるように思う。
- UIスレッドの考え方の違い。WPFではすべてメインスレッドからアクセスすることになっているが、UWPでは基準となるウインドウ毎に別のスレッドを使用することになっているため、今までのセオリーをそのまま適用すると問題が起こることがある(そのUIをホストしているDispatcherを使用する)。但しこれは、UIメッセージキューをウインドウ毎に分けたいという願望が実現されたとも言えるので、必ずしも悪い事ではないが、面食らうと思う。
- .NETメタデータアクセスに制約がある。特にリフレクションは、.NETの常識が通用しない場合がある。
もちろん、WPFのコントロールと、直接対応するUWPコントロールは、存在する場合もあれば存在しない場合もある(例: TextBoxクラスは双方に存在するが、DockPanelクラスに直接対応するものは無い)ので、XAMLの記述と言う点では、WPFとUWPに直接の互換性はありません。名前空間にも違いがあります。
.NETメタデータの操作については、リフレクションに関するクラスやメソッドは存在しますが、一部の使用には制約があります(コンパイル時に警告は出してくれる)。これは、UWPアプリケーションが、不正な動作を誘発させていないことを(Windowsストアの審査で)確認可能にするために行った仕様変更や制約です。
Epoxyの移植では、イベントをバインディングする機能の移植のところで、UWP専用のAPIを使用する必要がありました(しかも情報がほとんどなく、Rxのコードを参考にした)。個人的な意見としては、もっと段階的に移行可能な方法があったのではないかと思います。このような制約のために、リフレクションに限らず、例えばストリームI/O操作も.NETと異なる独自のクラスやインターフェイスが定義されているため、UWPを使う場合はそれらについて知っておく必要があります。
WPFと似たアーキテクチャのような体裁でありながら、XAMLとは無関係なところで、一般の.NET開発で常識と思われる知識が使えないことが多いと感じます。これらすべての欠点は、今俯瞰してみると、すべて「ネイティブコードへの変換時に、可能な限りの最適化を行う」ことを前提としているように思います。しかし、次節のXamarin Formsでも、iOSではネイティブコードへの変換(AOT)を行うわけで、効率に差はあるにしても、この欠点を受け入れてでも実現すべき事だったのかはわかりません。
私的には、もう少し冷静に判断して欲しかったところですが、スマートフォンとタブレットの迫る外圧に負けたのかもしれません…
Xamarin Forms
元々、Xamarin自体は、AndroidやiOSのアプリケーションをC#で記述可能で、ランタイムにmonoを使用する、という環境でした。ここには、AndroidとiOSを共通のコードで記述出来る、という意味は「含まれていません」。AndroidのアプリをC#で書ける、あるいはiOSのアプリをC#で書ける、という以上の意味は無く、それぞれの環境にあるAPIを使用します。
Xamarin Formsは、そこに共通のAPIをXAMLと共に定義し、可能な限り同一のコードで、AndroidやiOSのアプリを記述可能にしようとしたものです。例えば、ターゲットがAndroidやiOSのどちらであっても、共通のTextBoxクラスやButtonクラスを使います。
AndroidにはAndroidのスタートアップコードがあり、iOSにはiOSのスタートアップコードがあり、それらが共通のアプリケーションコードを呼び出す(共通コードでXAMLを定義し、Xamarin FormsのAPIを使う)、と言うような構造を採ります。
WPFからXAMLの歴史が始まっていることもあり、コントロールの扱いはWPFに似ていますが、現実的なスマートフォンアプリケーションを実装可能にするため、やはりWPFとは異なるコントロールは多いです。基本的にはUWPと同じように、ユーザーインターフェイス設計は、Xamarin Forms専用に考える必要があります。
Xamarin Formsを使う利点は、何といってもUWPのように独自の似て異なる.NET APIを使う必要が無いことです。例えば、ストリームI/OはStreamクラスを使えば良いし、リフレクションも基本的に.NETのそれと同一です。もちろん、iOSの場合はAOTコンパイルされるため、リフレクションAPIの使用には一定の制約があります(使うと実行時に死ぬという問題があります)が、APIが同一なので、.NETの一般的な知識がそのまま通用します。
Xamarinは、.NETでモバイル開発を行う選択肢としては、速い段階で実用レベルに達していたため、Xamarin Formsは実用可能な処理系として、急速に浸透しました。なので、商用サービスとしてのアプリケーション開発では、選択肢に加えることが出来そうなXAML環境の一つです。
なお、Xamarin FormsはUWPを実行環境とすることも出来ます。これは、ビルドした結果がUWPアプリケーションとして、ストアアプリ展開可能という意味です。もちろん、同じXAML環境であっても、UWPとXamarin FormsのXAMLには互換性は無く、この方法で実装する場合はあくまでXamarin Formsとして開発する事になります。Epoxyはもちろん、Xamarin Forms on UWPにも移植しています。
また、Xamarin FormsはGtkで動かすことも出来るため、macOSやLinuxのようなPosix互換環境(つまりはX Window System)をターゲットにすることも出来ます。但し、まだ標準的な手順でHello worldが動くというレベルに無いため、試してみるには追加の手作業が必要になります。別の例として、Unity(ゲームエンジン)にXamarin Formsを移植した例もあります。
Avalonia
Avaloniaは割と昔からあるXAML環境の一つで、最初からマルチプラットフォーム対応を謳っていました。Xamarin Forms同様に、WindowsだけではなくmacOSやPosix互換環境を選択可能で、開発時点ではまだ.NET Coreは無かったので、mono上で動作させることが前提でした。
現在は.NET Core/.NET 5環境でも動作させることが出来ます。XAML環境として見た場合、基本的なコントロールはサポートされており、私見ですが、リッチなユーザーインターフェイスでなければ、今現在マルチプラットフォームXAML環境としての完成度は高いように思います。XAMLはコントロールの合成が可能なので、この時点でも十分な能力を備えていると言えるのかも知れません。
その昔、かなり初期のバージョンで試したことがありましたが、その頃とは比較するまでもなく一発で動作し、不安定なそぶりはありませんでした。
Avaloniaの良い所は、WPFの構造上、あまりイケてない部分が改良されている事です:
- WPFでは、object型やDependencyObject型から型キャストを要求される場面が多いが、Avalonia APIはそういう所がきちんとジェネリック引数を要求するように変更されており、コンパイル時型チェックが機能する
- ほぼすべてのコントロールに、インターフェイス型が併設されている。このお陰で、インターフェイスを使った高度な抽象実装が可能だったり、独自のコントロールが基底クラスに縛られない
- ターゲットプラットフォームがnet461, netcoreapp2.0, netstandard2.0なので、非常にニュートラルであり、多くのライブラリに適合する
- C# 8.0のnull許容参照型が有効になっている(0.10.0現在)。ソースコードを参照すると、結構な勢いでC#の新しい構文で書き直されている。
全体的に自然な環境・綺麗な設計・実装で、アーキテクチャとして奇抜なところが無いので、.NETデベロッパは素直に扱えると思います。よく練られていて、WPFがこうだったら良かったのに、という感想です。一部のイベントハンドリングはRxを前提としていて、なるほどと思わせる部分があり、専用のReactiveUIライブラリを完備しています。また、現時点では、あまりトラブルを起こすことなく、LinuxやmacOS(注: macOSは未評価)で実際に動かせるというのも大きいでしょう。dotnet CLIにも対応しているので、すぐに始められます。
欠点もあります。それは、AvaloniaのXAML編集時に、インテリセンスが全く効かない事です(Visual Studio 2019上)。XAMLのインテリセンスは、この記事で紹介するすべての環境で満足行く状態ではない(C#インテリセンスとの比較)と考えていますが、Avaloniaはそもそも全くインテリセンスを使えないため、注意が必要です。恐らくauthorは問題を認識しているでしょうから、将来改善される見込みはあると思います。
また、マルチプラットフォーム対応として挙げているAndroidとiOSは、まだ追加の作業が必要なので、一般の開発者が扱える状態にはありません。この部分を見ると、Xamarin Formsとは真逆の状況にある印象です。
Avaloniaについて色々書きたいと思ったのですが、あまり書くことがありませんでした。その位良い実装だったという事です。Epoxyの移植時も、「そうだよ、それだよそれ!」って言いながらやっていたぐらいです。
(追記: なぜかAvalonia XAMLのインテリセンスが働くようになっていました。この記事を書いてから2~3日の話なので、何かアップデートがあったとかそう言う事ではない気がします。自席環境のせいかもしれません。XAMLインテリセンスは機能的に良くないと書いていますが、それでもあるのと無いのとでは雲泥の差です。もし、原因など分かれば、別記事で改めて紹介しようと思います)
Uno platform
Uno platformは最近出てきたXAML環境です。当然マルチプラットフォーム対応を謳っているのですが、wasm (Web Assembly、つまりはChromeなどのウェブブラウザ上で動作する)に対応しているなど、かなり尖っています。そのためか、少し前に大きな話題となりました(その頃は私事で忙しく、試している余裕が無かった)。
そのようなわけで、期待と共にEpoxyの移植に取り掛かったのですが、その内容は色々な面で驚きでした:
- UnoのAPIはUWP準拠。つまり、UWPの知識があれば、殆ど問題なくコードを書けることになる。UWPを下敷きにするのは、大胆と言うかびっくりですが、エコシステムを考えると、他のXAML環境のように一から設計するよりも良いのかも知れない。
- 大半のクラスに基底クラスが無い。え、そんな馬鹿な?UWPを下敷きにしているのでは?DependencyObjectがあるのでは?と思うが、何とDependencyObjectはクラスではなくインターフェイスとして定義されている。
- この時点で(既存のXAML環境の知識がある場合)、一体どうなっているのかわけがわからないと思うだろうが、何とDependencyObjectに相当する実装は、コンパイル時に自動生成されて(C#のpartialを使って)流し込まれる。
- 以上はUno on UWP以外の環境の話であり、Uno on UWPの場合は、大半の実装はUWPの実装をそのまま使う。つまりUnoがUWP準拠なのは、実際にUWPを使う場合はほぼそのまま、それ以外の環境では半自動生成された実装が流し込まれて、UWPのAPIにそれっぽく似せた状態にした上で、不足する実装を環境固有のライブラリで補う、と言う形式になっている
よくこんな事を思いついたなというのが感想で、アーキテクチャは間違いなく一番尖っています。メタプログラミングを駆使したXAML環境です。これを見たときには、半ばスルーしていたUWPを真面目に頑張ってみようかと思いました。
しかし、完成度がかなり残念な状態でした…
UnoをUWP環境で使う場合は、実際UWPのAPIをそのまま使うので、ちゃんと機能します。しかし、それ以外の環境では、エミュレーションライブラリに頼るわけですが、ここがまだまだこれからという状態です。例えば、UWP用ストリームI/O APIを使った以下のようなシンプルなコード:
static async ValueTask<ImageSource?> FetchImageAsync(Uri url) { // Redditから画像をダウンロードする (UWP) var raStream = new InMemoryRandomAccessStream(); await raStream.WriteAsync( (await Reddit.FetchImageAsync(url)).AsBuffer()); raStream.Seek(0); var bitmap = new BitmapImage(); await bitmap.SetSourceAsync(raStream); return bitmap; }
は、Uno on UWPでは動作しますが、それ以外ではNotImplementedExceptionとなります。また、XAMLを試行錯誤中、ビルド時に何度もMSBuildタスクがエラーになってビルドが失敗することがありました。そのため、Hello worldぐらいならwasmでも動作して「おお、ブラウザで動くXAMLアプリが作れるぞ?」と思いますが、その後が続きません。
Epoxyでは移植を行いましたが、このような理由でUno on UWPのみ検証しています。
斬新で、他のXAML環境とも全く異なるアーキテクチャのアプローチでしたが(嫌いじゃない)、まだまだ問題を自力で解決できる強い人向けという感じです。私的には、もう少し時間を置いてから評価したいと思います。実際に完成すれば、かなり良い選択肢になる可能性はあると思います。
WinUI 3
WinUIは現在バージョン3の評価中で、内容は見も蓋もありませんが、UWPの焼き直しです。実際に移植を行ってわかったのは、名前空間を変更するだけで殆どそのままUWPのコードを移植できる、という事です。また、新たに、普通のデスクトップアプリケーションとして振る舞わせる、つまり、一般的なウインドウを持つようなアプリケーションを開発できるようになったのが新しい(古い?)所です。
UWPやWinUI、そしてこの後に紹介するMAUIも含めて、直近の.NETユーザーインターフェイスフレームワークは混乱を極めていて、UWPからWinUIに移行するのも、私は本当のところの理由を正確に理解していません。UWPでは、GUIの部分(つまり今回記事にしているXAMLとその周り)と、Windowsに新たに追加された非ユーザーインターフェイスAPIを分離する、という話をチラ見した記憶があるため、その結果がWinUIなのかも知れません。
参照パッケージをUWPからWinUIに切り替えると、当然シンボル名などが未定義になったりします。そこで、名前空間をWinUIのものに合わせてやると解消します。例えば以下のような感じです:
#if WINDOWS_UWP || UNO using Windows.UI; using Windows.UI.Xaml.Media; #endif #if WINUI using Windows.UI; using Microsoft.UI.Xaml.Media; #endif // ... var brush = new SolidColorBrush(Color.FromArgb(...))
これはEpoxyの実装を一部抜き出したものですが、見てなにか気が付きますか? WinUIでは、一部の名前空間がUWPのままだったりします。これがユーザーインターフェイスとそれ以外のAPIの分離結果なのかも知れませんが、詳しく確認すると、SolidColorBrushは「Microsoft.UI.Xaml.Media」、Colorは「Windows.UI」に定義が存在します。もはや覚えられません…
勿論、C#であるならインテリセンスで自動補完が効くので、それほど問題ではないのでは?と思うかも知れませんが、Epoxyの移植時にはかなり混乱しました。
それでも、UWPのメンテナンスを行っている方は、WinUIへの移行に大きな負担は無いと思われます。同時に、デスクトップアプリケーションとしてもサポートされるので、今までスマートフォンやタブレットライクなアプリの挙動に躊躇していた方も使える環境と言えるかも知れません。
しかし、注意すべき点は、デスクトップアプリケーションとしてビルドした場合も、動作させるのにいわゆる「デプロイ」操作が必要で、そのためにパッケージングする必要もあります。ビルドすればEXEファイルが出来上がって、それをクリックすれば実行できる、とか、ファイルをzipで固めて別のマシンで展開して実行したい、のような事を考えていると、コレジャナイ感があると思います(つまり、xcopyデプロイは出来ません。WinUI3のissueとして数件挙がっていたので、やっぱりそうだよねと思った)。
そのような事もあり、実際にWinUIを直接使う必要性があまり感じられない(≒他のマルチプラットフォーム向けXAML環境を使ったほうが良いのでは?)と言う疑問を退けられませんでした。UWPのアップグレードと考えると、どうしてもUWPであるが故の制約(既に解説しました)が存在する事になります。デスクトップアプリケーションを開発すると考えた場合、これが制約に映ってしまいます。
今後もストアアプリでアプリケーションを展開したいと考えている場合は、有効な選択肢の一つでしょう。
MAUI
MAUIは次世代の.NETマルチプラットフォームユーザーインターフェイスフレームワークです。一方、Xamarin Formsの焼き直しという話をどこかで見ました。それだとちょっと見も蓋もないので、把握したことを書くと:
- .NET 6のリリースに合わせて作業中で、まだ公開されていない(?)
- Xamarin FormsはMAUIに置き換える予定で、とりあえず名前空間は変わります、という話は見た(アレ?これどこかで?)
- MVU (Model-View-Update)アーキテクチャに対応。Cometと呼ばれるプロジェクトで進行中
結局、まだ動くMAUIの実装は参照できないようなので、Epoxyの移植を試みるのは諦めました。そのため、MAUIでわかっているのは上記の事だけです。すいません。
TAN-Yさんが、MVUを試す記事を書いているのを見つけました。
MVUは、ユーザーインターフェイスをXMLではなくコードで記述します。アーキテクチャも異なりますが、XAML視点で見た場合、誤解を恐れずに書くならば、ある意味Windows Formsへの先祖返りのようにも思えます。最終的な仕上げには、Visual Studio上でのデザイン時編集を可能にするというゴールがあると思われ、今ならRoslynがあるので、それを実現する土台はあると言えると思います。また、XAMLのインテリセンスが散々であることからも、コードで書いたほうが精神的にも楽なのは想像出来ることです。
EpoxyがMVVMのためのライブラリなので、MVUやCometについて何か考慮することは無いと思います。
MAUI上でCometが実装されるより、CometのようなMVUフレームワークが、他の既存のXAML環境でも使えるようになると良いと思います。私がちょっと不安に思うのは、これでまたリタイヤしてしまうOSSが出るのではないかという事です。AvaloniaもUnoも良い挑戦だと思います。相互に発展するような方法で伸びることが出来ると良いのですが。
まとめ
ここまで読んでいただいた方なら、XAML環境の決定打は存在しない、という現実について大体わかっていただけたかなと思います。個人的には、できればデスクトップ開発はAvaloniaにシフト、モバイル開発はXamarin Formsという使い分けで行こうかと思っています。
デスクトップ開発は引き続きWPFを死に絶えるまで使うという選択肢はありだと思いますが、私の場合メインマシンをUbuntuに乗り換えてしまったこともあり… (EpoxyはUWPとかあるので、結局Windows上で開発していますが)
他にも、UWP系列を使わないのは、UWPの技術的な制約だけではなく、Microsoftの標準技術という位置づけでありながら、.NET側から見ると異端すぎること。そしてビルド環境が特殊なせいで、ビルドの再現性やCI/CDに馴染みにくい(イミュータビリティやポータビリティが殆どないので、自動化でいつも頭を悩ます)事を挙げておきます。WPFのビルド環境が.NET Coreで遜色ない方法でサポートされ、非常に親和性が高くなったので、余計にUWPの欠点が目立ちます。
このオチはある程度予想していたものですが、元々Epoxyが、複数の異なるXAML環境でも同じように使えるMVVMライブラリという落とし所を目指していたので、Epoxyの存在理由も含めて、丁度良かったかなと思っています。
(開発で使用するXAMLの環境を何にするのかは、それぞれの個人やお仕事上の事情があるでしょう。この記事は、いずれかのXAML環境への移行を推奨しているわけではありません。Epoxyの開発理由もそこにあります)
また、MAUIにはあまり過度の期待はしていませんが、一方でMVVMアーキテクチャがMVUに取って替わられる可能性は十分ありうるとも思います。Epoxyの開発は、MVVMでのあらゆる面倒くささが動機づけになっているので、それが解消する可能性があるならMVVMにこだわり続ける理由はありません。果たして実用に足りるのか?2022年には判明するかも知れないですね。