App.xaml
は全体で使うリソースを宣言するファイルで、App.xaml.cs
は(主に)アプリケーションのエントリーポイント。
WPF でも WinUI 3 でもあるこのファイルですが、微妙に内容は異なっていて、Prism のようなライブラリでは App.xaml
をカスタマイズすることがあります。
App.xaml
をカスタマイズするメリットを挙げると、(Prism ライブラリは)App.xaml
を書き換えることで DI をサポートする、MVVM に対応するといった機能を提供するようになります。
また、WinUI 3 は、App クラスに(WPF でいう)OnExit
に対応するメソッドが無かったりするので、個人的にカスタマイズするメリットは(他プロジェクトと比較して)高くなってしまったように思いました。WPF で利用していたライブラリ自体がまだ対応してなかったりするためです。
独自に App.xaml
および App.xaml.cs
をカスタマイズしようとすると、どうやって記述するのだろう、といった内容をメモする記事です。
以下は、WinUI 3 のプロジェクトで実験していますが、WPF でも基本的な部分(考え方)は同じです。実験的なコードなので、改良する点は多いと思います。
ファイル構成(App.xaml)
AppEx AppExBase の2つのクラスを新たに用意して、AppEx クラスは AppExBase クラスを継承します。
App.xaml は以下のように修正しました。
<?xml version="1.0" encoding="utf-8"?> <local:AppEx x:Class="Sample.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:Sample"> <Application.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <XamlControlsResources xmlns="using:Microsoft.UI.Xaml.Controls" /> <!-- Other merged dictionaries here --> </ResourceDictionary.MergedDictionaries> <!-- Other app resources here --> </ResourceDictionary> </Application.Resources> </local:AppEx>
ルートのタグを local:AppEx
に変更しただけです。
local:AppEx
と x:Class
で指定している App.xaml.cs
が、コードビハインドで対応しています。(結びつきます)
AppExBase クラスの実装
using Microsoft.UI.Xaml; namespace Sample; public abstract class AppExBase : Application { public AppExBase() { } protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args) { OnInitialize(args); } protected abstract Window CreateShell(); protected abstract void OnInitialize(Microsoft.UI.Xaml.LaunchActivatedEventArgs args); protected abstract void OnExit(WindowEventArgs args); }
AppExBase は Application クラスを継承しているので、一般的な App.xaml.cs
とクラスの継承関係、OnLaunched
の実行(WPF では StartUp)といった処理の構成が、同じ形で整えています。
本来であれば、OnLaunched
でウィンドウを表示するのですが、具体的な実装は抽象メソッドに任せておきます。
AppEx の実装
using Microsoft.UI.Xaml; using System; namespace Sample; public abstract class AppEx : AppExBase { private bool _ContentLoaded; public AppEx() { InitializeComponents(); } private void InitializeComponents() { if (_ContentLoaded) { return; } _ContentLoaded = true; var resourceLocator = new System.Uri("ms-appx:///App.xaml", System.UriKind.Absolute); Microsoft.UI.Xaml.Application.LoadComponent(this, resourceLocator); } protected override void OnInitialize(Microsoft.UI.Xaml.LaunchActivatedEventArgs args) { var window = CreateShell(); if (window is null) { throw new NullReferenceException(nameof(window)); } window.Closed += (sender, wargs) => { OnExit(wargs); }; window.Activate(); } }
AppEx は、カスタマイズしたい(主に初期化関係の)具体的な実装を施しています。CreateShell
は Prism のウィンドウの取得方法を真似して、App.cs に具体的なウィンドウ取得の実装を任せます。
WinUI 3 は、App クラスに(WPF 等における)OnExit
に対応するメソッドが無いのですが、このように AppEx を介することで OnExit
メソッドを(簡易的に)作成することができます。一度このコードを用意しておけば、簡易ライブラリとして利用することができるはずです。
InitializeComponents メソッドは、WPF や WinUI 3 のデフォルトで実装される InitializeComponent メソッドと同じことをしています。WPF だと以下のようなコードが App.g.i.cs
などの自動生成させるコードファイルに用意されます。(一度確認してみるとよいと思います)
/// <summary> /// InitializeComponent (MainWindow で自動生成されている例) /// </summary> [System.Diagnostics.DebuggerNonUserCodeAttribute()] [System.CodeDom.Compiler.GeneratedCodeAttribute("PresentationBuildTasks", "7.0.12.0")] public void InitializeComponent() { if (_contentLoaded) { return; } _contentLoaded = true; System.Uri resourceLocater = new System.Uri("/Sample;V1.0.0.0;component/views/mainwindow.xaml", System.UriKind.Relative); #line 1 "..\..\..\..\Views\MainWindow.xaml" System.Windows.Application.LoadComponent(this, resourceLocater); #line default #line hidden }
App.xaml.cs の実装
通常でも(ほとんどの場合)コーディングすることになる App.xaml.cs
は、App をカスタマイズしてきたゴールになるコードブロックです。
以下のコード部分に、カスタマイズすることで得られたメリットがないとダメ。または、ここにメリットがあるように作っています。
using Microsoft.UI.Xaml; namespace Sample; public partial class App { private Window _Window; protected override Window CreateShell() { _Window = new MainWindow(); return _Window; } protected override void OnExit(WindowEventArgs args) { } }
こんな感じで App ファイルの中身をスッキリさせることができたり、OnExit
メソッドを用意したりできた。
Prism に似せたいのなら、次のようなメソッドを override できるようにすればいいと思う。
- RegisterTypes
- ConfigureModuleCatalog
プロジェクトに全体 DI であったり、MVVM の仕組みを用意するのは便利なので App の工夫は、(同様のライブラリを利用できないなどの理由があるなら)メリットがあるように思いました。
Prism の App のコードや PrismApplication のコードを参考にしました。