NavigationViewを導入する【C#/XAML】

NavigationViewは最近のアプリでよく見るスタイルのナビゲーションです。左側のナビゲーション・メニューでページを選ぶスタイルで、ナビゲーション・メニューのサイズはウィンドウ幅に応じてアダプティブに変化するのが特徴です。WinUI3では簡単に実装することができるので、ぜひ使ってみてアプリをワンランクアップさせましょう。

開発環境

  • .NET 6.0
  • WinUI 3.0 (Windows App SDK 1.0)

NavigationViewとはアプリの左側や上部にメニューが並んでいて、それを選ぶと表示される項目が変化するような構造のことです。メニューの大きさはアプリの表示サイズによって最適化されていて、ウィンドウが小さくなると自動的に隠れるようになっていて、メイン・コンテンツを妨げないようになっています。

例えばXAMLコントロールのカタログアプリであるXAML Controls GalleryがNavigationViewの代表的な例です。

XAML Controls Gallery

その他にもデザインはカスタマイズされていますが、Microsoft StoreもNavigationViewを用いていると思われます。

このようにNavigationViewは様々なアプリで頻用されているコントロールであり、これを活用することでアプリの見栄えをレベルアップさせることができます。

NavigationViewについてはこちらのドキュメントもご覧ください。

ナビゲーション・メニューの配置は左側と上部から選ぶことができ、それぞれの場合についてNavigationViewの各項目は次のように定義されています。

NavigationView の左側ペインの構造
https://docs.microsoft.com/ja-jp/windows/apps/design/controls/navigationview より抜粋
  1. メニュー ボタン
  2. ナビゲーション項目
  3. 区切り記号
  4. ヘッダー
  5. 検索ボックス
  6. 「設定」 ボタン
NavigationView の上部ペインの構造
https://docs.microsoft.com/ja-jp/windows/apps/design/controls/navigationview より抜粋
  1. ヘッダー
  2. ナビゲーション項目
  3. 区切り記号
  4. 検索ボックス
  5. 「設定」ボタン

まずはNavigationViewに最低限の機能を実装したアプリを作成してみましょう。

ここでは、左側のメニューに「再生」「保存」「更新」「ダウンロード」の4つのメニューを用意し、それぞれのメニューからPage1 / Page2 / Page3 / Page4に遷移できるようにしておきましょう。

MainWindow

MainWindow.xaml
<Window ...(省略)... >

    <NavigationView Header="ヘッダー" PaneTitle="タイトル" SelectionChanged="NavigationView_SelectionChanged">
        <NavigationView.MenuItems>
            <NavigationViewItem Icon="Play" Content="再生" Tag="SamplePage1"/>
            <NavigationViewItem Icon="Save" Content="保存" Tag="SamplePage2"/>
            <NavigationViewItem Icon="Refresh" Content="更新" Tag="SamplePage3"/>
            <NavigationViewItem Icon="Download" Content="ダウンロード" Tag="SamplePage4"/>
        </NavigationView.MenuItems>
        <Frame x:Name="contentFrame"/>
    </NavigationView>
    
</Window>
MainWindow.xaml.cs
public sealed partial class MainWindow : Window
{
    public MainWindow()
    {
        this.InitializeComponent();
    }

    private void NavigationView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
    {
        var selectedItem = (NavigationViewItem)args.SelectedItem;
        if ((string)selectedItem.Tag == "SamplePage1") contentFrame.Navigate(typeof (Page1));
        else if ((string)selectedItem.Tag == "SamplePage2") contentFrame.Navigate(typeof(Page2));
        else if ((string)selectedItem.Tag == "SamplePage3") contentFrame.Navigate(typeof(Page3));
        else if ((string)selectedItem.Tag == "SamplePage4") contentFrame.Navigate(typeof(Page4));
    }
}

NavigationViewMenuItemsプロパティにNavigationViewItemをコレクション構文で指定することで、メニューを追加することができます。 NavigationViewItemのTagプロパティに分かりやすい名前を付けておいて、どのメニューが選択されたかを受け取れるようにしておきます。メニューが選択されたときの動作はSelectionChangedイベントにNavigationView_SelectionChangedメソッドを登録しておきましょう。

