在.NET MAUI中基于自定义类创建动态菜单的实现问询
在.NET MAUI中创建动态多级菜单(XAML/编程实现)
需求说明
ViewModel中已定义MyMenuItem类及对应的可观察集合,希望基于该集合生成支持子项的菜单栏(支持垂直/水平布局):
public class MyMenuItem { public string MyMenuItemName { get; set; } public MyMenuItem[] ChildMenuItems { get; set; } public ICommand MyMenuItemCommand { get; set; } }
已尝试编程实现但未成功,尝试的代码如下:
public void GenerateMenuBar() { var viewModel = (HomePageViewModel)BindingContext; AddMenuItemsToMenuBar(viewModel.MenuItems); } private void AddMenuItemsToMenuBar(IEnumerable<MyMenuItem> menuItems, IElement parent = null) { foreach (var menuItem in menuItems) { IElement currentMenuItem; if (parent is MenuFlyoutSubItem) { var parentElement = parent as MenuFlyoutSubItem; currentMenuItem = new MenuFlyoutItem {Text = menuItem.MyMenuItemName, Parent = parentElement}; parentElement.Add((MenuFlyoutItem)currentMenuItem); } else if (parent is MenuBarItem && menuItem.ChildMenuItems.Length > 0) { var parentElement = parent as MenuBarItem; currentMenuItem = new MenuFlyoutSubItem {Text = menuItem.MyMenuItemName, Parent = parentElement}; parentElement.Add((MenuFlyoutSubItem)currentMenuItem); } else if (parent is MenuBarItem && menuItem.ChildMenuItems.Length == 0) { var parentElement = parent as MenuBarItem; currentMenuItem = new MenuFlyoutItem { Text = menuItem.MyMenuItemName, Parent = parentElement }; parentElement.Add((MenuFlyoutItem)currentMenuItem); } else { currentMenuItem = new MenuBarItem {Text = menuItem.MyMenuItemName}; this.MenuBarItems.Add((MenuBarItem)currentMenuItem); } if (menuItem.ChildMenuItems != null) AddMenuItemsToMenuBar(menuItem.ChildMenuItems, currentMenuItem); } }
一、编程方式修复
你的代码存在空数组判断遗漏、未绑定命令、手动设置Parent属性等问题,调整后可正常生成菜单:
public void GenerateMenuBar() { var viewModel = (HomePageViewModel)BindingContext; MenuBarItems.Clear(); // 清空原有菜单避免重复添加 AddMenuItemsToMenuBar(viewModel.MenuItems); } private void AddMenuItemsToMenuBar(IEnumerable<MyMenuItem> menuItems, IElement parent = null) { foreach (var menuItem in menuItems) { if (parent is MenuFlyoutSubItem flyoutParent) { var flyoutItem = new MenuFlyoutItem { Text = menuItem.MyMenuItemName, Command = menuItem.MyMenuItemCommand }; flyoutParent.Add(flyoutItem); // 递归添加子项 if (menuItem.ChildMenuItems?.Length > 0) { var subFlyout = new MenuFlyoutSubItem { Text = menuItem.MyMenuItemName }; flyoutParent.Add(subFlyout); AddMenuItemsToMenuBar(menuItem.ChildMenuItems, subFlyout); } } else if (parent is MenuBarItem barParent) { if (menuItem.ChildMenuItems?.Length > 0) { var subFlyout = new MenuFlyoutSubItem { Text = menuItem.MyMenuItemName }; barParent.Add(subFlyout); AddMenuItemsToMenuBar(menuItem.ChildMenuItems, subFlyout); } else { var barItem = new MenuFlyoutItem { Text = menuItem.MyMenuItemName, Command = menuItem.MyMenuItemCommand }; barParent.Add(barItem); } } else { // 顶级菜单处理 var barItem = new MenuBarItem { Text = menuItem.MyMenuItemName }; MenuBarItems.Add(barItem); if (menuItem.ChildMenuItems?.Length > 0) { AddMenuItemsToMenuBar(menuItem.ChildMenuItems, barItem); } else { var flyoutItem = new MenuFlyoutItem { Text = menuItem.MyMenuItemName, Command = menuItem.MyMenuItemCommand }; barItem.Add(flyoutItem); } } } }
二、XAML绑定方式(推荐,符合MVVM)
通过DataTemplate和递归绑定实现,无需手动生成UI:
1. 页面XAML代码
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="YourNamespace.HomePage" BindingContext="{StaticResource HomePageViewModel}"> <ContentPage.MenuBarItems> <CollectionView ItemsSource="{Binding MenuItems}"> <CollectionView.ItemTemplate> <DataTemplate> <MenuBarItem Text="{Binding MyMenuItemName}"> <MenuBarItem.ItemsSource> <CollectionView ItemsSource="{Binding ChildMenuItems}"> <CollectionView.ItemTemplate> <DataTemplate> <ContentView> <ContentView.Content> <!-- 有子项时显示折叠菜单 --> <MenuFlyoutSubItem Text="{Binding MyMenuItemName}" ItemsSource="{Binding ChildMenuItems}" IsVisible="{Binding ChildMenuItems, Converter={StaticResource HasItemsConverter}}"> <MenuFlyoutSubItem.ItemTemplate> <DataTemplate> <MenuFlyoutItem Text="{Binding MyMenuItemName}" Command="{Binding MyMenuItemCommand}" /> </DataTemplate> </MenuFlyoutSubItem.ItemTemplate> </MenuFlyoutSubItem> <!-- 无子项时显示可点击项 --> <MenuFlyoutItem Text="{Binding MyMenuItemName}" Command="{Binding MyMenuItemCommand}" IsVisible="{Binding ChildMenuItems, Converter={StaticResource NoItemsConverter}}" /> </ContentView.Content> </ContentView> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView> </MenuBarItem.ItemsSource> </MenuBarItem> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView> </ContentPage.MenuBarItems> <!-- 页面其他内容 --> </ContentPage>
2. 转换器实现
// 判断是否有子项的转换器 public class HasItemsConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { return value is MyMenuItem[] items && items.Length > 0; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } // 判断是否无子项的转换器 public class NoItemsConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { return value is not MyMenuItem[] items || items.Length == 0; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
3. 注册转换器(App.xaml)
<Application.Resources> <ResourceDictionary> <local:HasItemsConverter x:Key="HasItemsConverter" /> <local:NoItemsConverter x:Key="NoItemsConverter" /> <local:HomePageViewModel x:Key="HomePageViewModel" /> </ResourceDictionary> </Application.Resources>
三、垂直布局菜单实现
如果需要垂直菜单(而非顶部菜单栏),可以用Expander配合CollectionView实现:
<CollectionView ItemsSource="{Binding MenuItems}" ItemsLayout="VerticalList"> <CollectionView.ItemTemplate> <DataTemplate> <Expander Header="{Binding MyMenuItemName}" TappedCommand="{Binding MyMenuItemCommand}"> <Expander.Content> <CollectionView ItemsSource="{Binding ChildMenuItems}" Margin="20,0,0,0"> <CollectionView.ItemTemplate> <DataTemplate> <Label Text="{Binding MyMenuItemName}" Margin="10,5"> <Label.GestureRecognizers> <TapGestureRecognizer Command="{Binding MyMenuItemCommand}" /> </Label.GestureRecognizers> </Label> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView> </Expander.Content> </Expander> </DataTemplate> </CollectionView.ItemTemplate> </CollectionView>
内容的提问来源于stack exchange,提问作者Pomodoro
相关产品推荐
相关产品推荐

