C#ListView的单元格支持添加基本及自定义任意控件

功能说明

使用ListView时,希望可以在单元格显示图片或其他控件,发现原生的ListView不支持,于是通过拓展,实现ListView可以显示任意控件的功能,效果如下:
在这里插入图片描述

实现方法

本来想着在单元格里面实现控件的自绘的,但是没找到办法,最后是通过在单元格的表面显示对应控件的,浮于表面达到目的。
实现要点如下:

  • ListView需要设置OwnerDraw=true,并重载自绘函数OnDrawColumnHeaderOnDrawItemOnDrawSubItem
  • 支持按单元格添加对应的控件,其Parent设置为列表ListView
  • 列表界面调整后,包括大小、列表头、滚动等,需重新绘制单元格的控件

实现源码

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace MyListView.Ui
{
    #region ListViewEx
    public class ListViewEx : ListView
    {
        #region 内部属性
        /// <summary>
        /// 存放单元格控件
        /// </summary>
        List<Control> _CellControls = new List<Control>();
        /// <summary>
        /// 界面是否发生变化
        /// </summary>
        bool IsViewChanged { get; set; }
        #endregion

        #region 方法
        /// <summary>
        /// 构造函数
        /// </summary>
        public ListViewEx()
        {
            #region 初始化
            #endregion

            #region 事件
            this.ColumnReordered += ColumnWidthChangedHandler;
            this.ColumnWidthChanged += ColumnWidthChangedHandler;
            #endregion
        }

        /// <summary>
        /// 添加控件
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="row"></param>
        /// <param name="col"></param>
        /// <param name="c"></param>
        /// <returns></returns>
        public T Add<T>(int row, int col, T c) where T : Control
        {
            if(row >= this.Items.Count || col >= this.Columns.Count)
            {
                return null;
            }

            var index = (row * this.Columns.Count) + col;
            for (var i = _CellControls.Count; i <= index; i++)
            {
                _CellControls.Add(null);
            }
            var oc = _CellControls[index];
            if (oc != null)
            {
                oc.Dispose();
            }
            OwnerDraw = true;
            IsViewChanged = true;
            c.Parent = this;
            _CellControls[index] = c;
            return c;
        }

        /// <summary>
        /// 设置行高度
        /// </summary>
        /// <param name="height"></param>
        public void SetItemHeight(int height)
        {
            if(this.SmallImageList == null)
            {
                this.SmallImageList = new ImageList();
            }
            this.SmallImageList.ImageSize = new Size(1, height);
        }
        #endregion

        #region 内部函数
        void ColumnWidthChangedHandler(object s, EventArgs e)
        {
            IsViewChanged = true;
        }
        /// <summary>
        /// 显示控件到目标单元格
        /// </summary>
        /// <param name="c"></param>
        /// <param name="item"></param>
        void ShowCellControl(Control c, ListViewItem.ListViewSubItem item)
        {
            int margin = 2;
            c.Text = item.Text;
            c.Visible = true;
            c.Bounds = new Rectangle(item.Bounds.X + margin,
                item.Bounds.Top + margin,
                item.Bounds.Width - 2 * margin,
                item.Bounds.Height - 2 * margin);
        }

        /// <summary>
        /// 显示单元格控件
        /// </summary>
        /// <param name="e"></param>
        /// <returns></returns>
        Control GetCellControl(DrawListViewSubItemEventArgs e)
        {
            Control c = null;
            #region 获取控件
            var index = (e.ItemIndex * this.Columns.Count) + e.ColumnIndex;
            if (index >= _CellControls.Count)
            {
                return null;
            }
            c = _CellControls[index];
            #endregion
            return c;
        }


        protected override void WndProc(ref Message m)
        {
            #region 事件定义
            const int WM_SIZE = 0x0005;
            const int WM_PAINT = 0x000F;
            const int WM_HSCROLL = 0x114;
            const int WM_VSCROLL = 0x115;
            const int WM_MOUSEWHEEL = 0x020A;
            #endregion

            #region 重绘显示控件
            if (m.Msg == WM_PAINT && IsViewChanged)
            {
                if(this.Columns.Count > 0)
                {
                    for (var i = 0; i < _CellControls.Count; i++)
                    {
                        var cell_control = _CellControls[i];
                        if (cell_control == null)
                        {
                            continue;
                        }
                        cell_control.Visible = false;
                        var row = i / this.Columns.Count;
                        var col = i % this.Columns.Count;
                        if(row >= Items.Count || col >= this.Columns.Count)
                        {
                            continue;
                        }
                        var item = this.Items[row];
                        if(item.Bounds.Y < 0 || item.Bounds.Y >= this.Height)
                        {
                            continue;
                        }
                        if(item.SubItems[col].Bounds.X < 0 || item.SubItems[col].Bounds.X >= this.Width)
                        {
                            continue;
                        }
                        ShowCellControl(cell_control, item.SubItems[col]);
                    }
                    IsViewChanged = false;
                }
            }
            else if(m.Msg == WM_HSCROLL || m.Msg == WM_VSCROLL || m.Msg == WM_MOUSEWHEEL || m.Msg == WM_SIZE)
            {
                IsViewChanged = true;
            }
            #endregion
            base.WndProc(ref m);
        }

        protected override void OnDrawColumnHeader(DrawListViewColumnHeaderEventArgs e)
        {
            e.DrawDefault = true;
        }

        protected override void OnDrawItem(DrawListViewItemEventArgs e)
        {
            // 已经在OnDrawSubItem处理过了
            // e.DrawText(TextFormatFlags.Default);
        }

        protected override void OnDrawSubItem(DrawListViewSubItemEventArgs e)
        {
            if(GetCellControl(e) != null)
            {
                return;
            }
            else
            {
                e.DrawDefault = true;
            }
        }
        #endregion
    }
    #endregion
}