なお、NavigationViewItemIconプロパティにSymbol列挙型の値を指定することで、あらかじめ定義されたアイコンを簡単に指定することができます。

遷移させるページは以下のようにPage1-Page4までを作成しておきます。

Page1

Page1.xaml
<Page ...(省略)... >

    <Grid>
        <TextBlock Text="ページ1"/>
    </Grid>
</Page>

Page2

Page2.xaml
<Page ...(省略)... >

    <Grid>
        <TextBlock Text="ページ2"/>
    </Grid>
</Page>

Page3

Page3.xaml
<Page ...(省略)... >

    <Grid>
        <TextBlock Text="ページ3"/>
    </Grid>
</Page>

Page4

Page4.xaml
<Page ...(省略)... >

    <Grid>
        <TextBlock Text="ページ4"/>
    </Grid>
</Page>

これにより以下のようなアプリができました。

メニューの表示位置を変える

NavigationViewのPaneDisplayModeプロパティでナビゲーション・メニューの表示位置を左側か上部かに指定することができます。

先ほどのサンプルアプリのナビゲーション・メニューを上部に表示してみましょう。

MainWindow

MainWindow.xaml
<Window ...(省略)... >

    <NavigationView Header="ヘッダー" PaneTitle="タイトル" PaneDisplayMode="Top"  SelectionChanged="NavigationView_SelectionChanged">
        <NavigationView.MenuItems>
            <NavigationViewItem Icon="Play" Content="再生" Tag="SamplePage1"/>
            <NavigationViewItem Icon="Save" Content="保存" Tag="SamplePage2"/>
            <NavigationViewItem Icon="Refresh" Content="更新" Tag="SamplePage3"/>
            <NavigationViewItem Icon="Download" Content="ダウンロード" Tag="SamplePage4"/>
        </NavigationView.MenuItems>
        <Frame x:Name="contentFrame"/>
    </NavigationView>
    
</Window>

なお、PaneDisplayModeプロパティで設定できる項目は以下の通りです。

  • Auto
  • Left
  • LeftCompact
  • LeftMinimal
  • Top

LeftCompactとLeftMinimalにすると以下のように表示されます。

LeftCompactLeftMinimal

なお、デフォルトではPaneDisplayModeプロパティはAutoに設定されており、アプリのウィンドウサイズを小さくすると自動的に「Left」→「LeftCompact」→「LeftMinimal」と変化します。この時表示モードが変わる境となるウィンドウ幅はそれぞれExpandedModeThresholdWidthプロパティとCompactModeThresholdWidthプロパティで指定されます。

メニューに階層構造を作る

今まではNavigationViewMenuItemsプロパティにNavigationViewItemを指定してきましたが、NavigationViewItem自体にもMenuItemsプロパティが用意されており、そこにさらにNavigationViewItemを指定することでメニューを階層構造にすることができます。

MainWindow

MainWindow.xaml
<Window ...(省略)... >

    <NavigationView Header="ヘッダー" PaneTitle="タイトル"  SelectionChanged="NavigationView_SelectionChanged">
        <NavigationView.MenuItems>
            <NavigationViewItem Icon="Play" Content="再生" Tag="SamplePage1"/>
            <NavigationViewItem Icon="Save" Content="保存" Tag="SamplePage2">
                <NavigationViewItem.MenuItems>
                    <NavigationViewItem Icon="Print" Content="印刷"/>
                    <NavigationViewItem Icon="Mail" Content="メール"/>
                </NavigationViewItem.MenuItems>
            </NavigationViewItem>
            <NavigationViewItem Icon="Refresh" Content="更新" Tag="SamplePage3"/>
            <NavigationViewItem Icon="Download" Content="ダウンロード" Tag="SamplePage4"/>
        </NavigationView.MenuItems>
        <Frame x:Name="contentFrame"/>
    </NavigationView>
    
</Window>

なお、ナビゲーション・メニュー内にラベルを付けて、メニューを整理させるためにはMenuItemsNavigationViewItemHeaderを配置させ、区切り線を入れるにはNavigationViewItemSeparatorを配置させます。

