同じスレッドの別ウインドウを開く

もくじ
https://tera1707.com/entry/2022/02/06/144447#WinUI3

やりたいこと

WinUI3は、別ウインドウを開けない?みたいな話を聞いていたが、最近のWinUI3では開けるらしい。
WinUI3 Galleryのサンプルを見て、試してみる。

やりかた

WinUI3 Galleryのサンプルコードから、別ウインドウを開くためのヘルパーをそのままもらってきて、それを使ってウインドウを開く。

下記が、そのヘルパー。
https://github.com/microsoft/WinUI-Gallery/blob/main/WinUIGallery/Common/WindowHelper.cs

//*********************************************************
//
// Copyright (c) Microsoft. All rights reserved.
// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF
// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY
// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR
// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.
//
//*********************************************************

using Microsoft.UI.Xaml;
using System.Collections.Generic;

namespace AppUIBasics
{
    // Helper class to allow the app to find the Window that contains an
    // arbitrary UIElement (GetWindowForElement).  To do this, we keep track
    // of all active Windows.  The app code must call WindowHelper.CreateWindow
    // rather than "new Window" so we can keep track of all the relevant
    // windows.  In the future, we would like to support this in platform APIs.
    public class WindowHelper
    {
        static public Window CreateWindow()
        {
            Window newWindow = new Window();
            TrackWindow(newWindow);
            return newWindow;
        }

        static public void TrackWindow(Window window)
        {
            window.Closed += (sender,args) => {
                _activeWindows.Remove(window);
            };
            _activeWindows.Add(window);
        }

        static public Window GetWindowForElement(UIElement element)
        {
            if (element.XamlRoot != null)
            {
                foreach (Window window in _activeWindows)
                {
                    if (element.XamlRoot == window.Content.XamlRoot)
                    {
                        return window;
                    }
                }
            }
            return null;
        }

        static public List<Window> ActiveWindows { get { return _activeWindows; }}

        static private List<Window> _activeWindows = new List<Window>();
    }
}

で、それを使って、ウインドウを開く。

using AppUIBasics;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;

namespace PageTemplate
{
    public sealed partial class BlankPage1 : Page
    {
        public BlankPage1()
        {
            this.InitializeComponent();
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var newWindow = WindowHelper.CreateWindow();
            var rootPage = new BlankPage2();
            newWindow.Content = rootPage;
            newWindow.Activate();
        }
    }
}

やっていることは、

  • WindowHelper.CreateWindow()でWindowをnewして、
    (CreateWindowの中でnewしている)
  • そのWindowのContentに、ウインドウで表示したいPageを入れる。
  • 最後にActivate()し、ウインドウを表示する。

上のサンプルコードだと、BlankPage2というページを表示した状態で、別ウインドウを表示する、ということをする。
また、WinUI3Galleryのコードだと、テーマ(ダークとかライトとか)を設定してるが、今回は割愛した。

感想など

これから、もう少し便利になる?

WinUI-GalleryのWindowHelperのコードの中には、下記のように書いてある。(元は英語。google翻訳。)

アプリが任意のUIElement(GetWindowForElement)を含むウィンドウを見つけられるようにするヘルパークラス。これを行うために、すべてのアクティブなWindowsを追跡します。アプリコードは、関連するすべてのウィンドウを追跡できるように、「新しいウィンドウ」ではなくWindowHelper.CreateWindowを呼び出す必要があります。将来的には、プラットフォームAPIでこれをサポートしたいと考えています。

追跡したいケースというと、「親ウインドウを閉じたら、子ウインドウも全部閉じたい」みたいな時かな?と想像。そういうのも、将来もっと簡単便利にできるようになるっぽい。
(別に、今のhelperがあればそれでいいじゃないと思ってしまうが、便利になるのはありがたい)

同じUIスレッドで動く別ウインドウが実装されたのはWindowsAppSDKの1.0なのか、1.1なのか?

これが、どうしてももやもやしたまま残っていて、解決できる情報が見つけられなかった。

WinUI3のロードマップには、WinAppSDKの1.1での更新内容として、下記のように書いてある。

しかし、WinUI3 Galleryのアプリの「Windows > CreateMutipleWindows」の項目には、下記のように書いてある。

