WinForm数据展示组件ListView

一ListView

listView的五个视图模式

模式名称 形象比喻 适用场景 图标要求
Details (详细) Excel 表格 最常用!显示多列数据(如姓名、年龄、日期)。 使用 SmallImageList
LargeIcon (大图标) 桌面图标 像 Windows 桌面那样,大图配下面一行字。 使用 LargeImageList
SmallIcon (小图标) 控制面板 左右排列的小图标,文字在图标右边。 使用 SmallImageList
List (列表) 垂直名单 只有一列数据,自上而下排列。 使用 SmallImageList
Tile (平铺) 卡片 较大的图标,右侧显示多行简短信息。 通常用 Small/Medium

二常用属性

三常用事件

一、高频事件(★★★★★ / ★★★★☆)

开发必用,90% 的业务场景都会用到,必须优先掌握。

1. SelectedIndexChanged

  • 触发时机 :ListView 的选中项(高亮) 发生改变时(单选 / 多选、取消选中都会触发)
  • 核心作用:获取当前选中的项,执行选中后的业务逻辑
  • 常用场景:选中列表项后显示详情、启用 / 禁用功能按钮、加载对应子数据
  • 频次:★★★★★(最高频,无之一)
  • 小贴士 :多选时会多次触发,建议加 if(listView1.SelectedItems.Count > 0) 避免空值报错。

2. ItemActivate

  • 触发时机 :用户双击列表项 或 按Enter 键激活选中项时触发
  • 核心作用:处理项的「激活操作」(打开、编辑、查看详情)
  • 常用场景:双击列表项打开详情窗口、编辑数据
  • 频次:★★★★★(第二高频)
  • 优势 :比鼠标双击事件更适配 ListView,仅点击项生效,无需判断点击位置。

3. ColumnClick

  • 触发时机 :点击列表列头时触发
  • 核心作用:获取点击的列索引,实现列表排序
  • 常用场景:点击列头对数据升序 / 降序排序(详情视图必备)
  • 频次:★★★★☆

4. MouseClick

  • 触发时机 :鼠标左键 / 右键 / 中键点击 ListView任意位置时触发
  • 核心作用:获取鼠标坐标、按钮类型,实现精准点击逻辑
  • 常用场景:弹出右键菜单、点击空白处取消选中
  • 频次:★★★★☆
  • 对比 :比基础 Click 事件更强大(带鼠标参数),WinForm 开发优先用它。

二、中频事件(★★★☆☆)

特定场景常用,开启复选框、编辑标签、键盘操作时必备。

1. ItemChecked

  • 触发时机 :ListView复选框 选中状态改变后触发
  • 核心作用:获取所有勾选的项,执行批量操作
  • 常用场景:批量删除、批量选中、统计勾选数量
  • 频次:★★★★☆(复选框场景必用)
  • 前提 :必须先设置 listView1.CheckBoxes = true

2. ItemCheck

  • 触发时机 :复选框选中状态改变前触发
  • 核心作用验证 / 取消勾选操作(限制勾选数量、禁止勾选禁用项)
  • 常用场景:最多勾选 3 项、禁止勾选特定项
  • 频次:★★★☆☆
  • 关键 :可通过 e.NewValue = CheckState.Unchecked 强制取消勾选。

3. MouseDoubleClick

  • 触发时机 :鼠标双击 ListView任意位置(空白处也会触发)
  • 核心作用:自定义双击逻辑
  • 常用场景 :替代ItemActivate,精准控制双击生效区域
  • 频次:★★★☆☆
  • 推荐 :列表项激活优先用ItemActivate,空白双击用此事件。

4. BeforeLabelEdit / AfterLabelEdit

  • 触发时机 :列表项标签开始编辑前 / 编辑完成后
  • 核心作用:限制编辑内容、保存修改后的文本
  • 常用场景:允许用户直接修改列表项名称
  • 频次:★★★☆☆
  • 前提 :必须先设置 listView1.LabelEdit = true

