しばやん雑記

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

Windows App SDK 1.1 Preview 3 で追加された Desktop Acrylic と Mica のサポートを試した

何故か Windows App SDK を使っても、Windows 10 や 11 で追加された Desktop Acrylic や Mica といったウィンドウ効果は適用できませんでしたが、先日リリースされた 1.1 Preview 3 でようやく対応したようです。

Windows App SDK の開発はぶっちゃけ想像以上に遅いですが、進捗はあるようなので長い目で見ないとダメのようです。以下のドキュメントに Acrylic と Mica について説明はありますが、ポップアップメニューについては Acrylic は未対応な気がしています。

実際に Windows App SDK 1.1 Preview 3 を使って Desktop Acrylic と Mica のサポートを組み込んでみましたが、ドキュメントにあるサンプルが分かり難かったので、以下の PR を見るのをお勧めします。

アプリケーションに 1.1 Preview 3 を使って Desktop Acrylic と Mica のサポートを組み込むには以下の手順が必要になります。WindowAppWindow のプロパティを変更すれば良いといったレベルではないのが残念なポイントです。

  1. 現在のスレッドに DispatcherQueueController を作成
    • Windows.System.DispatcherQueue 向けなので名前空間には注意
  2. SystemBackdropController を作成
  3. 現在の Window に対して SystemBackdropController を適用
  4. Window がアクティブ化された時に SystemBackdropController に状態を通知する

正直なところ DispatcherQueueController の作成なんてユーザーコードでやることじゃないと思うのですが、こういうところで Windows App SDK の出来の悪さが露呈している感があります。

Desktop Acrylic と Mica を有効化するコードを追加

将来的には WindowAppWindow に組み込まれると信じていますが、現状は謎のコードを書く必要があるので抽象クラスを作って隠ぺいしてしまうのが正解でしょう。

適当に実装した抽象クラスが以下のようになりました。ここには WindowsSystemDispatcherQueueHelper の実装は含まれていませんが、特に変更点はないのでサンプルからコピペすれば問題ありません。

public abstract class SystemBackdropWindow : Window
{
    protected SystemBackdropWindow()
    {
        Activated += Window_Activated;
        Closed += Window_Closed;
    }

    private readonly WindowsSystemDispatcherQueueHelper _wsdqHelper = new();

    private SystemBackdropConfiguration _configurationSource;
    private ISystemBackdropControllerWithTargets _systemBackdropController;

    protected bool TrySetSystemBackdrop()
    {
        if (MicaController.IsSupported())
        {
            SetSystemBackdropController<MicaController>();

            return true;
        }

        if (DesktopAcrylicController.IsSupported())
        {
            SetSystemBackdropController<DesktopAcrylicController>();

            return true;
        }

        return false;
    }

    private void SetSystemBackdropController<TSystemBackdropController>() where TSystemBackdropController : ISystemBackdropControllerWithTargets, new()
    {
        _wsdqHelper.EnsureWindowsSystemDispatcherQueueController();

        _configurationSource = new SystemBackdropConfiguration
        {
            IsInputActive = true,
            Theme = ((FrameworkElement)Content).ActualTheme switch
            {
                ElementTheme.Dark => SystemBackdropTheme.Dark,
                ElementTheme.Light => SystemBackdropTheme.Light,
                ElementTheme.Default => SystemBackdropTheme.Default,
                _ => throw new ArgumentOutOfRangeException()
            }
        };

        _systemBackdropController = new TSystemBackdropController();

        _systemBackdropController.AddSystemBackdropTarget(this.As<ICompositionSupportsSystemBackdrop>());
        _systemBackdropController.SetSystemBackdropConfiguration(_configurationSource);
    }

    private void Window_Activated(object sender, WindowActivatedEventArgs args)
    {
        if (_configurationSource is not null)
        {
            _configurationSource.IsInputActive = args.WindowActivationState != WindowActivationState.Deactivated;
        }
    }

    private void Window_Closed(object sender, WindowEventArgs args)
    {
        if (_systemBackdropController is not null)
        {
            _systemBackdropController.Dispose();
            _systemBackdropController = null;
        }

        Activated -= Window_Activated;

        _configurationSource = null;
    }
}

Windows App SDK のテンプレートで生成された MainWindow の基底クラスを SystemBackdropWindow に変更して、コンストラクタで TrySetSystemBackdrop を呼び出せば Mica => Desktop Acrylic の優先度で有効化されます。これで Windows 11 では Mica が、Windows 10 では Desktop Acrylic が有効になるはずです。

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

        if (!TrySetSystemBackdrop())
        {
            // Cannot support for system backdrop
        }
    }

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

この実装では Windows 11 の場合は Mica が優先的に有効化されますが、パッと見で違いが分かり難いので Desktop Acrylic を有効化するように変更して実行したのが以下のスクリーンショットです。

ウィンドウがアクティブな場合のみ Acrylic が有効になります。非アクティブ化されるとグレーになります。

この状態で ExtendsContentIntoTitleBar を設定すると、以下のようにタイトルバー部分も Desktop Acrylic や Mica が適用されるようになります。

これで Terminal のようなタイトルバー部分も Acrylic なアプリケーションを簡単に作れるようになりました。

1.1 Preview 2 からマルチモニター環境での不具合あり

自宅のマルチモニター環境で Desktop Acrylic と Mica サポートを試していると、ウィンドウを別のモニターに移動した時に 100% クラッシュすることに気が付きました。

Windows App SDK の Issue を確認すると Preview 2 からの不具合らしく、モニターが縦置きになっている場合のみ発生するようです。今のところ修正時期は未定のようです。

現在の感触としては Windows App SDK 1.1 の正式版が出たタイミングなら、移行を検討しても良いかなと思っています。開発のペースが上がることを祈るばかりです。