私には、WinUI3Galleryとロードマップで、WindowsAppSDKのどのVerで「同じUIスレッドで動く別ウインドウ」が実装されるか、について、食い違ったことを言っているように見える...
(1.0なのか?1.1なのか?)

ただ、上の実験コードのcsprojは、

<PackageReference Include="Microsoft.WindowsAppSDK" Version="1.0.0" />

となっておりSDKの1.0を使っているが、思った動作をしてくれているので「同じUIスレッドで動く別ウインドウ」が実現できているように見える。

つまり、SDK1.0で「同じUIスレッドで動く別ウインドウ」が動いているように見える。

だとしたら、ロードマップで言っている、SDK1.1で実装される「Apps can create multiple windows with WinUI3 content on the same UI thread.」とは、なんのことなのか?

WinUI3GalleryのtopページにはWinAppSDK1.1」とバーンと書かれてるが、

ウインドウのところは1.0のときのままだったのか?
(もしかして、全然別のものを私が勘違いしているだけ?)

ちょっと理解が追い付いてないので、詳しい方、教えて頂けたら嬉しいです...

追記

ロードマップを今一度よく見たら、「同じスレッドの、複数toplevelウインドウ」がWindowsAppSDK1.0.1以降でサポートとある。

で、「別れたスレッドの、複数toplevelウインドウ」が、「実験的な機能」として1.0で利用可能とある。

だから、上で使えたのは「同じスレッドの、複数toplevelウインドウ」で、それは1.0.1から使えていた。 「別れたスレッドの、複数toplevelウインドウ」が、1.1で使えるようになるということか。
(ただ、WinUI3ギャラリーにも、SDK1.1にも、別スレッドの複数ウインドウについての記載が見つけられないので、1.1では入らなかったのかもしれない。)

複数ウインドウを実現できる方法、どれが正しいのか...

複数ウインドウの作成、で検索してみると、下記のようなものがヒットする。

https://docs.microsoft.com/ja-jp/windows/apps/design/layout/app-window

https://docs.microsoft.com/ja-jp/windows/apps/design/layout/show-multiple-views

とりあえず、今時点(2022年6月)時点では、AppWindowを使った方法はPreview中だから使わないで、とあった。
他のは使えるのか、試してみてもよさそう。(まだ試してない)

(WinUI3の〇〇のやり方、で調べてると、なんとなく使えそうなやり方が出てくるが、それがUWPのやり方なのか、WPFなのか、WinUI2なのか3なのか、結局WinUI3で使えるのか、がいつも今一つぱっとわからない。。。)

参考

WinUI-Galleryのリポジトリ

https://github.com/microsoft/WinUI-Gallery

WinUI3のロードマップ

https://github.com/microsoft/microsoft-ui-xaml/blob/main/docs/roadmap.md

Windows App SDK のロードマップ

https://github.com/microsoft/WindowsAppSDK/blob/main/docs/roadmap.md

参考書

WinUI3

WinUI3でアプリを作ろうと思ったときのとっかかりによかった。 msdocsに書いてある情報を、体系的に、順番に読みたいな、というときによいかも。(ただし英語)
この本で分からなかった、かゆいところに手が届かなかった部分を私は記事にしてる感じ。

C#①

表紙に書いてある通り、教科書として最適。 これからC#を勉強したいけど、ネットだけで勉強するのは効率が悪いから体系的に学べる本が欲しいときや、 ちょっとC#を勉強してコード書けるようになったけど、もう少し広く深く知りたいなというときによいと思う。
私は仕事で触れるコードを軸に、基本ネットで断片的にC#を学んだので、その知識の隙間を埋めて枝葉を広げるためにとても分かりやすかった。

C#②

C#の文法的に色々できるのは分かったが、いざ実装するときに、わかったことを使ってどう実装すればいいのか?と悩んだときに指針になりそうな本。
「プロパティ等の名前の付け方、どうすればいい?」「情報をクラス外部に見せるときに、プロパティにすべき?メソッドにすべき?」「異常だと判定したいとき、どんなときにどんな例外をスローすべき?」などなど、勉強になる部分が山ほどあった。
私のように「コードは書くけどこれであってるのか自信がない、レビューで指摘されるのが嫌だ、実装時の(心の)よりどころが欲しい」という人に最適。