5. KeyDown

  • 触发时机:键盘按键按下时触发
  • 核心作用:处理键盘快捷键
  • 常用场景 :按Delete删除选中项、Ctrl+A全选
  • 频次:★★★☆☆

三、低频事件(★★☆☆☆ 及以下)

极少使用,仅自定义 UI、特殊布局需求时用到。

1. 自定义绘制类

DrawItemDrawSubItemDrawColumnHeader

  • 触发时机:绘制项 / 子项 / 列头时触发
  • 核心作用:自定义 ListView 外观(改颜色、图标、样式)
  • 场景:美化界面、定制列表样式
  • 频次:★★☆☆☆
  • 前提:必须设置 listView1.OwnerDraw = true

2. 布局 / 状态类

LayoutSizeChangedScrollItemHover

  • 触发时机:布局改变、大小改变、滚动条滚动、鼠标悬停
  • 场景:自适应布局、悬停提示、滚动监听
  • 频次:★☆☆☆☆

3. 验证类

ValidatingValidated

  • 触发时机:控件验证时
  • 场景:极少用于 ListView
  • 频次:★☆☆☆☆

4.易混淆事件对比

事件组合 核心区别
SelectedIndexChanged vs ItemChecked 前者 =高亮选中 改变;后者 =复选框勾选改变(完全独立)
ItemActivate vs MouseDoubleClick 前者 = 仅双击 生效;后者 = 双击任意位置生效(推荐用前者)
ItemCheck vs ItemChecked 前者 =勾选前 (可取消);后者 =勾选后(仅处理结果)
  • 多选场景下,SelectedIndexChanged 会多次触发,务必加非空判断
  • 右键菜单必须用 MouseClick 事件,判断 e.Button == MouseButtons.Right
  • 用复选框→开 CheckBoxes = true,用标签编辑→开 LabelEdit = true
  • 自定义绘制→必须开 OwnerDraw = true,否则绘制事件不生效。

四常用代码操作

Details(详细信息)模式下,ListView 的操作主要围绕着"行(Item)"和"子项(SubItem)"展开。

1. 添加数据(最基础操作)

ListView 中,每一行被称为一个 ListViewItem。第一列是"本体",后面的列都是它的"子项"。

方法1
cs 复制代码
// 1. 创建一行,并设置第一列的内容(比如序号或姓名)
ListViewItem item = new ListViewItem("1"); 

// 2. 给这一行添加后续列的数据
item.SubItems.Add("张三"); // 第二列
item.SubItems.Add("25");   // 第三列

// 3. 把这一行正式塞进控件里
listView1.Items.Add(item);
方法2 字符串数组

直接用字符串数组一次性创建所有列。

cs 复制代码
listView1.View = View.Details;
listView1.Columns.Add("ID", 80);
listView1.Columns.Add("姓名", 120);
listView1.Columns.Add("年龄", 80);


listView1.Items.Add(new ListViewItem(new[] {"1","张飞","20"}));
方法3:循环添加多行(批量添加)

从数组、集合里循环加数据(最常用)

cs 复制代码
listView1.View = View.Details;
listView1.Columns.Add("ID", 80);
listView1.Columns.Add("姓名", 120);
listView1.Columns.Add("年龄", 80);

// 模拟多条数据
string[,] data = {
    { "1", "张三", "20" },
    { "2", "李四", "21" },
    { "3", "王五", "22" }
};
    for (int i = 0; i < data.GetLength(0); i++)
    {
        listView1.Items.Add(new ListViewItem(new[]
        {
            data[i,0],
            data[i,1],
            data[i,2]
        } ));
    }
方法4 :先创建 item,再批量加入(性能高)

先把所有行创建好,最后一次性加入。

cs 复制代码
 List<ListViewItem> listItems = new List<ListViewItem>();

