しばやん雑記

Azure とメイドさんが大好きなフリーランスのプログラマーのブログ

Windows App SDK 1.3 で System Backdrop の設定と AppWindow の利用が簡単になった

先日 Windows App SDK 1.3 がリリースされて、個人的に良いアップデートだと思ったのでブログに書くことにしました。アップデート内容としてはタイトルの通りで少ないですが、Windows App SDK に対する不満の一部が解消されて嬉しいです。

ぶっちゃけてしまうと最初から実装しておいてほしいレベルなのですが、贅沢は言わないことにします。

モダンな Windows 10/11 アプリケーションには Desktop Acrylic や Mica といったエフェクトが欲しいのですが、1.3 以前のバージョンでは SDK 側のサポートが非常に薄く、関係あるのかないのかわからない謎の Interop コードを追加する必要もあり不満の残るものでした。

その当時のコードが気になる方は以下のエントリを参照してください。意味不明なコードも出てきます。

Windows App SDK 1.3 ではそのあたりが SystemBackdrop というクラスで綺麗にラップされるようになったので、シンプルに Window クラスのプロパティで設定できるようになりました。

これからは以下のようなシンプルなコードで Desktop Acrylic や Mica を有効化できます。

public sealed partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        SystemBackdrop = new DesktopAcrylicBackdrop();
    }

    private void myButton_Click(object sender, RoutedEventArgs e)
    {
        myButton.Content = "Clicked";
    }
}

実行してみると Desktop Acrylic が有効化されていることが分かります。Mica の場合ぱっと見では分かりにくいので Desktop Acrylic を使っていますが、設定方法はほぼ同じです。

Windows App SDK は Windows 10 と 11 の両方に対応しているので、実際に利用する場合には以下のように Mica や Desktop Acrylic に対応しているか調べるようにした方が良さそうです。

public sealed partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        if (MicaController.IsSupported())
        {
            SystemBackdrop = new MicaBackdrop();
        }
        else if (DesktopAcrylicController.IsSupported())
        {
            SystemBackdrop = new DesktopAcrylicBackdrop();
        }
    }

    private void myButton_Click(object sender, RoutedEventArgs e)
    {
        myButton.Content = "Clicked";
    }
}

このように書いておけば Mica に非対応な Windows 10 の場合は Desktop Acrylic で表示されるはずです。これまでと比較すると劇的にシンプルなコードで実現できるようになりました。

Windows App SDK を使う上で不満が大きかったのが AppWindow 周りでした。Window クラスには有用なプロパティがほぼ存在しないので、実際にウィンドウの操作を行うには AppWindow を取得して触る必要があるのですが、その AppWindow のインスタンスを取得するのに手間がかかっていました。

これまでは以下の公式ドキュメントにもある通り、わざわざ Interop コードを書いて HWND から取得する必要があるという、本気で意味が分からないコードを強要されていました。

実際に自分がイラっとした時の例を挙げると、タイトルバーに表示されているアイコンを非表示にしたい場合には、以下のように AppWindow を取得するための謎のコードを書く必要がありました。

public sealed partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        // Retrieve the window handle (HWND) of the current (XAML) WinUI 3 window.
        var hWnd = WinRT.Interop.WindowNative.GetWindowHandle(this);

        // Retrieve the WindowId that corresponds to hWnd.
        var windowId = Microsoft.UI.Win32Interop.GetWindowIdFromWindow(hWnd);

        // Lastly, retrieve the AppWindow for the current (XAML) WinUI 3 window.
        var appWindow = AppWindow.GetFromWindowId(windowId);

        if (appWindow != null)
        {
            appWindow.TitleBar.IconShowOptions = IconShowOptions.HideIconAndSystemMenu;
        }
    }

    private void myButton_Click(object sender, RoutedEventArgs e)
    {
        myButton.Content = "Clicked";
    }
}

それぞれのクラスの名前空間がすべて異なるというのも、一貫性が皆無でイラっとするポイントです。

しかし Windows App SDK 1.3 からは Window クラスに AppWindow プロパティが追加されたので、先ほどと同じコードは以下のように 1 行で完結出来るようになります。

public sealed partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        AppWindow.TitleBar.IconShowOptions = IconShowOptions.HideIconAndSystemMenu;
    }

    private void myButton_Click(object sender, RoutedEventArgs e)
    {
        myButton.Content = "Clicked";
    }
}

ウィンドウの表示や非表示といった最低限必要なメソッドは全て AppWindow 側に存在しているので、これまで面倒だった処理も比較的簡単に実現できるようになりました。

正直なところ Windows App SDK 1.0 の時点で対応しておいて欲しい改善しかなかったのですが、最近の Windows App SDK のアップデートの中では一番嬉しい更新でした。