如何在 WPF 列表框中添加标题
Posted
技术标签:
【中文标题】如何在 WPF 列表框中添加标题【英文标题】:How to add headers in WPF ListBox 【发布时间】:2021-12-18 17:58:40 【问题描述】:如何在 WPF 的 ListBox 中添加标题?我有下面的代码,并且“标题 - ..”文本在哪里我想要下面所有项目的标题/组名称,直到下一个标题:
<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>
以下是我想要实现的示例:
它不一定需要每个列表框项都有图标,但主要是选项、用户设置和管理等标题。
我只看到人们有多个列的其他线程,但这会创建具有不同数据的两列,我希望有 1 列,但只需添加粗体标题和更大的字体以将不同的菜单彼此分开.
我正在为 WPF 使用 MaterialDesign 主题 - 无论它是否重要或相关...
谢谢!
编辑 1:正如我之前没有提到的 - 它不一定是 ListBox,我使用它,因为它在我用来编写菜单的代码中使用:
<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>
【问题讨论】:
您能添加一张您想要达到的目标的图片吗? @DenisSchaf 完成。 必须是 ListBox 吗? (您可以使用 ListBox 实现 - 我会使用绑定和 ListBox.GroupStyle - 但菜单可能更适合您想要实现的目标) @SimonEvans 不,不一定是 ListBox,只要能在我的<materialDesign:DrawerHost>
中工作即可
【参考方案1】:
对于硬编码的 xaml、非 MVVM 解决方案,我会使用菜单而不是列表框。我通常只使用 ListBoxes 来绑定数据/项目列表。
我们也可以用这种方法实现你的图标。
很明显,你喜欢的风格,但如果你拿出你的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>
这会产生(我只在上半部分添加了图标,以说明如何):
【讨论】:
这工作得很好——我唯一的问题是是否可以像 ListBox 一样过滤<menu>
?如果没有,也不是问题,只是想知道它是否适用于该解决方案的任何未来用途。
Menu 确实支持绑定到 ItemsSource,因此如果通过绑定完成,您可以在您的集合上应用过滤器,但我认为该控件没有任何直接过滤选项。您始终可以附加到文本框的 KeyUp 事件,然后在后面的代码中适当地设置菜单项的可见性。
我试过这个private void ItemsSearchBox_TextChanged(object sender, TextChangedEventArgs e) foreach(MenuItem menuItem in ItemsMenu.Items) var menuItemText_split = menuItem.ToString().Split(' ',1); if (menuItemText_split[1].StartsWith(ItemsSearchBox.Text)) menuItem.Visibility = Visibility.Collapsed;
问题是我们的菜单不仅包含MenuItems
,还包含TextBlock
和Separator
。
在类型对象而不是 MenuItem 上循环,然后使用 GetType 测试 MenuItems【参考方案2】:
可能不是我的最终答案,因为仍然使用演示中的 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 类添加另一个属性,例如categoryName,并在初始化 DemoItems 集合时使用不同的类别填充它。
然后将本例中的 DemoLCV.GroupDescriptions.Add(new PropertyGroupDescription("Name"));
更改为 DemoLCV.GroupDescriptions.Add(new PropertyGroupDescription("categoryName"));
以按您的类别分组。
然后您可以在 ListBox.GroupStyle 中使用 TextBlock 的样式来根据需要显示标题。
稍后我将尝试弹出一个示例,并看看我是否可以提出非 MVVM 选项。
【讨论】:
以上是关于如何在 WPF 列表框中添加标题的主要内容,如果未能解决你的问题,请参考以下文章