listItems.Add(new ListViewItem(new[] { "2", "张三", "20" }));
listItems.Add(new ListViewItem(new[] { "3", "张四", "22" }));
//一次性加入
listView1.Items.AddRange(listItems.ToArray());//只接受数组

✅ 数据量大时界面不卡顿

方法5:绑定对象集合(高级用法,项目最常用)

把自己的类(如 User)自动转成 ListView 行。

cs 复制代码
public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
}

// 模拟数据源
var users = new List<User>
{
    new User{Id=1, Name="张三", Age=20},
    new User{Id=2, Name="李四", Age=21}
};

// 绑定到 ListView
foreach (var user in users)
{
    // ✅ 用 ListViewItem 包装数组,Add 方法就能正确识别了
    listView1.Items.Add(new ListViewItem(new[]
    {
        user.Id.ToString(),
        user.Name,
        user.Age.ToString()
    }));
}

✅ 真正项目开发必用

2.带图标添加

如果你绑定了 SmallImageList,代码里只需要多传一个索引号:

cs 复制代码
// 参数1:文字;参数2:ImageList 里的图片编号 (从 0 开始)
ListViewItem item = new ListViewItem("1", 0); 
item.SubItems.Add("李四");
item.SubItems.Add("20");
listView1.Items.Add(item);

3. 获取用户选中的是哪一行

当用户点击了某一行,你可能想知道他选的是谁。注意:ListView 默认支持多选,所以选中的结果是一个集合

cs 复制代码
// 判断用户是否真的选中了东西
if (listView1.SelectedItems.Count > 0)
{
    // 获取选中的第一行
    ListViewItem selectedRow = listView1.SelectedItems[0];
    
    // 获取这一行的文字(第一列)
    string id = selectedRow.Text;
    
    // 获取第二列(姓名)
    string name = selectedRow.SubItems[1].Text;
    
    MessageBox.Show("你选中了:" + name);
}

4. 修改和删除数据

修改:直接定位到某一行进行赋值。

cs 复制代码
// 把选中的那一行的姓名改掉
listView1.SelectedItems[0].SubItems[1].Text = "王五";

删除选中行

cs 复制代码
foreach (ListViewItem item in listView1.SelectedItems)
{
    listView1.Items.Remove(item);
}

清空全表

cs 复制代码
listView1.Items.Clear();

ListViewItem

1.是什么