フッターメニューを配置する

NavigationViewのMenuItemsプロパティの代わりにFooterMenuItemsプロパティにNavigationViewItemをコレクション構文で指定することで、ナビゲーション・メニューの下部(Footer)にメニューを配置できます。なお、PaneDisplayModeがtopに設定されている場合は、ナビゲーション・メニューの右側に配置されます。

それでは、サンプルアプリのナビゲーション・メニューの下部に「メッセージ」「ユーザー」メニューを追加してみましょう。

MainWindow

MainWindow.xaml
<Window ...(省略)... >

    <NavigationView Header="ヘッダー" PaneTitle="タイトル" SelectionChanged="NavigationView_SelectionChanged">
        <NavigationView.MenuItems>
            <NavigationViewItem Icon="Play" Content="再生" Tag="SamplePage1"/>
            <NavigationViewItem Icon="Save" Content="保存" Tag="SamplePage2"/>
            <NavigationViewItem Icon="Refresh" Content="更新" Tag="SamplePage3"/>
            <NavigationViewItem Icon="Download" Content="ダウンロード" Tag="SamplePage4"/>
        </NavigationView.MenuItems>
        <NavigationView.FooterMenuItems>
            <NavigationViewItem Icon="Message" Content="メッセージ"/>
            <NavigationViewItem Icon="OtherUser" Content="ユーザー"/>
        </NavigationView.FooterMenuItems>
        <Frame x:Name="contentFrame"/>
    </NavigationView>
    
</Window>

これでナビゲーション・メニューの下部(Footer)に「メッセージ」「ユーザー」メニューを追加することができました。

PaneDisplayMode=”top”の場合は次のようになります。

「設定」ボタン・「戻る」ボタンの表示・非表示を切り替える

NavigationViewIsSettingsVisibleプロパティで設定ボタンの表示・非表示を、IsBackButtonVisibleプロパティで戻るボタンの表示・非表示を切り替えることができます。

設定ボタン・戻るボタン共に表示しないようにしてみましょう。

MainWindow

MainWindow.xaml
<Window ...(省略)... >

    <NavigationView Header="ヘッダー" PaneTitle="タイトル" SelectionChanged="NavigationView_SelectionChanged" IsSettingsVisible="False" IsBackButtonVisible="Collapsed">
        <NavigationView.MenuItems>
            <NavigationViewItem Icon="Play" Content="再生" Tag="SamplePage1"/>
            <NavigationViewItem Icon="Save" Content="保存" Tag="SamplePage2"/>
            <NavigationViewItem Icon="Refresh" Content="更新" Tag="SamplePage3"/>
            <NavigationViewItem Icon="Download" Content="ダウンロード" Tag="SamplePage4"/>
        </NavigationView.MenuItems>
        <Frame x:Name="contentFrame"/>
    </NavigationView>
    
</Window>

検索ボックスを組み込む

NavigationViewAutoSuggestBoxプロパティにAutoSuggestBoxを指定することで、ナビゲーション・メニューに検索ボックスを組み込むことができます。

MainWindow

MainWindow.xaml
<Window ...(省略)... >

    <NavigationView Header="ヘッダー" PaneTitle="タイトル" SelectionChanged="NavigationView_SelectionChanged">
        <NavigationView.AutoSuggestBox>
            <AutoSuggestBox QueryIcon="Find"/>
        </NavigationView.AutoSuggestBox>
        <NavigationView.MenuItems>
            <NavigationViewItem Icon="Play" Content="再生" Tag="SamplePage1"/>
            <NavigationViewItem Icon="Save" Content="保存" Tag="SamplePage2"/>
            <NavigationViewItem Icon="Refresh" Content="更新" Tag="SamplePage3"/>
            <NavigationViewItem Icon="Download" Content="ダウンロード" Tag="SamplePage4"/>
        </NavigationView.MenuItems>
        <Frame x:Name="contentFrame"/>
    </NavigationView>
    
</Window>

ナビゲーション領域に任意のコントロールを配置する

