Windows App SDKでデスクトップアプリを作る

新しい開発環境であるWindows App SDKでデスクトップアプリを作ってみましょう。

でも、そもそもWindows App SDKとは何なのでしょうか。

![p1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/568364/4d6228d6-555d-22c6-8eab-bf90e3ce5f93.png)

マイクロソフトでは、Windowsでしか動作しない.NET Frameworkから、マルチプラットフォームの.NET Coreへの移行を進めていることは皆さんご存じでしょう。しかしデスクトップの開発環境を見ると、従来のWin Formsに加え、サンドボックス環境で動くUWPと、非サンドボックス環境で動くWPFがあります。UWPとWPFはどちらもXAMLでUIを定義する方式でありながら、その環境は分断されていました。

![p2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/568364/6b0cd815-ef5b-ab09-9b1b-fadfa8480e3c.png)

そこでマイクロソフトはこの分断を解消すべくプロジェクト・リユニオンを立ち上げ、コードの共通化を図っています。この開発環境を提供するのがWindows App SDKです。

# 開発環境のインストールとプロジェクトの作成

現時点ではPreview 3が最新版であり、まだ正式版ではありません。VisualStudio 2019にエクステンションを追加するか、Visual Studio 2022プレビュー版を使って開発します。この辺の設定の流れはどうせ今後変わりますので、詳しい説明は省略します。

https://docs.microsoft.com/en-us/windows/apps/windows-app-sdk/set-up-your-development-environment?tabs=vs%2Cstable

![2021-11-12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/568364/466a6594-7498-d118-20ef-72b9d5d856eb.png)

デスクトップのWin UI3を選びます。

ウィザードが自動生成したアプリを起動すると、ボタンが1つだけしかないシンプルなサンプルのアプリが起動します。

# サンプルプロジェクト

以下の2つのプロジェクトがサンプルとしてお勧めです。GitHubからクローンしておきましょう。

https://github.com/microsoft/Xaml-Controls-Gallery

XAML Controls GalleryはXAMLコントロールのカタログで、ラジオボタンやコンボボックスなど好みのコントロールを探して、その実装例をコピペできます。この記事を書いている時点では、winui3というブランチにWinUI3のサンプルが見つかります。

ビルド・実行できない人は、VisualStudioにUWP開発環境を追加したり、Dotnet Core SDKの最新版をインストールしたりしてみてください。この辺はいずれ、そのうちリリースされるVisualStudio 2022をインストールするだけで済むようになると思われます。

https://github.com/microsoft/WindowsAppSDK-Samples

WindowsAppSDK Samplesは文字通りWindows App SDKのサンプルコードです。

# Navigationを実装してみる。

![2021-11-13 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/568364/5e8a592c-8d10-ad85-f5c2-9b6213c334c1.png)

Windows 10以降のコントロールパネルみたいな、左にナビゲーションメニューがあるアプリを作ってみましょう。このあたりはまだ従来のWPFの開発と変わらないでしょう。すでにご存じの人には、おさらいということで…。

![2021-11-13 (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/568364/f1b43a5f-fe91-73b7-6989-c279eec6f7a0.png)

プロジェクトにサンプルページを追加。名前はSamplePage1としておきましょう。

``` xml:SamplePage1.xaml
<Page
    x:Class="Sample.SamplePage1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Sample"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Grid>
        <TextBlock FontSize="100" Text="Hello World!"/>
    </Grid>
</Page>
```

ナビゲーションメニューでタブが選択されたときに表示される中身の部分を作ります。

``` xml:MainWindow.xaml
<Window
    x:Class="Sample.MainWindow"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Sample"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

    <NavigationView
        SelectionChanged="NavigationView_SelectionChanged">
        <NavigationView.MenuItems>
            <NavigationViewItem Content="Menu Item1" Tag="SamplePage1" x:Name="SamplePage1Item" >
                <NavigationViewItem.Icon>
                    <SymbolIcon Symbol="Play"/>
                </NavigationViewItem.Icon>
            </NavigationViewItem>
        </NavigationView.MenuItems>

        <Frame x:Name="contentFrame"/>
    </NavigationView>

</Window>
```

WindowにNavigationViewを追加。NavigationViewItemのTagには、項目が選択されたときに生成されるページのクラス名を設定します。

``` cs:MainWindow.xaml.cs
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using System;

namespace Sample
{
    public sealed partial class MainWindow : Window
    {
        public MainWindow()
        {
            this.InitializeComponent();
        }

        public void NavigationView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
        {
            var selectedItem = args.SelectedItem as Microsoft.UI.Xaml.Controls.NavigationViewItem;
            contentFrame.Navigate(Type.GetType($"Sample.{selectedItem.Tag}"));
        }
    }
}
```

ナビが選択されたら、そのページを開く処理を追加します。タグ名からページのクラスのインスタンスを動的に生成しています。

つまり今後XAMLを修正してナビの項目が増えても、ここの部分はもう書き換えなくてよいのです。リフレクション万歳。

![2021-11-13 (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/568364/52235de7-112f-fb9c-d607-bdca0826f25d.png)

動いていますね。

左下の設定をクリックするとクラッシュします。設定メニューを追加してみましょう。

``` xml:Settings.xaml
<Page
    x:Class="Sample.Settings"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:Sample"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

    <Grid>
        <TextBlock FontSize="100" Text="To be available!"/>
    </Grid>
</Page>
```

Settingsの名前でページを作るだけです。…英語版のVisualStudioではそうだったのですが、日本語版だと「設定」という名前のクラスが作られようとしてしまいます。不必要なローカライズのせいで大迷惑です。

クラス名を「設定」にするという手もありますが、気持ちが悪いので、NavigationViewを修正します。

``` xml:MainWindow.xaml
<NavigationView
    SelectionChanged="NavigationView_SelectionChanged"
    Loaded="NavigationView_Loaded">
    <NavigationView.MenuItems>
        <NavigationViewItem Content="Menu Item1" Tag="SamplePage1" x:Name="SamplePage1Item" >
            <NavigationViewItem.Icon>
                <SymbolIcon Symbol="Play"/>
            </NavigationViewItem.Icon>
        </NavigationViewItem>
    </NavigationView.MenuItems>
    <Frame x:Name="contentFrame"/>
</NavigationView>
```

Loadedイベントを追加して…。

``` cs:MainWindow.xaml.cs
public void NavigationView_Loaded(object sender, RoutedEventArgs e)
{
    var navigationView = sender as NavigationView;
    var settings = navigationView.SettingsItem as NavigationViewItem;
    settings.Tag = "Settings";
}
```

タグをSettingsに書き換えてしまいます。

別の方法として、NavigationView_SelectionChangedでTagが「設定」だったらSample.Settingsを渡す、という手もあります。どちらにしても英語版だったらいらない苦労です。

# 次回予告

次回以降はWindows App SDK desktop固有のちょっとしたテクニックをいくつか共有していきたいと思います。