single.php

C# WinUI3 の[BreadcrumbBar]で区切りマークをクリックで[FlyoutMenu]を表示する手順

C# WinUI 3アプリを作っていく途中で、調べたことを忘録的に投稿します。今回はWinUI3プロジェクトで、Xamlに追加した[BreadcrumbBar]コントロールのアイテムをクリック操作で[FlyoutMenu]を表示する手順です。

アドレスバーのポップアップメニュー

WinUI3プロジェクトに追加できる[BreadcrumbBar]コントロールがあります。

少し仕様が異なりますが、Windows11の[エクスプローラー]のアドレスバーにも階層リンクバーのようなコントロールが実装されています。

[Windows エクスプローラー]では[Chevron]パーツをクリックすると一覧表示がメニュー表示でポップアップします。

今回は、この機能を[BreadcrumbBar]コントロールに実装してみます。

[Chevron]シェブロン

WinUI3プロジェクトに追加できる[BreadcrumbBar]コントロールのドキュメント[階層リンク バー]には次のような記載があります。

[構造]の解説によるとアイテムの間に表示されるパーツ名は[Chevron]です。

過去にもツールバーの表示領域が足りない際に、まだツールボタンが存在するのをユーザーに知らせる表示として同じ名前の機構がありました。

[Chevron]は軍服などに表示される階級章という意味のようです。

[LastItem]のスタイルを変更

今回は[軽量なスタイル設定(Lightweight styling)]で[LastItem]の状態を編集します。

[軽量なスタイル設定(Lightweight styling)]の詳しい内容は、別記事をご覧ください。

https://blog.janjan.net/2025/04/13/winui3-lightweight-styling-control

[FlyoutMenu]の表示

別記事で[TextBlock]で構成されている[Chevron]パーツを非表示にして[BreadcrumbBarItem]として区切りマークを追加してクリックによるイベント処理を可能にしました。

今回は、追加したイベントで[FlyoutMenu]を表示する方法を紹介します。

具体的には、次の手順で行います。

1. Xaml 編集画面で[BreadcrumbBar]コントロールに[ItemClicked]イベントを追加します。

<BreadcrumbBar
  x:Name="explorer_breadcrumbBar"
  ItemClicked="explorer_breadcrumbBar_ItemClicked">
  <BreadcrumbBar.Resources>
    <x:String x:Key="BreadcrumbBarChevronLeftToRight"></x:String>

    <Style
      BasedOn="{StaticResource CustomBreadcrumbBarItemStyle}"
      TargetType="BreadcrumbBarItem" />
  </BreadcrumbBar.Resources>
  <BreadcrumbBar.ItemTemplate>
    <DataTemplate x:DataType="local:StorageFolder">
      <TextBlock 
        Text="{x:Bind DisplayName}"
        FontSize="{x:Bind FontSize}" 
        FontFamily="{x:Bind FontFamily}" />
    </DataTemplate>
  </BreadcrumbBar.ItemTemplate>
</BreadcrumbBar>

2. 追加されたイベントに次のコードを追加します。

private void FlyoutMenuItemClick()
{
  //メニューをクリック時のイベント
  menuFlyout.Hide();
  menuFlyout.Items.Clear();
}

private void FlyoutMenuClosed()
{
  //メニューを閉じた時に[Chevron]風のアイテムの表示文字を変更
  var items = explorer_breadcrumbBar.ItemsSource as ObservableCollection<StorageFolder>;
  for(int nIndex = 0; nIndex < items.Count; nIndex++)
  {
    if (items[nIndex].DisplayName == "\uE70D")
    {
      explorer_breadcrumbBar.ItemsSource = "";
      folderlist[nIndex].DisplayName = "\uE76C";
      items[nIndex].DisplayName = "\uE76C";
      explorer_breadcrumbBar.ItemsSource = folderlist;
    }
  }
}

private void AddFlyoutMenuItem(string  menuText)
{
  //メニューの作成
  MenuFlyoutItem menuItem = new() { Text = menuText };
  menuItem.Click += (sender, e) => { this.FlyoutMenuItemClick(); };
  menuFlyout.Items.Add(menuItem);
}

private void explorer_breadcrumbBar_ItemClicked(Microsoft.UI.Xaml.Controls.BreadcrumbBar sender, BreadcrumbBarItemClickedEventArgs args)
{
  //アイテムをクリックした際のイベント
  var items = explorer_breadcrumbBar.ItemsSource as ObservableCollection<StorageFolder>;

  //メニューの初期化
  menuFlyout.Hide();
  menuFlyout.Items.Clear();

  if (items[args.Index].DisplayName == "\uE76C")
  {
    //アイテムが[>]の場合に[V]に変更
    explorer_breadcrumbBar.ItemsSource = "";
    folderlist[args.Index].DisplayName = "\uE70D";
    items[args.Index].DisplayName = "\uE70D";
    explorer_breadcrumbBar.ItemsSource = folderlist;

    //メニューを作成
    AddFlyoutMenuItem("表示テスト");
    menuFlyout.Closed += (sender, e) => { this.FlyoutMenuClosed(); };
    menuFlyout.ShouldConstrainToRootBounds = false;

    //メニューを作成
    menuFlyout.ShowAt(sender, new Point(0, explorer_breadcrumbBar.ActualHeight));

    return;
  }

  if (items[args.Index].DisplayName == "\uE70D")
  {
    //アイテムが[V]の場合に[>]に変更
    explorer_breadcrumbBar.ItemsSource = "";
    folderlist[args.Index].DisplayName = "\uE76C";
    items[args.Index].DisplayName = "\uE76C";
    explorer_breadcrumbBar.ItemsSource = folderlist;

    return;
  }
}

