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

如何通过自定义GroupItem实现ListBox的组标题可选中?

实现ListBox组标题可选中的完整方案

这个需求我之前做过类似的,核心就是给GroupItem扩展选中属性,再把它和ListBox的分组逻辑打通,我给你一步步拆解:

1. 先明确GroupItem和ListBox的关联

当ListBox通过CollectionViewSource实现分组后,每个分组会被包装成CollectionViewGroup对象,ListBox会自动为每个CollectionViewGroup创建一个GroupItem作为可视化容器——它就是你看到的组标题+展开器的载体,但默认没有IsSelected属性,所以我们需要自定义一个带选中状态的GroupItem子类。

2. 自定义带IsSelected属性的GroupItem

创建继承自GroupItem的类,添加IsSelected依赖属性,同时处理选中后的逻辑(比如选中组时自动选中组内所有项,或者仅标记组本身):

using System.Windows;
using System.Windows.Controls;

public class SelectableGroupItem : GroupItem
{
    // 定义IsSelected依赖属性,支持绑定和状态通知
    public static readonly DependencyProperty IsSelectedProperty =
        DependencyProperty.Register(
            nameof(IsSelected), 
            typeof(bool), 
            typeof(SelectableGroupItem), 
            new PropertyMetadata(false, OnIsSelectedChanged));

    public bool IsSelected
    {
        get => (bool)GetValue(IsSelectedProperty);
        set => SetValue(IsSelectedProperty, value);
    }

    // 选中状态变化时的回调逻辑
    private static void OnIsSelectedChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var groupItem = d as SelectableGroupItem;
        if (groupItem == null) return;

        // 获取当前组的数据源包装对象
        var group = groupItem.Content as CollectionViewGroup;
        if (group == null || groupItem.ListBox == null) return;

        // 这里可以自定义逻辑:比如选中组时选中所有子项,取消时取消子项选中
        if ((bool)e.NewValue)
        {
            foreach (var child in group.Items)
            {
                groupItem.ListBox.SelectedItems.Add(child);
            }
        }
        else
        {
            foreach (var child in group.Items)
            {
                groupItem.ListBox.SelectedItems.Remove(child);
            }
        }
    }

    // 获取当前GroupItem所属的ListBox实例
    private ListBox ListBox => ItemsControl.ItemsControlFromItemContainer(this) as ListBox;
}

3. 修改ListBox的GroupStyle,替换为自定义GroupItem

在XAML里,把ListBox默认的GroupItem容器换成我们自定义的SelectableGroupItem,同时自定义组标题的样式(比如选中时高亮):

<ListBox x:Name="MyListBox" SelectionMode="Extended">
    <ListBox.Resources>
        <!-- 布尔值转画刷的转换器,用于选中时的背景高亮 -->
        <local:BoolToBrushConverter x:Key="BoolToBrushConverter"/>
    </ListBox.Resources>

    <!-- 分组样式:替换容器为自定义的SelectableGroupItem -->
    <ListBox.GroupStyle>
        <GroupStyle>
            <GroupStyle.ContainerStyle>
                <Style TargetType="{x:Type local:SelectableGroupItem}">
                    <!-- 自定义GroupItem的模板,用Expander包裹标题和子项 -->
                    <Setter Property="Template">
                        <Setter.Value>
                            <ControlTemplate TargetType="{x:Type local:SelectableGroupItem}">
                                <Expander IsExpanded="True"
                                          Background="{TemplateBinding Background}">
                                    <Expander.Header>
                                        <!-- 标题区域:绑定IsSelected属性,选中时显示高亮背景 -->
                                        <Border Padding="6,3"
                                                Background="{Binding IsSelected, 
                                                    RelativeSource={RelativeSource TemplatedParent},
                                                    Converter={StaticResource BoolToBrushConverter},
                                                    ConverterParameter={x:Static Brushes.SkyBlue}}">
                                            <TextBlock Text="{Binding Name}" 
                                                       FontWeight="Bold" 
                                                       FontSize="14"/>
                                        </Border>
                                    </Expander.Header>
                                    <!-- 显示组内的子项 -->
                                    <ItemsPresenter/>
                                </Expander>
                            </ControlTemplate>
                        </Setter.Value>
                    </Setter>
                    <!-- 绑定鼠标点击事件,触发选中状态切换 -->
                    <EventSetter Event="MouseLeftButtonUp" 
                                 Handler="SelectableGroupItem_MouseLeftButtonUp"/>
                </Style>
            </GroupStyle.ContainerStyle>
        </GroupStyle>
    </ListBox.GroupStyle>

    <!-- 这里是你的ListBox项模板,根据自己的数据源自定义 -->
    <ListBox.ItemTemplate>
        <DataTemplate>
            <TextBlock Text="{Binding YourItemProperty}" Padding="10,2"/>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

4. 添加辅助转换器和点击事件处理

布尔转画刷转换器

需要一个简单的转换器,实现选中状态到背景色的转换:

using System;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Media;

public class BoolToBrushConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (value is bool isSelected && isSelected)
        {
            return parameter as Brush ?? Brushes.LightGray;
        }
        return Brushes.Transparent;
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

点击事件逻辑

在后台代码里处理GroupItem的点击,模拟ListBox原生的选中逻辑(支持Ctrl键切换选中):

private void SelectableGroupItem_MouseLeftButtonUp(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
    var groupItem = sender as SelectableGroupItem;
    if (groupItem == null) return;

    // 模拟ListBox的选中逻辑:Ctrl点击切换单个选中,否则选中当前组并取消其他组
    if (Keyboard.Modifiers == System.Windows.Input.ModifierKeys.Control)
    {
        groupItem.IsSelected = !groupItem.IsSelected;
    }
    else
    {
        // 取消其他所有组的选中状态
        foreach (var container in MyListBox.ItemContainerGenerator.Items
            .Select(item => MyListBox.ItemContainerGenerator.ContainerFromItem(item))
            .OfType<SelectableGroupItem>()
            .Where(container => container != groupItem))
        {
            container.IsSelected = false;
        }
        groupItem.IsSelected = true;
    }

    e.Handled = true; // 阻止事件冒泡,避免同时选中组内的项
}

可选定制说明

如果你不需要自动选中组内所有项,只需要修改OnIsSelectedChanged方法,去掉操作SelectedItems的代码,仅保留视觉状态的处理即可;另外ListBox的SelectionMode建议设为Extended或Multiple,这样才能支持多组选中的逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.20 09:18:58