WinUI 3で複数画面の業務アプリを作るとき、最初につまずきやすいのが画面遷移です。ボタンのClickイベントでPageを切り替えるだけなら簡単ですが、画面が増えると「戻るボタンをどうするか」「同じ項目をもう一度押したらどうするか」「ウィンドウを狭くしたときメニューをどう見せるか」といった問題が一気に増えます。
そこで役立つのがNavigationViewとFrameの組み合わせです。NavigationViewはアプリのトップレベルナビゲーションを担当し、Frameは実際のPage遷移と履歴を担当します。今回は、この2つの役割を分けて「戻る」が自然に動く基本構成を作ります。
NavigationViewだけでは画面遷移は完成しない
NavigationViewは、左ペインや上部メニューを持つナビゲーションUIです。Microsoftの公式ガイドでは、画面サイズに適応しながら一貫したトップレベルナビゲーションを提供するコントロールとして説明されています。一方、NavigationViewItemを選択しただけでPageが自動的に切り替わるわけではありません。
ここでFrameを組み合わせます。FrameはPageを表示する入れ物で、Navigateによる遷移だけでなく、戻る履歴も管理します。イメージとしては、NavigationViewが「建物の案内板」、Frameが「実際に部屋を移動し、通った部屋を覚えている係」です。
まずはXAMLでナビゲーションの骨格を作る
次の例では、ホーム画面と設定画面の2項目を用意し、NavigationViewのContentとしてFrameを配置します。
<NavigationView x:Name="NavView"
IsBackButtonVisible="Auto"
IsBackEnabled="{x:Bind ContentFrame.CanGoBack, Mode=OneWay}"
ItemInvoked="NavView_ItemInvoked"
BackRequested="NavView_BackRequested">
<NavigationView.MenuItems>
<NavigationViewItem Content="ホーム"
Tag="home">
<NavigationViewItem.Icon>
<SymbolIcon Symbol="Home" />
</NavigationViewItem.Icon>
</NavigationViewItem>
<NavigationViewItem Content="レポート"
Tag="report" />
</NavigationView.MenuItems>
<Frame x:Name="ContentFrame" />
</NavigationView>
IsBackButtonVisible="Auto"にすると、NavigationViewが表示モードなどに応じて戻るボタンの見せ方を扱えます。IsBackEnabledにはFrameのCanGoBackを結び付け、実際に戻れる履歴があるときだけ戻る操作を有効にします。
選択された項目からPageへ遷移する
コードビハインドでは、NavigationViewItemのTagを画面識別子として使い、遷移先の型を決めます。
private void NavView_ItemInvoked(
NavigationView sender,
NavigationViewItemInvokedEventArgs args)
{
if (args.InvokedItemContainer is not NavigationViewItem item)
return;
Type? pageType = item.Tag switch
{
"home" => typeof(HomePage),
"report" => typeof(ReportPage),
_ => null
};
if (pageType is null)
return;
if (ContentFrame.CurrentSourcePageType != pageType)
{
ContentFrame.Navigate(pageType);
}
}
ここで大切なのは、CurrentSourcePageTypeを確認している点です。同じ「ホーム」を何度も押すたびにNavigateすると、同じPageが履歴へ積み重なります。その状態で戻ると「ホームからホームへ戻る」という不自然な履歴になります。同一ページへの不要なNavigateを避けるだけで、戻る履歴をかなりきれいに保てます。
戻る処理はFrameの履歴に任せる
NavigationViewの戻るボタンが押されたら、Frameが戻れることを確認してGoBackを呼びます。
private void NavView_BackRequested(
NavigationView sender,
NavigationViewBackRequestedEventArgs args)
{
if (ContentFrame.CanGoBack)
{
ContentFrame.GoBack();
}
}
自分で「直前のページ」を変数へ保存する方式でも2画面程度なら動きます。しかし、ホーム→レポート→詳細→設定のように遷移が増えると、履歴管理を自作するコードは複雑になります。FrameにはBackStackがあり、Navigateした履歴を持てるため、通常のページ遷移ではその仕組みを利用するほうが自然です。
NavigationViewとFrameの役割を混ぜない
初心者が混乱しやすいのは、「選択中のメニュー」と「現在表示しているPage」を同じものとして扱うことです。トップレベルのNavigationViewItemは大きなカテゴリを表しますが、Frameの履歴にはそのカテゴリ内の詳細Pageも入ることがあります。
たとえば「レポート」を選び、ReportPageからReportDetailPageへ進んだ場合、NavigationViewでは引き続き「レポート」を選択状態にしたいはずです。詳細PageをNavigationViewのMenuItemsへ追加する必要はありません。NavigationViewはアプリの主要な場所、Frameはその中を含む実際の移動履歴、と役割を分けると設計しやすくなります。
ウィンドウ幅への対応はNavigationViewに任せやすい
NavigationViewの強みは、単にメニューを表示できることではありません。PaneDisplayModeで左側や上部などのナビゲーション方式を選べ、既定のAdaptive動作では利用可能な幅に応じて表示状態を変えられます。Microsoftも、NavigationViewをさまざまな画面サイズへ適応するトップレベルナビゲーションとして位置付けています。
そのため、独自Gridで「幅が狭くなったらメニューを隠す」処理を一から組む前に、NavigationViewの表示モードで要件を満たせないか確認するとよいでしょう。業務アプリでも、ノートPCの狭いウィンドウと大型モニターの最大化表示の両方に対応しやすくなります。
実務では文字列Tagから一段進める
今回の例では分かりやすさを優先してTag="home"のような文字列を使いました。小規模アプリなら十分ですが、画面数が増えると文字列のタイプミスやswitch文の肥大化が起きます。
実務では、ナビゲーション先を表すモデルやサービスを用意し、Page型とメニュー項目の対応を一か所へ集約する方法を検討できます。さらにMVVMを採用するなら、画面遷移そのものをNavigationServiceとして分離すると、ViewModelがWindowやFrameへ直接依存しない構成にしやすくなります。
ただし、最初から抽象化を増やしすぎる必要はありません。まずNavigationViewとFrameの責務を理解し、画面数やテスト要件が増えてからサービスへ切り出すほうが、仕組みを理解しやすいでしょう。
注意点:Pageの状態と履歴は別問題
Frameが戻る履歴を持っていても、Pageインスタンスの状態が必ずそのまま残るとは限りません。フォームへの入力内容や検索条件など「戻ったときに復元したい状態」がある場合は、Pageのキャッシュ、ViewModel、アプリ側の状態管理などを別途設計する必要があります。
また、戻る操作にはNavigationViewのボタン以外にも、マウスの戻るボタンやキーボード操作などを考慮するアプリがあります。今回のコードはNavigationView上の基本的な戻る操作に絞った例であり、アプリ全体の入力方式を網羅したものではありません。
2026年時点でのWindows App SDKも確認する
WinUI 3はWindows App SDKの一部として提供されています。Microsoftの公式情報では、2026年9月時点でWindows App SDK 1.8系の安定版が公開されています。新規プロジェクトでは、利用するWindows App SDKとサポート期間を確認しながら開発してください。この記事のNavigationViewとFrameの基本パターンは特定の新機能だけに依存するものではありませんが、実際のプロジェクトでは参照しているSDKバージョンの公式ドキュメントも合わせて確認することが重要です。
まとめ
WinUI 3の複数画面アプリでは、NavigationViewを「トップレベルの案内」、Frameを「Page表示と履歴管理」として分けると構成が整理できます。NavigationViewItemからFrame.Navigateを呼び、戻るときはFrame.CanGoBackを確認してGoBackする、という流れが基本です。
さらに、同じPageへの重複Navigateを避ける、詳細PageまでNavigationViewへ並べない、Pageの状態保持とナビゲーション履歴を別の問題として考える。この3点を意識すると、画面が増えても「戻る」が壊れにくい設計になります。ナビゲーションは見た目のメニューではなく、ユーザーがアプリの中で迷わないための道筋です。最初に役割を整理しておくと、その後の画面追加もずっと楽になります。
参考資料
- Microsoft Learn – NavigationView
- Microsoft Learn – Structure a modern WinUI 3 desktop app
- Microsoft Learn – WinUI 3
- Microsoft Windows App SDK – Released Artifacts
コメント