如何在 WPF ListBox 中添加 headers
How to add headers in WPF ListBox
如何在 WPF 的 ListBox 中添加 header?我有以下代码,其中“Header - ..”文本是我想要下面所有项目的 header/group 名称,直到下一个 header:
<ListBox
x:Name="ItemsListBox"
Margin="0 16 0 16"
Style="{StaticResource MaterialDesignNavigationPrimaryListBox}">
<ListBox.Resources>
<Style TargetType="ScrollBar" BasedOn="{StaticResource MaterialDesignScrollBarMinimal}"/>
</ListBox.Resources>
Header - Config
<Separator/>
<ListBoxItem>Config menu 1</ListBoxItem>
<ListBoxItem>Config menu 2</ListBoxItem>
<ListBoxItem>Config menu 3</ListBoxItem>
<ListBoxItem>Config menu 4</ListBoxItem>
<ListBoxItem>Config menu 5</ListBoxItem>
<ListBoxItem>Config menu 6</ListBoxItem>
<ListBoxItem>Config menu 7</ListBoxItem>
Header - Tasks
<Separator/>
<ListBoxItem>Task menu 1</ListBoxItem>
<ListBoxItem>Task menu 2</ListBoxItem>
<ListBoxItem>Task menu 3</ListBoxItem>
<ListBoxItem>Task menu 4</ListBoxItem>
<ListBoxItem>Task menu 5</ListBoxItem>
<ListBoxItem>Task menu 6</ListBoxItem>
<ListBoxItem>Task menu 7</ListBoxItem>
<Separator/>
</ListBox>
下面是我想要实现的示例:
不一定需要每个列表框项目都有图标,但主要是 header 选项、用户设置和管理等图标。
我只看到其他线程,其中人们有多个列,但这会创建两列不同的数据,我想有 1 列,但只需添加粗体的 headers 和更大的字体将不同的菜单彼此分开。
我正在为 WPF 使用 MaterialDesign 主题 - 无论它是否重要或相关...
谢谢!
编辑 1:正如我之前没有提到的 - 它不一定是列表框,我使用它是因为它在我用来编写菜单的代码中使用:
<materialDesign:DrawerHost
IsLeftDrawerOpen="{Binding ElementName=MenuToggleButton, Path=IsChecked}">
<materialDesign:DrawerHost.LeftDrawerContent>
<DockPanel MinWidth="220">
<ToggleButton
Style="{StaticResource MaterialDesignHamburgerToggleButton}"
DockPanel.Dock="Top"
HorizontalAlignment="Right"
Margin="16"
IsChecked="{Binding ElementName=MenuToggleButton, Path=IsChecked, Mode=TwoWay}"/>
<TextBox
x:Name="DemoItemsSearchBox"
Text="{Binding SearchKeyword, UpdateSourceTrigger=PropertyChanged}"
DockPanel.Dock="Top"
Margin="16, 4"
Width="200"
materialDesign:HintAssist.Hint="Search"
materialDesign:HintAssist.IsFloating="True"
materialDesign:TextFieldAssist.HasClearButton="True"
materialDesign:TextFieldAssist.HasOutlinedTextField="True"
materialDesign:TextFieldAssist.DecorationVisibility="Collapsed"
materialDesign:TextFieldAssist.TextFieldCornerRadius="4"/>
<ListBox
x:Name="ItemsListBox"
Margin="0 16 0 16"
Style="{StaticResource MaterialDesignNavigationPrimaryListBox}">
<ListBox.Resources>
<Style TargetType="ScrollBar" BasedOn="{StaticResource MaterialDesignScrollBarMinimal}"/>
</ListBox.Resources>
-- This is where the ListBox items would be --
</ListBox>
</DockPanel>
</materialDesign:DrawerHost.LeftDrawerContent>
<DockPanel>
<materialDesign:ColorZone
Padding="16"
materialDesign:ShadowAssist.ShadowDepth="Depth2"
Mode="PrimaryMid"
DockPanel.Dock="Top">
<DockPanel>
<StackPanel Orientation="Horizontal">
<ToggleButton
x:Name="MenuToggleButton"
Style="{StaticResource MaterialDesignHamburgerToggleButton}"
IsChecked="False"
Click="MenuToggleButton_OnClick"
AutomationProperties.Name="HamburgerToggleButton"/>
</StackPanel>
<materialDesign:PopupBox
DockPanel.Dock="Right"
PlacementMode="BottomAndAlignRightEdges"
StaysOpen="False">
<StackPanel>
<Grid Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock
Text="Light"
Margin="0 0 10 0"/>
<ToggleButton
x:Name="DarkModeToggleButton"
Click="MenuDarkModeButton_Click"
Grid.Column="1"/>
<TextBlock
Text="Dark"
Margin="10 0 0 0"
Grid.Column="2"/>
<TextBlock
Text="Enabled"
Margin="0 10 10 0"
Grid.Row="1"/>
<ToggleButton
x:Name="ControlsEnabledToggleButton"
Margin="0 10 0 0"
IsChecked="{Binding ControlsEnabled}"
Grid.Row="1"
Grid.Column="1"/>
</Grid>
<Separator/>
<Button
Content="Hello World"
/>
<Button
Content="Nice Popup"
/>
<Button
Content="Can't Touch This"
IsEnabled="False"/>
<Separator/>
<Button
Content="Goodbye"
/>
</StackPanel>
</materialDesign:PopupBox>
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="22"
Margin="-152,0,0,0"
Text="Hi"/>
</DockPanel>
</materialDesign:ColorZone>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ScrollViewer
x:Name="MainScrollViewer"
Grid.Row="1"
materialDesign:ScrollViewerAssist.IsAutoHideEnabled="True"
HorizontalScrollBarVisibility="{Binding SelectedItem.HorizontalScrollBarVisibilityRequirement, FallbackValue=Disabled}"
VerticalScrollBarVisibility="{Binding SelectedItem.VerticalScrollBarVisibilityRequirement, FallbackValue=Disabled}" >
<ContentControl
Margin="{Binding MarginRequirement, FallbackValue=16}"/>
</ScrollViewer>
<materialDesign:Snackbar
x:Name="MainSnackbar"
MessageQueue="{materialDesign:MessageQueue}"
Grid.Row="1"/>
</Grid>
</DockPanel>
</materialDesign:DrawerHost>
可能不是我的最终答案,因为仍然使用演示中的 MVVM,但使用演示项目:
在MainWindowViewModel.cs中,在public ObservableCollection<DemoItem> DemoItems { get; }
之后添加public ListCollectionView DemoLCV { get; }
并添加
DemoLCV = new ListCollectionView(DemoItems);
DemoLCV.GroupDescriptions.Add(new PropertyGroupDescription("Name"));
之后
MoveNextCommand = new AnotherCommandImplementation(
_ =>
{
if (!string.IsNullOrWhiteSpace(SearchKeyword))
SearchKeyword = string.Empty;
SelectedIndex++;
},
_ => SelectedIndex < DemoItems.Count - 1);
然后在MainWindow.xaml中修改ListBox为:
<ListBox
x:Name="DemoItemsListBox"
Margin="0 16 0 16"
SelectedIndex="{Binding SelectedIndex}"
SelectedItem="{Binding SelectedItem, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding DemoLCV}"
PreviewMouseLeftButtonUp="UIElement_OnPreviewMouseLeftButtonUp"
AutomationProperties.Name="DemoPagesListBox"
Style="{StaticResource MaterialDesignNavigationPrimaryListBox}">
<ListBox.Resources>
<Style TargetType="ScrollBar" BasedOn="{StaticResource MaterialDesignScrollBarMinimal}"/>
</ListBox.Resources>
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListBox.GroupStyle>
<ListBox.ItemTemplate>
<DataTemplate DataType="domain:DemoItem">
<TextBlock Text="{Binding Name}" Margin="24 4 0 4" AutomationProperties.AutomationId="DemoItemPage"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
这会将菜单项名称添加为每个项目的类别标题。
这只是为了演示这个概念。
要使用有用的标题扩展这个想法,将另一个 属性 添加到 DemoItem class,例如categoryName,并在 DemoItems collection.
初始化时用不同的类别填充它
然后将此示例中的 DemoLCV.GroupDescriptions.Add(new PropertyGroupDescription("Name"));
更改为 DemoLCV.GroupDescriptions.Add(new PropertyGroupDescription("categoryName"));
以按您的类别分组。
然后您可以使用 ListBox.GroupStyle 中的 TextBlock 样式来根据需要显示标题。
稍后我将尝试弹出一个示例,并看看我是否可以想出一个 non-MVVM 选项。
对于硬编码 xaml、非 MVVM 解决方案,我会使用菜单而不是列表框。我通常只使用 ListBoxes 绑定到 data/items.
列表
我们也可以用这种方法实现你的图标。
当然,风格随你喜欢,但如果你取出你的ListBox,你可以用Menu替换如下:
<ScrollViewer>
<Menu>
<Menu.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</Menu.ItemsPanel>
<Menu.Resources>
<Style TargetType="ScrollBar" BasedOn="{StaticResource MaterialDesignScrollBarMinimal}"/>
</Menu.Resources>
<TextBlock Text="Config" FontWeight="Bold" FontSize="20" IsEnabled="False" />
<Separator/>
<MenuItem>
<MenuItem.Header>
<DockPanel>
<materialDesign:PackIcon Kind="Settings" Margin="3" />
<TextBlock Text="Config item 1" Margin="3" />
</DockPanel>
</MenuItem.Header>
</MenuItem>
<MenuItem >
<MenuItem.Header>
<DockPanel>
<materialDesign:PackIcon Kind="Settings" Margin="3" />
<TextBlock Text="Config item 2" Margin="3" />
</DockPanel>
</MenuItem.Header>
</MenuItem>
<MenuItem >
<MenuItem.Header>
<DockPanel>
<materialDesign:PackIcon Kind="Settings" Margin="3" />
<TextBlock Text="Config item 3" Margin="3" />
</DockPanel>
</MenuItem.Header>
</MenuItem>
<MenuItem >
<MenuItem.Header>
<DockPanel>
<materialDesign:PackIcon Kind="Settings" Margin="3" />
<TextBlock Text="Config item 4" Margin="3" />
</DockPanel>
</MenuItem.Header>
</MenuItem>
<MenuItem >
<MenuItem.Header>
<DockPanel>
<materialDesign:PackIcon Kind="Settings" Margin="3" />
<TextBlock Text="Config item 5" Margin="3" />
</DockPanel>
</MenuItem.Header>
</MenuItem>
<MenuItem >
<MenuItem.Header>
<DockPanel>
<materialDesign:PackIcon Kind="Settings" Margin="3" />
<TextBlock Text="Config item 6" Margin="3" />
</DockPanel>
</MenuItem.Header>
</MenuItem>
<MenuItem >
<MenuItem.Header>
<DockPanel>
<materialDesign:PackIcon Kind="Settings" Margin="3" />
<TextBlock Text="Config item 7" Margin="3" />
</DockPanel>
</MenuItem.Header>
</MenuItem>
<TextBlock Text="Tasks" FontWeight="Bold" FontSize="20" />
<Separator/>
<MenuItem Header="Task item 1" />
<MenuItem Header="Task item 2" />
<MenuItem Header="Task item 3" />
<MenuItem Header="Task item 4" />
<MenuItem Header="Task item 5" />
<MenuItem Header="Task item 6" />
<MenuItem Header="Task item 7" />
</Menu>
</ScrollViewer>
这会产生(我只在上半部分添加了图标,以说明如何):
如何在 WPF 的 ListBox 中添加 header?我有以下代码,其中“Header - ..”文本是我想要下面所有项目的 header/group 名称,直到下一个 header:
<ListBox
x:Name="ItemsListBox"
Margin="0 16 0 16"
Style="{StaticResource MaterialDesignNavigationPrimaryListBox}">
<ListBox.Resources>
<Style TargetType="ScrollBar" BasedOn="{StaticResource MaterialDesignScrollBarMinimal}"/>
</ListBox.Resources>
Header - Config
<Separator/>
<ListBoxItem>Config menu 1</ListBoxItem>
<ListBoxItem>Config menu 2</ListBoxItem>
<ListBoxItem>Config menu 3</ListBoxItem>
<ListBoxItem>Config menu 4</ListBoxItem>
<ListBoxItem>Config menu 5</ListBoxItem>
<ListBoxItem>Config menu 6</ListBoxItem>
<ListBoxItem>Config menu 7</ListBoxItem>
Header - Tasks
<Separator/>
<ListBoxItem>Task menu 1</ListBoxItem>
<ListBoxItem>Task menu 2</ListBoxItem>
<ListBoxItem>Task menu 3</ListBoxItem>
<ListBoxItem>Task menu 4</ListBoxItem>
<ListBoxItem>Task menu 5</ListBoxItem>
<ListBoxItem>Task menu 6</ListBoxItem>
<ListBoxItem>Task menu 7</ListBoxItem>
<Separator/>
</ListBox>
下面是我想要实现的示例:
不一定需要每个列表框项目都有图标,但主要是 header 选项、用户设置和管理等图标。
我只看到其他线程,其中人们有多个列,但这会创建两列不同的数据,我想有 1 列,但只需添加粗体的 headers 和更大的字体将不同的菜单彼此分开。
我正在为 WPF 使用 MaterialDesign 主题 - 无论它是否重要或相关...
谢谢!
编辑 1:正如我之前没有提到的 - 它不一定是列表框,我使用它是因为它在我用来编写菜单的代码中使用:
<materialDesign:DrawerHost
IsLeftDrawerOpen="{Binding ElementName=MenuToggleButton, Path=IsChecked}">
<materialDesign:DrawerHost.LeftDrawerContent>
<DockPanel MinWidth="220">
<ToggleButton
Style="{StaticResource MaterialDesignHamburgerToggleButton}"
DockPanel.Dock="Top"
HorizontalAlignment="Right"
Margin="16"
IsChecked="{Binding ElementName=MenuToggleButton, Path=IsChecked, Mode=TwoWay}"/>
<TextBox
x:Name="DemoItemsSearchBox"
Text="{Binding SearchKeyword, UpdateSourceTrigger=PropertyChanged}"
DockPanel.Dock="Top"
Margin="16, 4"
Width="200"
materialDesign:HintAssist.Hint="Search"
materialDesign:HintAssist.IsFloating="True"
materialDesign:TextFieldAssist.HasClearButton="True"
materialDesign:TextFieldAssist.HasOutlinedTextField="True"
materialDesign:TextFieldAssist.DecorationVisibility="Collapsed"
materialDesign:TextFieldAssist.TextFieldCornerRadius="4"/>
<ListBox
x:Name="ItemsListBox"
Margin="0 16 0 16"
Style="{StaticResource MaterialDesignNavigationPrimaryListBox}">
<ListBox.Resources>
<Style TargetType="ScrollBar" BasedOn="{StaticResource MaterialDesignScrollBarMinimal}"/>
</ListBox.Resources>
-- This is where the ListBox items would be --
</ListBox>
</DockPanel>
</materialDesign:DrawerHost.LeftDrawerContent>
<DockPanel>
<materialDesign:ColorZone
Padding="16"
materialDesign:ShadowAssist.ShadowDepth="Depth2"
Mode="PrimaryMid"
DockPanel.Dock="Top">
<DockPanel>
<StackPanel Orientation="Horizontal">
<ToggleButton
x:Name="MenuToggleButton"
Style="{StaticResource MaterialDesignHamburgerToggleButton}"
IsChecked="False"
Click="MenuToggleButton_OnClick"
AutomationProperties.Name="HamburgerToggleButton"/>
</StackPanel>
<materialDesign:PopupBox
DockPanel.Dock="Right"
PlacementMode="BottomAndAlignRightEdges"
StaysOpen="False">
<StackPanel>
<Grid Margin="10">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<TextBlock
Text="Light"
Margin="0 0 10 0"/>
<ToggleButton
x:Name="DarkModeToggleButton"
Click="MenuDarkModeButton_Click"
Grid.Column="1"/>
<TextBlock
Text="Dark"
Margin="10 0 0 0"
Grid.Column="2"/>
<TextBlock
Text="Enabled"
Margin="0 10 10 0"
Grid.Row="1"/>
<ToggleButton
x:Name="ControlsEnabledToggleButton"
Margin="0 10 0 0"
IsChecked="{Binding ControlsEnabled}"
Grid.Row="1"
Grid.Column="1"/>
</Grid>
<Separator/>
<Button
Content="Hello World"
/>
<Button
Content="Nice Popup"
/>
<Button
Content="Can't Touch This"
IsEnabled="False"/>
<Separator/>
<Button
Content="Goodbye"
/>
</StackPanel>
</materialDesign:PopupBox>
<TextBlock
HorizontalAlignment="Center"
VerticalAlignment="Center"
FontSize="22"
Margin="-152,0,0,0"
Text="Hi"/>
</DockPanel>
</materialDesign:ColorZone>
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<ScrollViewer
x:Name="MainScrollViewer"
Grid.Row="1"
materialDesign:ScrollViewerAssist.IsAutoHideEnabled="True"
HorizontalScrollBarVisibility="{Binding SelectedItem.HorizontalScrollBarVisibilityRequirement, FallbackValue=Disabled}"
VerticalScrollBarVisibility="{Binding SelectedItem.VerticalScrollBarVisibilityRequirement, FallbackValue=Disabled}" >
<ContentControl
Margin="{Binding MarginRequirement, FallbackValue=16}"/>
</ScrollViewer>
<materialDesign:Snackbar
x:Name="MainSnackbar"
MessageQueue="{materialDesign:MessageQueue}"
Grid.Row="1"/>
</Grid>
</DockPanel>
</materialDesign:DrawerHost>
可能不是我的最终答案,因为仍然使用演示中的 MVVM,但使用演示项目:
在MainWindowViewModel.cs中,在public ObservableCollection<DemoItem> DemoItems { get; }
public ListCollectionView DemoLCV { get; }
并添加
DemoLCV = new ListCollectionView(DemoItems);
DemoLCV.GroupDescriptions.Add(new PropertyGroupDescription("Name"));
之后
MoveNextCommand = new AnotherCommandImplementation(
_ =>
{
if (!string.IsNullOrWhiteSpace(SearchKeyword))
SearchKeyword = string.Empty;
SelectedIndex++;
},
_ => SelectedIndex < DemoItems.Count - 1);
然后在MainWindow.xaml中修改ListBox为:
<ListBox
x:Name="DemoItemsListBox"
Margin="0 16 0 16"
SelectedIndex="{Binding SelectedIndex}"
SelectedItem="{Binding SelectedItem, UpdateSourceTrigger=PropertyChanged}"
ItemsSource="{Binding DemoLCV}"
PreviewMouseLeftButtonUp="UIElement_OnPreviewMouseLeftButtonUp"
AutomationProperties.Name="DemoPagesListBox"
Style="{StaticResource MaterialDesignNavigationPrimaryListBox}">
<ListBox.Resources>
<Style TargetType="ScrollBar" BasedOn="{StaticResource MaterialDesignScrollBarMinimal}"/>
</ListBox.Resources>
<ListBox.GroupStyle>
<GroupStyle>
<GroupStyle.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</GroupStyle.HeaderTemplate>
</GroupStyle>
</ListBox.GroupStyle>
<ListBox.ItemTemplate>
<DataTemplate DataType="domain:DemoItem">
<TextBlock Text="{Binding Name}" Margin="24 4 0 4" AutomationProperties.AutomationId="DemoItemPage"/>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
这会将菜单项名称添加为每个项目的类别标题。
这只是为了演示这个概念。
要使用有用的标题扩展这个想法,将另一个 属性 添加到 DemoItem class,例如categoryName,并在 DemoItems collection.
初始化时用不同的类别填充它然后将此示例中的 DemoLCV.GroupDescriptions.Add(new PropertyGroupDescription("Name"));
更改为 DemoLCV.GroupDescriptions.Add(new PropertyGroupDescription("categoryName"));
以按您的类别分组。
然后您可以使用 ListBox.GroupStyle 中的 TextBlock 样式来根据需要显示标题。
稍后我将尝试弹出一个示例,并看看我是否可以想出一个 non-MVVM 选项。
对于硬编码 xaml、非 MVVM 解决方案,我会使用菜单而不是列表框。我通常只使用 ListBoxes 绑定到 data/items.
列表我们也可以用这种方法实现你的图标。
当然,风格随你喜欢,但如果你取出你的ListBox,你可以用Menu替换如下:
<ScrollViewer>
<Menu>
<Menu.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</Menu.ItemsPanel>
<Menu.Resources>
<Style TargetType="ScrollBar" BasedOn="{StaticResource MaterialDesignScrollBarMinimal}"/>
</Menu.Resources>
<TextBlock Text="Config" FontWeight="Bold" FontSize="20" IsEnabled="False" />
<Separator/>
<MenuItem>
<MenuItem.Header>
<DockPanel>
<materialDesign:PackIcon Kind="Settings" Margin="3" />
<TextBlock Text="Config item 1" Margin="3" />
</DockPanel>
</MenuItem.Header>
</MenuItem>
<MenuItem >
<MenuItem.Header>
<DockPanel>
<materialDesign:PackIcon Kind="Settings" Margin="3" />
<TextBlock Text="Config item 2" Margin="3" />
</DockPanel>
</MenuItem.Header>
</MenuItem>
<MenuItem >
<MenuItem.Header>
<DockPanel>
<materialDesign:PackIcon Kind="Settings" Margin="3" />
<TextBlock Text="Config item 3" Margin="3" />
</DockPanel>
</MenuItem.Header>
</MenuItem>
<MenuItem >
<MenuItem.Header>
<DockPanel>
<materialDesign:PackIcon Kind="Settings" Margin="3" />
<TextBlock Text="Config item 4" Margin="3" />
</DockPanel>
</MenuItem.Header>
</MenuItem>
<MenuItem >
<MenuItem.Header>
<DockPanel>
<materialDesign:PackIcon Kind="Settings" Margin="3" />
<TextBlock Text="Config item 5" Margin="3" />
</DockPanel>
</MenuItem.Header>
</MenuItem>
<MenuItem >
<MenuItem.Header>
<DockPanel>
<materialDesign:PackIcon Kind="Settings" Margin="3" />
<TextBlock Text="Config item 6" Margin="3" />
</DockPanel>
</MenuItem.Header>
</MenuItem>
<MenuItem >
<MenuItem.Header>
<DockPanel>
<materialDesign:PackIcon Kind="Settings" Margin="3" />
<TextBlock Text="Config item 7" Margin="3" />
</DockPanel>
</MenuItem.Header>
</MenuItem>
<TextBlock Text="Tasks" FontWeight="Bold" FontSize="20" />
<Separator/>
<MenuItem Header="Task item 1" />
<MenuItem Header="Task item 2" />
<MenuItem Header="Task item 3" />
<MenuItem Header="Task item 4" />
<MenuItem Header="Task item 5" />
<MenuItem Header="Task item 6" />
<MenuItem Header="Task item 7" />
</Menu>
</ScrollViewer>
这会产生(我只在上半部分添加了图标,以说明如何):