ListViewItemWinForms 里专门给 ListView 控件用的 "行对象" ,相当于 ListView 里的一行数据。

  • 它本身不是一个 "列表容器",而是用来表示列表里的单个条目
  • 它的核心作用:
    • 存这一行的主文本(你代码里的 "文本"
    • 存这一行的图标索引、子项(SubItems)、分组信息、状态(选中 / 高亮)等
    • ListView 和它的数据行之间的桥梁
2.本质

ListViewItem是.NET系统提取写好的一个类,它提取定义好了一堆属性,专门用来存和ListView行相关的信息:

  • Text:显示的文字
  • ImageIndex:图标索引
  • SubItems:多列数据
  • Tag:附加数据
  • FontForeColor:样式
  • Group:归属哪个分组
  • 不负责存储整个列表 ,只负责存储一行的所有信息

  • 它不存储列表,列表存在别的地方

    • 所有行 的容器是:listView.Items
    • 一行多列 的容器是:item.SubItems

UI框架交互与数据模型设计

核心架构思想

这是职业程序员的"基本功"。它讲究的是"分工明确":

  • M (Model - 模型) :就是你的 User 类。它只负责存数据,不管界面长啥样。

  • V (View - 视图) :就是你的 ListView。它只负责显示,不存数据。

  • C/VM (控制器/视图模型):就是你写的那些逻辑,负责把模型里的数据"翻译"给视图看。

为什么要分层? 假设哪天老板说:"咱们不用 ListView 了,换成别的控件。"如果你用了高级做法,你只需要改"翻译"的那部分代码,你的 User 类和业务逻辑完全不用动。

特性 普通做法 (手动改界面) 高级做法 (数据驱动)
工作量 每次改数据都要手动改界面代码 只管改数据,界面自动更新
准确性 容易漏掉界面更新,导致数据不一致 仓库和货架永远同步
扩展性 想加个"保存到 JSON"功能会很乱 只需要在仓库变动时加一行保存代码

第一步:定义你的"商品"是什么(User类)

先告诉电脑,"用户"长什么样。

cs 复制代码
public class User
{
    public int Id { get; set; }      // 序号
    public string Name { get; set; } // 姓名
    public int Age { get; set; }     // 年龄
}

第二步:建立"智能仓库" (BindingList)

BindingList<User> 和普通 List 的区别在于:它自带"报警器"。

cs 复制代码
// 在窗体类里声明
BindingList<User> users = new BindingList<User>();

作用:

  • List<User>(普通列表) 你可以把它想象成一个普通仓库

    • 你可以往里面放东西(Add)、拿东西(Remove
    • 但仓库自己不会主动通知别人"我变了!"
    • 比如你在代码里加了个用户,界面上的表格 / 列表控件根本不知道,不会自动刷新
  • BindingList<User>(绑定列表) 它就是个带自动报警器的智能仓库

    • 除了放东西、拿东西,它自带一个ListChanged事件(就是 "报警器")
    • 只要你往里面加 / 删 / 改数据,它就会自动触发这个事件,通知绑定它的界面控件:"喂,我变了,你快更新显示!"

第三步:连接"仓库"与"货架" (数据变更事件)

这是最关键的逻辑,我们要告诉程序:一旦仓库有变动,就去刷新货架。

监听变动

cs 复制代码
// 在窗体初始化时绑定
users.ListChanged += Users_ListChanged;

编写"刷货架"的方法 (RenderRow)

cs 复制代码
private void Users_ListChanged(object sender, ListChangedEventArgs e)
{
    // 简单粗暴的方法:先清空货架,再根据仓库里的东西重新摆
    listView1.Items.Clear();
    foreach (var user in users)
    {
        ListViewItem item = new ListViewItem(user.Id.ToString());
        item.SubItems.Add(user.Name);
        item.SubItems.Add(user.Age.ToString());
        listView1.Items.Add(item);
    }
}

第四步:操作"仓库"即可

有了上面的自动化连接,你以后再也不用写 listView1.Items.Add 这种代码了。

添加数据

cs 复制代码
// 你只管往仓库里加人,界面会自动跳出新行!
users.Add(new User { Id = 1, Name = "张三", Age = 25 });

删除数据

cs 复制代码
// 你只管从仓库里移除,界面会自动消失那一行!
users.RemoveAt(0);
相关推荐
程序设计实验室6 小时前
Spark.NET:一个试图把 Django / Rails 式开发体验带回 .NET 世界的全栈 Web 框架。
c#
byoass7 小时前
智巢AI知识库深度解析:企业文档管理从大海捞针到精准狙击的进化之路
开发语言·网络·人工智能·安全·c#·云计算
njsgcs11 小时前
solidworks自动标注折弯4 无向图 c#
开发语言·c#·solidworks
我是唐青枫12 小时前
C#.NET ThreadLocal 深入解析:线程独享数据、性能收益与实战边界
c#·.net
JQLvopkk14 小时前
C# 工业级上位机:交互实战
开发语言·c#·交互
kingwebo'sZone15 小时前
PdfiumViewer使用权限控制期操作按钮(PdfViewer其实也可以完整兼容)
c#
张小俊_15 小时前
WPF 跨线程 UI 更新与硬编码赋值引发的 Bug 排查
c#·bug·wpf
無斜15 小时前
【CAPL实用开发】--- CAPL调用 .NET DLL
开发语言·c#·capl·canoe
puamac16 小时前
UcTabWindow 布局多tab,加载编辑器和资源管理器等自定义控件
c#·编辑器·datagridview