sh1’s diary

プログラミング、読んだ本、資格試験、ゲームとか私を記録するところ

WinUI3 App.xaml (App.xaml.cs) をカスタマイズする

 

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

  • App.xaml
  • AppEx.cs (AppExBase.cs を継承)
  • AppExBase.cs

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:AppExx: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 は、カスタマイズしたい(主に初期化関係の)具体的な実装を施しています。CreateShellPrism のウィンドウの取得方法を真似して、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 のコードを参考にしました。

参考