NavigationViewPaneCustomContentプロパティに任意のコントロールを配置させることが可能です。ここで配置させた要素はナビゲーション・メニューの一番上に配置されます。

例えば以下のようにしてボタンを配置させてみましょう。

MainWindow

MainWindow.xaml
<Window ...(省略)... >

    <NavigationView Header="ヘッダー" PaneTitle="タイトル" SelectionChanged="NavigationView_SelectionChanged">
        <NavigationView.PaneCustomContent>
            <StackPanel Orientation="Horizontal">
                <Button Content="ボタン1"/>
                <Button Content="ボタン2"/>
                <Button Content="ボタン3"/>
            </StackPanel>
        </NavigationView.PaneCustomContent>
        <NavigationView.MenuItems>
            <NavigationViewItem Icon="Play" Content="再生" Tag="SamplePage1"/>
            <NavigationViewItem Icon="Save" Content="保存" Tag="SamplePage2"/>
            <NavigationViewItem Icon="Refresh" Content="更新" Tag="SamplePage3"/>
            <NavigationViewItem Icon="Download" Content="ダウンロード" Tag="SamplePage4"/>
        </NavigationView.MenuItems>
        <Frame x:Name="contentFrame"/>
    </NavigationView>
    
</Window>

メニューから表示させるページを遷移させる

NavigatioViewではコンテントプロパティにFrameを組み込み、そのFrameの中でPageを遷移させます。Frameを用いたナビゲーションについては以下後ご覧ください。

NavigationViewItemが選択されると、NavigationViewに次のようなイベントが発生します。

現在選択されているアイテムと同じものが選択されたときに、SelectionChangedイベントは発生しませんが、ItemInvokedはイベント発生するという違いがあります。

ここではSelectionChangedイベントに処理を登録していきましょう。XAMLのコード・ビハインドに以下のようなNavigationView_SelectionChangedメソッドを作成し、これを登録します。(プログラム全体は最初のサンプルアプリをご参照ください)

MainWindow

MainWindow.xaml.cs
private void NavigationView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
{
    var selectedItem = (NavigationViewItem)args.SelectedItem;
    if ((string)selectedItem.Tag == "SamplePage1") contentFrame.Navigate(typeof(Page1));
    else if ((string)selectedItem.Tag == "SamplePage2") contentFrame.Navigate(typeof(Page2));
    else if ((string)selectedItem.Tag == "SamplePage3") contentFrame.Navigate(typeof(Page3));
    else if ((string)selectedItem.Tag == "SamplePage4") contentFrame.Navigate(typeof(Page4));
}

選択されたメニューはNavigationViewSelectionChangedEventArgsオブジェクトのSelectedItemプロパティで受け取ります。選択されたメニューのTagによって条件分岐し、FrameNavigateメソッドで指定のPageを表示させています。

「設定」ボタンで設定ページを表示させる

設定ボタンを選択しても発生するイベントは他のメニューと同じで、ItemInvokedイベントやSelectionChangedイベントとなります。SelectionChangedイベントの場合はNavigationViewSelectionChangedEventArgsオブジェクトが引数として渡されますが、そのオブジェクトのIsSettingsSelectedプロパティで「設定」ボタンが選択されているかどうかを取得することができます。

「設定」ボタンの処理を定義する際は、これを用いて「設定」ボタンが選択されたか否かを条件分岐しましょう。以下は「設定」ボタンを選択すると、あらかじめ作成しておいたSettingsPageが表示されるようにした書き換えたプログラムです。

MainWindow

MainWindow.xaml.cs
public sealed partial class MainWindow : Window
{

    ...(省略)...

        private void NavigationView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
        {
            if (args.IsSettingsSelected)
            {
                contentFrame.Navigate(typeof(SettingsPage));
            }
            else
            {
                var selectedItem = (NavigationViewItem)args.SelectedItem;
                if ((string)selectedItem.Tag == "SamplePage1") contentFrame.Navigate(typeof(Page1));
                else if ((string)selectedItem.Tag == "SamplePage2") contentFrame.Navigate(typeof(Page2));
                else if ((string)selectedItem.Tag == "SamplePage3") contentFrame.Navigate(typeof(Page3));
                else if ((string)selectedItem.Tag == "SamplePage4") contentFrame.Navigate(typeof(Page4));
            }
        }

