前言
ItemsControl和ListBox都可以用做列表,既然是列表,那么我们怎样获取列表点击的项呢。
ItemsControl、ListBox和ListView
结构的复杂程度从低到高依次为:
ItemsControl
=>ListBox
=>ListView
ListBox继承于ItemsControl,ListView继承于ListBox,所以后面的组件拥有前面组件的一切特性。
相同点:
- 这三个控件都是列表型控件,可以进行列表绑定(ItemsSource);
- 这三个控件均使用ItemsPresenter来展示列表信息;
不同点:
ItemsControl是不包含水平和垂直方向的滚动条的。ListBox和ListView有水平和垂直方向滚动条。
ListBox 继承于ItemsControl,增加了一个Selector对象,支持单选,ItemsControl中的Item是不支持选择的。
而ListBox中Item是支持选择,并且可以单选,多选。
ListView类似 Gridview ,支持多列。
ListBox很简单, 就一列。
ListBox点击列表项后就不能再触发点击事件,而ItemsControl压根就没有选中项,那么怎样处理呢?
自定义ListBox
实体类
1 | public class ZNotifyModel : INotifyPropertyChanged |
列表的对象
1 | public class ZListItemModel : ZNotifyModel |
页面的PageData对象
1 | public class UserCenterPageData : ZNotifyModel |
页面中调用
1 | internal UserCenterPageData pageData = new UserCenterPageData(); |
全局样式
1 | <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> |
样式转换器
背景颜色转换器
1 | public class ColorBgConverter : IValueConverter |
文字颜色转换器
1 | public class ColorTextConverter : IValueConverter |
页面
列表项(建议写在页面中,除非是通用的)
1 | <Window.Resources> |
ListBox
1 | <Grid Background="#f3f3f3"> |
代码
1 | private async void type_list_lb_SelectionChanged(object sender, SelectionChangedEventArgs e) |
如果想让选中的项能够再次点击
设置选中索引即可
1 | type_list_lb.SelectedIndex = -1; |
获取点击项
使用SelectionChanged回调(推荐)
重置选中索引
1 | <ListBox |
代码中
1 | private void UserAnsSelectionChanged(object sender, SelectionChangedEventArgs e) |
自定义ListBox
ListBox点击会选中,但是选中的项目不能再次点击,所以这里
自定义ListBox,当item选中后再重置为未选中
自定义ListBox
1 | using System.Windows; |
使用
1 | <views:ZListBox |
对应的
1 | private void UserAnsSelectionChanged(object sender, SelectionChangedEventArgs e) |
也可以简写为
1 | private void UserAnsSelectionChanged(object sender, SelectionChangedEventArgs e) |
使用Tag传值(推荐)
还有一种方式是绑定Tag
1 | <DataTemplate x:Key="LeftMenu"> |
代码中
1 | private void leftbar_item_Click(object sender, RoutedEventArgs e) |
注意:
这种方式获取到的是对应的值,可以通过值获取在列表中的索引。
ItemsControl、ListBox和ListView均支持该方式。
ItemsControl选中项索引(不推荐)
你可以在ItemsControl
的MouseLeftButtonDown
事件中获取点击的索引。
以下是一个示例:
XAML:1
2
3
4
5
6
7<ItemsControl ItemsSource="{Binding Items}" MouseLeftButtonDown="ItemsControl_MouseLeftButtonDown">
<ItemsControl.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" />
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
代码:1
2
3
4
5
6
7
8
9private void ItemsControl_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
// 获取点击的ItemsControl项
var clickedItem = ((FrameworkElement)e.OriginalSource).DataContext;
// 获取点击项的索引
int clickedIndex = ((ItemsControl)sender).Items.IndexOf(clickedItem);
Console.WriteLine($@"选中的项索引{clickedIndex}");
}
在这个示例中,MouseLeftButtonDown
事件会在用户点击ItemsControl
中的任何一个项时触发。通过获取事件参数中的OriginalSource
属性,可以确定实际点击的元素。
在这个示例中,我们将OriginalSource
强制转换为FrameworkElement
,以便我们可以访问其DataContext
(即ItemsControl
中的项)。
一旦我们得到了点击的项,我们可以使用IndexOf
方法获取其索引。
这种方式要注意:
选中项的模版中不能是Button,这样点击事件就被消费掉了。
查找控件方式(不推荐)
Item中添加Button,对Button添加事件,获取Button所在Item的Index
工具类
1 | using System; |
xaml
1 | <Window.Resources> |
代码
1 | private void toolbar_item_Click(object sender, RoutedEventArgs e) |
ListBox配置项
引用样式
1 | <ListBox |
其中
ItemsSource
是数据源跟每个Item有关的是
ItemContainerStyle
和ItemTemplate
ItemContainerStyle
:每一项的外部样式 比如设置选中的状态的边框ItemTemplate
:每一项的内部模板
Template
是所有项的外部容器
项容器样式
ItemContainerStyle
1 | <Style x:Key="ListBoxItemContainerStyle1" TargetType="ListBoxItem"> |
项模板
ItemTemplate
1 | <DataTemplate x:Key="recordDT"> |
列表外部容器
Template
是所有项的外部容器
比如能换行的
1 | <ControlTemplate x:Key="ListBoxTemplate" TargetType="{x:Type ListBox}"> |
不能换行的
1 | <ControlTemplate x:Key="ListBoxTemplateH" TargetType="{x:Type ListBox}"> |
注意
作为外部容器要添加
IsItemsHost="True"
属性。
ItemsControl配置项
ItemsControl常用于展示类且没有滚动条的场景。
比如4宫格,9宫格之类的。
1 | <ItemsControl |
模板及样式
1 | <Window.Resources> |
柱状图示例
其中ZGenericTypeConverter
参见 https://www.psvmc.cn/article/2019-12-30-wpf-start-03-mvvm.html
1 | <ListBox |
ItemsControl选中项最大化
有这样一个需求,我们想点击其中的项时,该项最大化,我们想到的方式是让其他项隐藏就行了。
1 | <ItemsControl |
其中整体容器的模板是
1 | <ControlTemplate x:Key="ZListGridTemplate" TargetType="{x:Type ItemsControl}"> |
因为可能是多列,所以选中项最大化时,我们要把列数设置为1。
隐藏其他项的时候设置ItemTemplate
的隐藏是不行的,我们要设置ItemContainerStyle
的隐藏
1 | <Style x:Key="ZItemContainerStyle" TargetType="ContentPresenter"> |
这点要注意
虽然我们在实时可视化树中看不到
ItemContainer
,但是它是占位置的,设置项的隐藏只能设置ItemContainerStyle
。设置
ItemTemplate
只会让项隐藏但是依旧占据位置。