C# WinUI 3アプリを作っていく途中で、調べたことを忘録的に投稿します。今回はWinUI3プロジェクトで、Xamlに追加した[BreadcrumbBar]コントロールのアイテムをクリック操作で[FlyoutMenu]を表示する手順です。
アドレスバーのポップアップメニュー
WinUI3プロジェクトに追加できる[BreadcrumbBar]コントロールがあります。
少し仕様が異なりますが、Windows11の[エクスプローラー]のアドレスバーにも階層リンクバーのようなコントロールが実装されています。
[Windows エクスプローラー]では[Chevron]パーツをクリックすると一覧表示がメニュー表示でポップアップします。
今回は、この機能を[BreadcrumbBar]コントロールに実装してみます。
[Chevron]シェブロン
WinUI3プロジェクトに追加できる[BreadcrumbBar]コントロールのドキュメント[階層リンク バー]には次のような記載があります。
[構造]の解説によるとアイテムの間に表示されるパーツ名は[Chevron]です。
過去にもツールバーの表示領域が足りない際に、まだツールボタンが存在するのをユーザーに知らせる表示として同じ名前の機構がありました。
[Chevron]は軍服などに表示される階級章という意味のようです。
[LastItem]のスタイルを変更
今回は[軽量なスタイル設定(Lightweight styling)]で[LastItem]の状態を編集します。
[軽量なスタイル設定(Lightweight styling)]の詳しい内容は、別記事をご覧ください。
[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](区切り文字)をクリックでメニューをポップアップ表示したい人の参考になれば幸いです。
スポンサーリンク
最後までご覧いただき、ありがとうございます。