    ...(省略)...

}

「戻る」ボタンの処理を定義する

NavigationViewの「戻る」ボタンはデフォルトでは表示はされていますが、クリックできない状態となっています。まずはIsBackEnabledプロパティをTrueに設定して有効化しましょう。「戻る」ボタンがクリックされるとBackRequestedイベントが発生するので、ここに処理を登録します。

以下の例ではNavigationView_BackRequestedメソッドを作成し、BackRequestedイベントに登録しています。実際の処理自体はFrameのナビゲーションを用います。

MainWindow

MainWindow.xaml
<Window ...(省略)... >

    <NavigationView Header="ヘッダー" PaneTitle="タイトル" SelectionChanged="NavigationView_SelectionChanged" IsBackEnabled="True" BackRequested="NavigationView_BackRequested">
        <NavigationView.MenuItems>
            <NavigationViewItem Icon="Play" Content="再生" Tag="SamplePage1"/>
            <NavigationViewItem Icon="Save" Content="保存" Tag="SamplePage2"/>
            <NavigationViewItem Icon="Refresh" Content="更新" Tag="SamplePage3"/>
            <NavigationViewItem Icon="Download" Content="ダウンロード" Tag="SamplePage4"/>
        </NavigationView.MenuItems>
        <Frame x:Name="contentFrame"/>
    </NavigationView>
    
</Window>
MainWindow.xaml.cs
public sealed partial class MainWindow : Window
{

    ...(省略)...

        private void NavigationView_BackRequested(NavigationView sender, NavigationViewBackRequestedEventArgs args)
        {
            if (contentFrame.CanGoBack)
            {
                contentFrame.GoBack();
            }
        }

    ...(省略)...

}

スポンサーリンク

“NavigationViewを導入する【C#/XAML】” への3件の返信

  1. 当方初心者です。C#の勉強の参考に利用させていただいております
    先日windows11PCを購入し、当ページのコードを用いて動かしてみたのですが、
    xamlで記載したボタンの日本語表示(content部)が文字化けしてしまう現象が発生しました。
    c#側でcontentを記載した場合は問題なく表示されます。

    など思いつく限りのことは試してみたのですが、xaml側で書いた日本語表示の文字化けを解消することができません。

    情報不足とは思いますが、解決策をご存じでいたらご教授いただけますと幸いです。

  2. お返事が遅くなり申し訳ありません。
    実はVisual Studioのアップデートをしたところこちらでも同じ現象が起きていて困っているところです(Visual Studio 2022 17.4.1)。
    おそらくデフォルトで生成されるXAMLファイルのエンコードが変わってしまったことが原因です。Visual Studioで以下のようにして当該のXAMLファイルのエンコードを変更することで文字化けは解決すると思います。
    https://teratail.com/questions/265233
    もしくは、当該のXAMLファイルをメモ帳などで開きエンコードをUTF-8などにしてもOKです。

    Visual Studioのデフォルトのエンコードを変える方法を存じ上げず、もしご存じの方がいたら教えてほしいのですが…
    (今回の件はVisual Studioのアップデートに伴う一時的なバグな気はしているので、そのうち修正されるとは思っているのですが)

    1. ご返信ありがとうございます。
      先ほど別方法で自己解決できました。

      コントロールパネル>日付、時刻、数値形式の変更>
      管理タブ>システムロケールの変更>「ベータ:ワールドワイド言語サポートでunicode UTF-8を使用」にチェック

      これで文字化けがなくなりました
      この設定にしておけば自PC内ではエンコード変更する必要はなくなるかと思います。

      (一安心してNavigationViewで遊んでいたらフッターに階層を作って表示をTOPでビルドすると
      フッターの階層が下方向に適切に開かないという現象が発生しててんやわんやしてます。仕様なのかバグなのか…)

      わざわざのご対応ありがとうございました。今後ともよろしくお願いいたします。

川上大樹 へ返信する コメントをキャンセル

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)