起動時にNavigationViewItemを選択しておく/コードから選択を解除する

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

やりたいこと

なにもしないと、NavigationViewの表示直後は、どの項目(NavigationViewItem)も選んでいない状態で表示される。

こんな感じ。

しかし私の場合は、まずどれかの画面(Page)を開いた状態でアプリ起動してほしいことがおおい。

こんな感じ。

なので、Pageを表示した状態で起動させる方法を調べたメモ。

サンプルコード

下記のようなコードを書いて、実験した。

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

    <Grid>
        <NavigationView x:Name="navi" PaneDisplayMode="Top" IsSettingsVisible="False"
                        SelectionChanged="nvSample_SelectionChanged" Loaded="nvSample_Loaded">
            <NavigationView.MenuItems>
                <NavigationViewItem x:Name="vi1" Tag="MainPage" Icon="Emoji2" Content="メインページ"  />
                <NavigationViewItem x:Name="vi2" Tag="SubPage"  Icon="Save" Content="サブページ" />
            </NavigationView.MenuItems>

            <Frame x:Name="contentFrame"/>

        </NavigationView>

        <Button Content="選択解除" Click="Button_Click"/>
    </Grid>
</Window>
namespace WinUI3PackageProjectTemplate
{
    /// <summary>
    /// An empty window that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainWindow : Window
    {
        public MainWindow()
        {
            this.InitializeComponent();
        }

        private void nvSample_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
        {
            if (args.SelectedItemContainer != null)
            {
                if ((string)args.SelectedItemContainer?.Tag == "MainPage")
                {
                    contentFrame.Navigate(typeof(MainPage), null, new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromRight });
                }
                else if ((string)args.SelectedItemContainer?.Tag == "SubPage")
                {
                    contentFrame.Navigate(typeof(SubPage), null, new SlideNavigationTransitionInfo() { Effect = SlideNavigationTransitionEffect.FromLeft });
                }
            }
        }

        private void nvSample_Loaded(object sender, RoutedEventArgs e)
        {
            // ①Navigateメソッドを使う
            // 青線:出ない 画面:出る
            contentFrame.Navigate(typeof(MainPage), null);

            // ②NavigationViewItemのIsSelectedをtrueにする(NavigationViewItemに名前つけるパターン)
            // 青線:出る 画面:出る
            vi1.IsSelected = true;

            // ③NavigationViewItemのIsSelectedをtrueにする(NavigationViewから探すパターン)
            // 青線:出る 画面:出る
            navi.MenuItems.OfType<NavigationViewItem>().ToList().First().IsSelected = true;

            // ④NavigationViewのSelectedItemプロパティにNavigtionViewItemをセットする
            // 青線:出る 画面:出る
            navi.SelectedItem = vi1;//但し、画面は表示されるが、NavigationViewItemの下に選択されていることを示す青線がでない
        }

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            navi.MenuItems.OfType<NavigationViewItem>().ToList().ForEach(x => x.IsSelected = false);
            navi.SelectedItem = null;
        }
    }
}

起動時にNavigationViewItemを選択しておく方法

調べた限り、下記の4種類のやり方があった。

private void nvSample_Loaded(object sender, RoutedEventArgs e)
{
    // ①Navigateメソッドを使う
    // 青線:出ない 画面:出る
    contentFrame.Navigate(typeof(MainPage), null);

    // ②NavigationViewItemのIsSelectedをtrueにする(NavigationViewItemに名前つけるパターン)
    // 青線:出る 画面:出る
    vi1.IsSelected = true;

    // ③NavigationViewItemのIsSelectedをtrueにする(NavigationViewから探すパターン)
    // 青線:出る 画面:出る
    navi.MenuItems.OfType<NavigationViewItem>().ToList().First().IsSelected = true;

    // ④NavigationViewのSelectedItemプロパティにNavigtionViewItemをセットする
    // 青線:出る 画面:出る
    navi.SelectedItem = vi1;//但し、画面は表示されるが、NavigationViewItemの下に選択されていることを示す青線がでない
}

青線がでる、というのは、下図のような感じで、選択されていることを示す線が出ているということ。

上のコードの②③④だと、その線がでる。①だとでない。

これは、①は、NavigationViewItemのどれかを「選んだ」のではなく、単にFrameの部分の表示を切り替えただけなので、 選択表示にならないものと思われる。(当たり前か?)

なので、nvSample_SelectionChanged()を通らない。

②③④は、NavigationViewItemのどれかを「選んだ」ことになるので、選択表示がでるとおもわれる。

nvSample_SelectionChanged()も通る。

これを、NavigationViewのLoaded時に行ってやれば、どれかのItemを選んだ状態で起動することができる。

どれをやればいいのかといわれると、

①は見た目、Itemを選択しているように見えなくて困るので、

上記②③④のうちのどれか1つを行えばOKと思うが、②④はNavigationViewItemに名前を付けないといけないので、名前を付けなくて済む③がよいかも。

※注意

この処理を、Windowのコンストラクタでやるのはやめた方がよさそう。

この後書く、選択の解除がうまく動かないケースがあった。 (Loaded以降のタイミングでやった方がよさそう)

コードから選択を解除する

どうも、NavigationViewは、一度Itemを選ぶと、選択されている項目がない状態にもどることはできない仕様っぽい。

しかし、どうしてもコードから全解除したい。

ということを言っている、別の方が挙げているmicrosoft-ui-xamlのgithubのissueがあったので、それを参考にやってみた。

下記のようにすれば、全解除ができた。

private void Button_Click(object sender, RoutedEventArgs e)
{
    navi.MenuItems.OfType<NavigationViewItem>().ToList().ForEach(x => x.IsSelected = false);
    navi.SelectedItem = null;
}

やっていることは、

  • すべてのNavigationViewItemの、IsSelected を false にする。
  • NavigationViewの、SelectedItem を null にする

ということ。

これで解除できる。が、issueをみるに、「たまたまそうやれば全解除できるけど、たまたまですよ」感を感じるので、将来もずっとこれで行けるかどうかはわからないかも?

参考

github issue
NavigationViewは、一度Itemを選ぶと、選択されている項目がない状態にもどることはできない仕様とある。

https://github.com/microsoft/microsoft-ui-xaml/issues/957

NavigationView系メモ記事

https://tera1707.com/entry/2022/02/27/213659

https://tera1707.com/entry/2022/03/09/232350