3. ビルドして実行します。

4.[>]部分をクリックすると[表示テスト]メニューがポップアップ表示します。

5. メニューを選択するか、[V]部分をクリックするとメニューが閉じます。

[FlyoutMenu]の表示位置

メニューをポップアップ表示させることができました。

しかし、表示位置の設定が無いので、Windows11のエクスプローラーのアドレスバーのようにクリックした[>]部分の場所にメニューが表示されません。

クリックした[>]の場所にメニューを表示するように修整します。

少し面倒な処理ですが[BreadcrumbBar]の場合、目的の[BreadcrumbBarItem]を追加した状態で[DesiredSize.Width]プロパティで左側位置を取得できます。

また、既にアイテムを追加している場合などは[UpdateLayout]メソッドで更新しないと正しい数値が取得できません。

長い処理になったので[GetDisplayMenuLeftPoint]関数にしました。

private double GetDisplayMenuLeftPoint(ObservableCollection<StorageFolder> items, int nItemIndex)
{
  ObservableCollection<StorageFolder> dummylist = new ObservableCollection<StorageFolder>();
  for (int nIndex = 0; nIndex < nItemIndex; nIndex++)
  {
    explorer_breadcrumbBar.ItemsSource = "";
    if (items[nIndex].DisplayName == "\uE76C" || items[nIndex].DisplayName == "\uE70D")
    {
      dummylist.Add(new StorageFolder() { DisplayName = items[nIndex].DisplayName, FontSize = 12, FontFamily = "Segoe Fluent Icons" });
    }
    else
    {
      dummylist.Add(new StorageFolder() { DisplayName = items[nIndex].DisplayName });
    }
  }

  explorer_breadcrumbBar.ItemsSource = dummylist;
  explorer_breadcrumbBar.UpdateLayout();
  double dblResult = explorer_breadcrumbBar.DesiredSize.Width;
  explorer_breadcrumbBar.ItemsSource = folderlist;
    
  return dblResult;
}

追加した関数を[ItemClicked]イベント内で呼び出してメニューの左上として設定します。

private void explorer_breadcrumbBar_ItemClicked(Microsoft.UI.Xaml.Controls.BreadcrumbBar sender, BreadcrumbBarItemClickedEventArgs args)
{
  //アイテムをクリックした際のイベント
  var items = explorer_breadcrumbBar.ItemsSource as ObservableCollection<StorageFolder>;

  //メニューの初期化
  menuFlyout.Hide();
  menuFlyout.Items.Clear();

  double dblDesireWidth = GetDisplayMenuLeftPoint(items, args.Index);

  if (items[args.Index].DisplayName == "\uE76C")
  {
    //アイテムが[>]の場合に[V]に変更
    explorer_breadcrumbBar.ItemsSource = "";
    folderlist[args.Index].DisplayName = "\uE70D";
    items[args.Index].DisplayName = "\uE70D";
    explorer_breadcrumbBar.ItemsSource = folderlist;

    //メニューを作成
    if(args.Index == 0)
    {
        AddFlyoutMenuItem("ホーム");
        AddFlyoutMenuItem("ギャラリー");
        AddFlyoutMenuItem("ダウンロード");
        AddFlyoutMenuItem("デスクトップ");
        AddFlyoutMenuItem("ドキュメント");
        AddFlyoutMenuItem("ピクチャ");
        AddFlyoutMenuItem("ビデオ");
        AddFlyoutMenuItem("ミュージック");
    }

    if (args.Index == 2)
    {
        AddFlyoutMenuItem("ローカル ディスク (C:)");
        AddFlyoutMenuItem("ボリューム (D:)");
        AddFlyoutMenuItem("ボリューム (F:)");
        AddFlyoutMenuItem("ボリューム (G:)");
        AddFlyoutMenuItem("ボリューム (H:)");
    }

    menuFlyout.Closed += (sender, e) => { this.FlyoutMenuClosed(); };
    menuFlyout.ShouldConstrainToRootBounds = false;

    //メニューを作成
    menuFlyout.ShowAt(sender, new Point(dblDesireWidth, explorer_breadcrumbBar.ActualHeight));

    return;
  }

  if (items[args.Index].DisplayName == "\uE70D")
  {
    //アイテムが[V]の場合に[>]に変更
    explorer_breadcrumbBar.ItemsSource = "";
    folderlist[args.Index].DisplayName = "\uE76C";
    items[args.Index].DisplayName = "\uE76C";
    explorer_breadcrumbBar.ItemsSource = folderlist;

    return;
  }
}

実行すると、左端の[>]をクリックでメニューがポップアップされます。

別の[>]をクリックすると、表示位置を変えて別メニューが表示されます。

まとめ

Visual StudioのWinUI3プロジェクトで、Xamlに追加した[BreadcrumbBar]コントロールのアイテムをクリック操作で[FlyoutMenu]を表示する手順を紹介しました。

長い手順とコードになりましたが、Windows11で提供されているエクスプローラーの[アドレスバー]で実装されている区切りマークを、クリックしてメニューが表示されるような機能が実装できました。

WinUI 3アプリで[BreadcrumbBar]で表示される[Chevron](区切り文字)をクリックでメニューをポップアップ表示したい人の参考になれば幸いです。

スポンサーリンク

最後までご覧いただき、ありがとうございます。

コメントを残す

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