You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET MAUI 7中MenuFlyout如何绑定集合?无ItemsSource解决方案

.NET MAUI 7 为 MenuFlyout/MenuFlyoutSubItem 绑定集合的实现方案

由于.NET MAUI 7的MenuFlyout和MenuFlyoutSubItem原生没有ItemsSource属性,无法直接绑定集合生成菜单项,我们可以通过自定义附加属性实现动态生成,无需硬编码所有项。

具体实现步骤

1. 创建附加属性工具类

新建静态类,定义用于绑定集合的附加属性,以及集合变化时自动生成菜单项的逻辑:

using Microsoft.Maui.Controls;
using System.Collections;
using System.Collections.Specialized;

namespace YourAppNamespace
{
    public static class MenuFlyoutBindingExtensions
    {
        // 定义绑定集合的附加属性
        public static readonly BindableProperty ItemsSourceProperty =
            BindableProperty.CreateAttached(
                "ItemsSource",
                typeof(IEnumerable),
                typeof(MenuFlyoutBindingExtensions),
                null,
                propertyChanged: OnItemsSourceChanged);

        // 定义菜单项模板的附加属性
        public static readonly BindableProperty ItemTemplateProperty =
            BindableProperty.CreateAttached(
                "ItemTemplate",
                typeof(DataTemplate),
                typeof(MenuFlyoutBindingExtensions),
                null);

        // 获取ItemsSource的方法
        public static IEnumerable GetItemsSource(BindableObject obj)
        {
            return (IEnumerable)obj.GetValue(ItemsSourceProperty);
        }

        // 设置ItemsSource的方法
        public static void SetItemsSource(BindableObject obj, IEnumerable value)
        {
            obj.SetValue(ItemsSourceProperty, value);
        }

        // 获取ItemTemplate的方法
        public static DataTemplate GetItemTemplate(BindableObject obj)
        {
            return (DataTemplate)obj.GetValue(ItemTemplateProperty);
        }

        // 设置ItemTemplate的方法
        public static void SetItemTemplate(BindableObject obj, DataTemplate value)
        {
            obj.SetValue(ItemTemplateProperty, value);
        }

        // 集合变化时更新菜单项
        private static void OnItemsSourceChanged(BindableObject bindable, object oldValue, object newValue)
        {
            if (bindable is not MenuFlyoutBase menuContainer || newValue is not IEnumerable items)
                return;

            // 移除旧集合的监听(如果是可观察集合)
            if (oldValue is INotifyCollectionChanged oldObservable)
                oldObservable.CollectionChanged -= OnCollectionChanged;

            // 清空原有菜单项
            menuContainer.Clear();

            var itemTemplate = GetItemTemplate(bindable);
            if (itemTemplate == null)
                return;

            // 生成新菜单项
            foreach (var item in items)
            {
                if (itemTemplate.CreateContent() is MenuFlyoutItem menuItem)
                {
                    menuItem.BindingContext = item;
                    menuContainer.Add(menuItem);
                }
            }

            // 监听新集合的变化(支持动态增删)
            if (newValue is INotifyCollectionChanged newObservable)
                newObservable.CollectionChanged += (s, e) => OnCollectionChanged(s, e, menuContainer, itemTemplate);
        }

        // 处理集合动态增删
        private static void OnCollectionChanged(object? sender, NotifyCollectionChangedEventArgs e, MenuFlyoutBase menuContainer, DataTemplate itemTemplate)
        {
            if (e.Action == NotifyCollectionChangedAction.Add && e.NewItems != null)
            {
                foreach (var item in e.NewItems)
                {
                    if (itemTemplate.CreateContent() is MenuFlyoutItem menuItem)
                    {
                        menuItem.BindingContext = item;
                        menuContainer.Add(menuItem);
                    }
                }
            }
            else if (e.Action == NotifyCollectionChangedAction.Remove && e.OldItems != null)
            {
                foreach (var item in e.OldItems)
                {
                    var targetItem = menuContainer.FirstOrDefault(m => m.BindingContext == item);
                    if (targetItem != null)
                        menuContainer.Remove(targetItem);
                }
            }
            else if (e.Action == NotifyCollectionChangedAction.Reset)
            {
                menuContainer.Clear();
            }
        }
    }
}

2. 在XAML中使用附加属性

在页面XAML中引用自定义命名空间,给MenuFlyout或MenuFlyoutSubItem设置绑定属性:

<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:local="clr-namespace:YourAppNamespace"
             x:Class="YourAppNamespace.MainPage">

    <StackLayout>
        <!-- 给控件绑定上下文菜单 -->
        <Label Text="长按显示菜单" Padding="20">
            <Label.ContextMenu>
                <MenuFlyout>
                    <!-- 固定菜单项 -->
                    <MenuFlyoutItem Text="固定操作" Command="{Binding FixedCommand}" />
                    <!-- 动态绑定的子菜单 -->
                    <MenuFlyoutSubItem Text="动态子菜单"
                                      local:MenuFlyoutBindingExtensions.ItemsSource="{Binding SubMenuItems}"
                                      local:MenuFlyoutBindingExtensions.ItemTemplate="{StaticResource SubMenuItemTemplate}" />
                </MenuFlyout>
            </Label.ContextMenu>
        </Label>
    </StackLayout>

    <!-- 菜单项模板 -->
    <ContentPage.Resources>
        <DataTemplate x:Key="SubMenuItemTemplate">
            <MenuFlyoutItem Text="{Binding MenuText}"
                            Command="{Binding MenuCommand}"
                            CommandParameter="{Binding MenuParam}" />
        </DataTemplate>
    </ContentPage.Resources>
</ContentPage>

3. 准备ViewModel与数据模型

创建对应的菜单项模型和ViewModel,提供绑定的集合数据:

// 菜单项数据模型
public class MenuItemModel
{
    public string MenuText { get; set; }
    public ICommand MenuCommand { get; set; }
    public object MenuParam { get; set; }
}

// 页面ViewModel
public class MainViewModel
{
    public ObservableCollection<MenuItemModel> SubMenuItems { get; set; }
    public ICommand FixedCommand { get; }
    public ICommand DynamicMenuCommand { get; }

    public MainViewModel()
    {
        // 初始化动态子菜单集合
        SubMenuItems = new ObservableCollection<MenuItemModel>
        {
            new MenuItemModel { MenuText = "动态选项1", MenuCommand = DynamicMenuCommand, MenuParam = "1" },
            new MenuItemModel { MenuText = "动态选项2", MenuCommand = DynamicMenuCommand, MenuParam = "2" }
        };

        FixedCommand = new Command(() => Console.WriteLine("固定操作被触发"));
        DynamicMenuCommand = new Command<object>(param => Console.WriteLine($"动态选项{param}被触发"));
    }
}

额外说明

  • 上述方案支持集合的动态更新(增删项自动同步菜单),因为监听了INotifyCollectionChanged事件。
  • 不仅适用于MenuFlyout,MenuFlyoutSubItem也可以直接套用该附加属性实现子菜单的动态绑定。

内容的提问来源于stack exchange,提问作者tech74

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.07 14:45:24