测试代码

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace MyListView
{
    public partial class Form1 : Form
    {
        #region 函数
        public Form1()
        {
            #region 布局初始化
            InitializeComponent();
            var lv = new Ui.ListViewEx()
            {
                Dock = DockStyle.Fill,
                View = View.Details,
                GridLines = true,
            };
            this.Controls.Add(lv);
            var headers = new string[] { "序号", "名称", "年龄", "住址", "荣誉", "岗位", "头像" };
            foreach(var v in headers)
            {
                lv.Columns.Add(v, 100, HorizontalAlignment.Center);
            }
            lv.SetItemHeight(40);
            for(var i=0; i<50; i++)
            {
                var lvi = lv.Items.Add($"数据{i + 1}");
                for(var j=1; j<lv.Columns.Count; j++)
                {
                    lvi.SubItems.Add($"数据{i + 1}-{j}");
                    switch(j)
                    {
                        case 1:
                            lv.Add(i, j, new Label());
                            break;
                        case 2:
                            lv.Add(i, j, new Button());
                            break;
                        case 3:
                            lv.Add(i, j, new TextBox()
                            {
                                Font = new Font("宋体", 18)
                            });
                            break;
                        case 4:
                            lv.Add(i, j, new ComboBox()
                            {
                                Font = new Font("宋体", 18)
                            });
                            break;
                        case 6:
                            {
                                var pic = lv.Add(i, j, new PictureBox());
                                pic.Image = LoadImage($"logo{i%7}.jpg");
                                pic.SizeMode = PictureBoxSizeMode.StretchImage;
                            }
                            break;
                    }
                }
            }
            #endregion
        }

        Image LoadImage(string name)
        {
            var file = Path.GetFullPath(Path.Combine(@"..\..\Data\IMG", name));
            if(!File.Exists(file))
            {
                return null;
            }
            return Image.FromFile(file);
        }
        #endregion
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/730395.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

消息队列kafka中间件详解:案例解析(第10天)

系列文章目录 1- 消息队列&#xff08;熟悉&#xff09;2- Kafka的基本介绍&#xff08;掌握架构&#xff0c;其他了解&#xff09;3- Kafka的相关使用&#xff08;掌握kafka常用shell命令&#xff09;4- Kafka的Python API的操作&#xff08;熟悉&#xff09; 文章目录 系列文…

拒绝吸烟,远离慢阻肺——朗格力复合营养素助力守护肺部健康

#肺科营养#朗格力#班古营养#复合营养素#肺部营养#肺部健康# 你知道吗?慢阻肺这一疾病在我国的患者数量已突破亿级大关,尤其在40岁以上的成年人中,平均每7个人中就有1位可能受其困扰。然而,很多人对慢阻肺的严重性认识不足,常常将其视为一种普通的咳嗽或喘息,忽视了它潜在的危…

ChatGPT 提示词技巧一本速通

目录 一、基本术语 二、提示词设计的基本原则 三、书写技巧 2.1 赋予角色 2.2 使用分隔符 2.2 结构化输出 2.3 指定步骤 2.4 提供示例 2.5 指定长度 2.6 使用或引用参考文本 2.7 提示模型进行自我判断 2.8 思考问题的解决过程 ​编辑 2.10 询问是否有遗漏 2.11 …

Consul 如何删除不需要的服务

一、找到需要删除的id 二、打开postman 使用put请求 http://ip:port/v1/agent/service/deregister/mc-admin-192-168-0-182-8084三、区域如果要验证输入验证

倍思突破氮化镓快充技术,为用户带来安全舒适体验

氮化镓,这个化学式为GaN的化合物,其高热稳定性和化学稳定性使其在多种极端环境中都能保持优良的性能,从而为其在电子器件领域的应用奠定了坚实的基础。 2018年前后开始,氮化镓快充充电器进入国内市场。作为第三代半导体材料的代表,氮化镓具有宽禁带的特性,其禁带宽度远大于传统…

芋道源码 yudao-cloud 、Boot 文档,开发指南 看全部,破解[芋道快速开发平台 Boot + Cloud]

1、文档全部保存本地部署查看&#xff0c;真香 文档已抓取最新版本&#xff0c;2024.06.21。【唯一遗憾&#xff0c;表结构到2024.04月&#xff0c;已被限制放到知识星球】会员中心&#xff0c;支付中心&#xff0c;CRM&#xff0c;ERP&#xff0c;商城&#xff0c;公众号运行…

代理网络基础设施 101:增强安全性、速度和可扩展性

编辑代理网络在现代网络架构中发挥着重要作用&#xff0c;充当管理和重新路由数据流的中介。它们处理的数据可以是各种类型&#xff0c;包括搜索查询和潜在的敏感客户信息&#xff0c;这凸显了它们在数据安全方面的作用。 然而&#xff0c;代理的好处不仅限于安全性。它们为用…

二分练习题(C. Earning on Bets)

二分练习题&#xff08;C. Earning on Bets&#xff09; 原题链接&#xff1a;点击此处 Earning on Bets 题面翻译 有人提议让您玩一个游戏。在这个游戏中&#xff0c;有 n n n 种可能的结果&#xff0c;对于每一种结果&#xff0c;您都必须下注一定整数的硬币。如果 i …

微信小程序UI组件库合集

文章目录 前言参考地址推荐组件库1.官方WeUI&#xff08;建议使用☆☆☆☆&#xff09;2.ColorUI&#xff08;广告很多&#xff0c;不建议使用&#xff09;3.vantUI又名&#xff1a;ZanUI&#xff08;操作简单&#xff0c;建议使用☆☆☆☆&#xff09;4.MinUI&#xff08;比较…

摊牌了,我不装了~各种Amazon Bedrock小样儿、试用装,今天免费!

探索世界顶级的大模型、智能体、文生图、对话机器人……新手&#xff1f;还是专家&#xff1f;加入我们&#xff0c;解锁精彩内容&#xff1a; l 初体验&#xff1a;在 Amazon Bedrock Playground 直接调用强大的大模型&#xff0c;点亮生成式AI技能树。 l 文生图&#xff1a…

(vue3)基于vite+vue3+element-plus项目创建

(vue3)基于vitevue3element-plus项目创建 vue.js官方中文文档&#xff1a;https://cn.vuejs.org/guide/quick-start.html vite官方中文文档&#xff1a;https://cn.vitejs.dev/guide/ element-plus官网&#xff1a;https://element-plus.org/zh-CN/guide/installation.html 第…

python学习笔记-10

面向对象编程-下 1.私有化属性 语法&#xff1a;两个下划线开头&#xff0c;声明该属性为私有&#xff0c;不能在类的外部被使用或直接访问。 使用私有化属性的场景&#xff1a; 1.把特定的一个属性隐藏起来&#xff0c;不让类的外部进行直接调用。 2.不让属性的值随意改变。…

微服务开发与实战Day08 - Elasticsearch

一、初始Elasticsearch 高性能分布式搜索引擎 1. 认识和安装 1.1 认识 Lucene是一个Java语言的搜索引擎类库&#xff0c;是Apache公司的顶级项目&#xff0c;由DougCutting于1999年研发。官网地址&#xff1a;Apache Lucene - Welcome to Apache Lucene Lucene的优势&…

RabbitMQ(七)Shovel插件对比Federation插件

文章目录 Shovel和Federation的主要区别&#xff08;重点&#xff09;一、启用Shovel插件二、配置Shovel三、测试1、测试计划2、测试效果发布消息源节点目标节点 Shovel和Federation的主要区别&#xff08;重点&#xff09; • Shovel更简洁一些 • Federation更倾向于跨集群使…

基于JSP技术的个性化影片推荐系统

开头语&#xff1a;你好呀&#xff0c;我是计算机学长猫哥&#xff01;如果有相关需求&#xff0c;文末可以找到我的联系方式。 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;JSPServlet 工具&#xff1a;MyEclipse、Tomcat、MySQL 系统展示 首页 …

Golang——channel

channel是Go在语言层面提供的协程间的通信方式。通过channel我们可以实现多个协程之间的通信&#xff0c;并对协程进行并发控制。 使用注意&#xff1a; 管道没有缓冲区时&#xff0c;从管道中读取数据会阻塞&#xff0c;直到有协程向管道中写入数据。类似地&#xff0c;向管道…

内网安全[3]-代理Socks协议路由不出网后渗透通讯CS-MSF控制上线

1.环境 隧道技术: 隧道技术是一类网络协议&#xff0c;它是一种数据包封装技术&#xff0c;它将原始IP包&#xff08;其报头包含原始发送者和最终目的地&#xff09;封装在另外一个数据包&#xff08;称为封装的IP包&#xff09;的数据净荷中进行传输&#xff0c;使用隧道的原…

V4和V6双栈处理

现进行双栈 对R1 对R2 对R3 对R4 路由地址配完&#xff0c;起协议 然后起ripng&#xff0c;在R2&#xff0c;R3&#xff0c;R4上都宣告一下 然后在PC1和PC2上都手动配置一下就可以了

第1章 MySQL数据库概述

1.1 基本概念 数据库是什么&#xff1f; 存储数据的地方 DB&#xff1a;数据库&#xff08;Database&#xff09; 为什么要用数据库&#xff1f; 因为应用程序产生的数据是在内存中的&#xff0c;如果程序退出或者是断电了&#xff0c;则数据就会消失。使用数据库是为了…

CVPR上新 | 从新视角合成、视频编解码器、人体姿态估计,到文本布局分析,微软亚洲研究院精选论文

编者按&#xff1a;欢迎阅读“科研上新”栏目&#xff01;“科研上新”汇聚了微软亚洲研究院最新的创新成果与科研动态。在这里&#xff0c;你可以快速浏览研究院的亮点资讯&#xff0c;保持对前沿领域的敏锐嗅觉&#xff0c;同时也能找到先进实用的开源工具。 本周&#xff0…