实现带「显示更多X项」功能的ListBox样式控件
实现可展开/收起的ListBox控件(显示隐藏条目数)
我想做一个能通过Expander切换展开/收起状态的列表控件:收起时固定显示3项,同时提示隐藏的条目数量;点击后展开查看全部,再次点击回到仅显示3项的状态。自己写了个样式,但效果差、外观不佳,希望得到改进方案或更规范的实现方式。
原实现代码
XAML样式
<Style x:Key="CustomListBoxKey" TargetType="{x:Type ListBox}"> <Setter Property="Template"> <Setter.Value> <ControlTemplate> <Grid> <Grid.RowDefinitions> <RowDefinition/> <RowDefinition/> </Grid.RowDefinitions> <ItemsPresenter/> <Expander x:Name="Expander" Grid.Row="1" Header="Show X More" ExpandDirection="Down"/> </Grid> <ControlTemplate.Triggers> <DataTrigger Binding="{Binding ElementName=Expander, Path=IsExpanded}" Value="False"> <Setter Property="MaxHeight" Value="80"/> </DataTrigger> <DataTrigger Binding="{Binding ElementName=Expander, Path=IsExpanded}" Value="True"> <Setter Property="MaxHeight" Value="Infinity"/> </DataTrigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style>
绑定数据源(C#)
public List<string> Items { get; set; } = new() { "A", "B", "C", "D", "E", "F", "G", "H", "I" };
使用示例
<Grid> <ListBox ItemsSource="{Binding Items}" Style="{StaticResource CustomListBoxKey}" VerticalAlignment="Top"> <ListBox.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding Path=.}"/> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Grid>
改进后的规范实现方案
核心优化点
- 动态计算并显示隐藏条目数,替换固定文本
- 基于Item高度计算收起时的MaxHeight,避免硬编码数值
- 优化布局结构,让Expander与ListBox的交互更自然
- 自动隐藏条目数≤3时的Expander
- 展开时启用滚动条,避免内容溢出
完整代码实现
XAML样式与转换器引用
<Window.Resources> <!-- 可折叠ListBox样式 --> <Style x:Key="CollapsibleListBoxStyle" TargetType="{x:Type ListBox}"> <Setter Property="ScrollViewer.HorizontalScrollBarVisibility" Value="Disabled"/> <Setter Property="ScrollViewer.VerticalScrollBarVisibility" Value="Auto"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="{x:Type ListBox}"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="*"/> <RowDefinition Height="Auto"/> </Grid.RowDefinitions> <!-- 包裹ItemsPresenter,控制收起高度 --> <ScrollViewer x:Name="ItemsScrollViewer" Grid.Row="0" VerticalScrollBarVisibility="Hidden" HorizontalScrollBarVisibility="Disabled"> <ItemsPresenter/> </ScrollViewer> <!-- 动态显示状态的Expander --> <Expander x:Name="ItemsExpander" Grid.Row="1" Margin="0,4,0,0" HorizontalAlignment="Stretch" ExpandDirection="Down"> <Expander.Header> <TextBlock> <Run Text="{Binding ElementName=ItemsScrollViewer, Path=ExtentHeight, Converter={StaticResource HiddenItemsCountConverter}, ConverterParameter={Binding RelativeSource={RelativeSource TemplatedParent}, Path=ItemHeight}}"/> <Run Text=" 项"/> </TextBlock> </Expander.Header> </Expander> </Grid> <ControlTemplate.Triggers> <!-- 条目数≤3时隐藏Expander --> <DataTrigger Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Items.Count}" Value="0"> <Setter TargetName="ItemsExpander" Property="Visibility" Value="Collapsed"/> </DataTrigger> <DataTrigger Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Items.Count}" Value="1"> <Setter TargetName="ItemsExpander" Property="Visibility" Value="Collapsed"/> </DataTrigger> <DataTrigger Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Items.Count}" Value="2"> <Setter TargetName="ItemsExpander" Property="Visibility" Value="Collapsed"/> </DataTrigger> <DataTrigger Binding="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=Items.Count}" Value="3"> <Setter TargetName="ItemsExpander" Property="Visibility" Value="Collapsed"/> </DataTrigger> <!-- 收起状态:限制高度为3个Item的总高度 --> <DataTrigger Binding="{Binding ElementName=ItemsExpander, Path=IsExpanded}" Value="False"> <Setter TargetName="ItemsScrollViewer" Property="MaxHeight" Value="{Binding RelativeSource={RelativeSource TemplatedParent}, Path=ItemHeight, Converter={StaticResource MultiplyConverter}, ConverterParameter=3}"/> <Setter TargetName="ItemsExpander" Property="Header" Value="{Binding ElementName=ItemsScrollViewer, Path=ExtentHeight, Converter={StaticResource HiddenItemsCountConverter}, ConverterParameter={Binding RelativeSource={RelativeSource TemplatedParent}, Path=ItemHeight}} 项"/> </DataTrigger> <!-- 展开状态:取消高度限制,显示滚动条 --> <DataTrigger Binding="{Binding ElementName=ItemsExpander, Path=IsExpanded}" Value="True"> <Setter TargetName="ItemsScrollViewer" Property="MaxHeight" Value="Infinity"/> <Setter TargetName="ItemsScrollViewer" Property="VerticalScrollBarVisibility" Value="Auto"/> <Setter TargetName="ItemsExpander" Property="Header" Value="收起"/> </DataTrigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style> <!-- 计算隐藏条目数的转换器 --> <local:HiddenItemsCountConverter x:Key="HiddenItemsCountConverter"/> <!-- 乘法转换器,计算3个Item的总高度 --> <local:MultiplyConverter x:Key="MultiplyConverter"/> </Window.Resources>
转换器代码(C#)
public class HiddenItemsCountConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is double extentHeight && parameter is double itemHeight && itemHeight > 0) { var totalItems = Math.Floor(extentHeight / itemHeight); var visibleItems = 3; return totalItems > visibleItems ? totalItems - visibleItems : 0; } return 0; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } public class MultiplyConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value is double itemHeight && double.TryParse(parameter.ToString(), out double multiplier)) { return itemHeight * multiplier; } return 0; } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { throw new NotImplementedException(); } }
使用示例
<Grid> <ListBox ItemsSource="{Binding Items}" Style="{StaticResource CollapsibleListBoxStyle}" ItemHeight="24" <!-- 固定Item高度,确保计算准确 --> VerticalAlignment="Top"> <ListBox.ItemTemplate> <DataTemplate> <TextBlock Text="{Binding}" Padding="4,2"/> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Grid>
关键说明
- 动态文本:通过转换器自动计算隐藏条目数,Expander标题会根据状态切换为"X项"或"收起"
- 自适应高度:收起时的MaxHeight由ItemHeight乘以3得到,确保刚好显示3项,适配不同Item高度
- 智能隐藏:当列表条目数≤3时,Expander自动隐藏,避免冗余控件
- 滚动支持:展开时启用垂直滚动条,避免内容超出容器范围
内容的提问来源于stack exchange,提问作者Easdev
相关产品推荐
相关产品推荐

