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

Xamarin Forms可展开ListView仅部分项分组折叠展开的实现咨询

实现方案

核心思路是把无分组的单个菜单项伪装成仅包含单个元素的分组,隐藏该类分组的头部使其看起来是独立项,仅对有实际分组的菜单项显示可折叠的分组头部。

步骤1:新增分组模型类

定义支持折叠状态通知的分组模型:

using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Runtime.CompilerServices;

public class MenuGroup : ObservableCollection<NavMenuItem>, INotifyPropertyChanged
{
    private string _groupName;
    public string GroupName
    {
        get => _groupName;
        set { _groupName = value; OnPropertyChanged(); }
    }

    private bool _isExpanded = true;
    public bool IsExpanded
    {
        get => _isExpanded;
        set { _isExpanded = value; OnPropertyChanged(); }
    }

    // 标记是否为无分组的独立菜单项
    public bool IsStandalone => Count == 1 && string.IsNullOrEmpty(GroupName);

    public new event PropertyChangedEventHandler PropertyChanged;
    protected new void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

步骤2:改造数据源构造逻辑

将原有的平级菜单项列表转换成分组列表:

// 原有的菜单项定义保持不变
var allItems = new List<NavMenuItem>
{
    new NavMenuItem {Id = MenuItemType.Home, Title="Home", IconSource="homeicon.png" },
    new NavMenuItem {Id = MenuItemType.Offers, Title="Offerings", IconSource = "offeringsicon.png", Group = "Shopping"},
    // 剩下的菜单项和之前完全一致,此处省略
};

var menuGroups = new List<MenuGroup>();
// 处理有分组的菜单项,按Group值聚合
var groupedItems = allItems.Where(x => !string.IsNullOrEmpty(x.Group))
    .GroupBy(x => x.Group)
    .Select(g => new MenuGroup { GroupName = g.Key, g.ToList() });
menuGroups.AddRange(groupedItems);
// 处理无分组的菜单项,每个单独生成一个分组
var standaloneItems = allItems.Where(x => string.IsNullOrEmpty(x.Group));
foreach (var item in standaloneItems)
{
    menuGroups.Add(new MenuGroup { GroupName = "", new List<NavMenuItem> { item } });
}

// 可按需调整分组排序,比如将Home放在最顶部
ListViewMenu.ItemsSource = menuGroups;

步骤3:修改ListView的XAML代码

新增分组头部模板,控制头部显示和折叠交互:

<!-- 新增布尔值反转转换器,用于控制独立项分组的头部隐藏 -->
<ContentPage.Resources>
    <ResourceDictionary>
        <local:InverseBoolConverter x:Key="InverseBoolConverter" />
        <local:ExpandedIconConverter x:Key="ExpandedIconConverter" />
    </ResourceDictionary>
</ContentPage.Resources>

<ListView x:Name="ListViewMenu"
          HasUnevenRows="True"
          HorizontalOptions="Start"
          IsGroupingEnabled="True">
    <!-- 分组头部模板 -->
    <ListView.GroupHeaderTemplate>
        <DataTemplate>
            <ViewCell Height="{Binding IsStandalone, Converter={StaticResource InverseBoolConverter}, ConverterParameter=40;0}">
                <StackLayout 
                    Padding="15,8"
                    BackgroundColor="#f5f5f5">
                    <StackLayout.GestureRecognizers>
                        <TapGestureRecognizer Tapped="GroupHeader_Tapped" />
                    </StackLayout.GestureRecognizers>
                    <Grid IsVisible="{Binding IsStandalone, Converter={StaticResource InverseBoolConverter}}">
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition Width="*"/>
                            <ColumnDefinition Width="Auto"/>
                        </Grid.ColumnDefinitions>
                        <Label Text="{Binding GroupName}" FontSize="14" TextColor="#333" Grid.Column="0"/>
                        <!-- 展开/折叠状态箭头,可自行替换为图片 -->
                        <Label Text="{Binding IsExpanded, Converter={StaticResource ExpandedIconConverter}}" 
                               FontSize="14" Grid.Column="1" HorizontalOptions="End"/>
                    </Grid>
                </StackLayout>
            </ViewCell>
        </DataTemplate>
    </ListView.GroupHeaderTemplate>

    <!-- 原有菜单项模板,仅新增IsVisible绑定 -->
    <ListView.ItemTemplate>
        <DataTemplate>
            <ViewCell IsVisible="{Binding Source={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:MenuGroup}}, Path=IsExpanded}">
                <Frame HasShadow="True"
                       CornerRadius="10"
                       BorderColor="#282828"
                       Padding="1">
                    <StackLayout>
                        <Grid Padding="10" >
                            <Grid.ColumnDefinitions>
                                <ColumnDefinition Width="0.2*"/>
                                <ColumnDefinition Width="0.8*"/>
                            </Grid.ColumnDefinitions>
                            <Image Source="{Binding Image}" Grid.Column="0" WidthRequest="30" HeightRequest="30"/>
                            <Label Text="{Binding Title}" FontSize="Small" Grid.Column="1" TextColor="black"/>
                        </Grid>
                    </StackLayout>
                </Frame>
            </ViewCell>
        </DataTemplate>
    </ListView.ItemTemplate>
</ListView>

步骤4:新增辅助逻辑

  • 两个简单的转换器:InverseBoolConverter用于反转布尔值,ExpandedIconConverter用于将折叠状态转为箭头符号(展开返回↑,折叠返回↓)
  • 分组头部点击事件:
private void GroupHeader_Tapped(object sender, EventArgs e)
{
    var layout = sender as StackLayout;
    var group = layout.BindingContext as MenuGroup;
    group.IsExpanded = !group.IsExpanded;
}

该方案完全符合需求:有Group值的菜单项会显示分组头部,点击可折叠展开子项;无Group值的菜单项不会显示分组头部,直接作为独立项展示。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 15:36:03