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

news2024/11/27 9:48:08

功能说明

使用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
    }
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python-日历库calendar

目录 打印日历 基本日历类Calendar TextCalendar类 HTMLCalendar类 打印日历 设置日历每周开始日期(周几) import calendarcalendar.setfirstweekday(calendar.SUNDAY) # 设置日历中每周以周几为第一天显示 打印某年日历 print(calendar.calendar(2024, w2, l1, c6, m…

事实证明:企业级中后台框架,大厂还是主角,小厂打酱油。

提及中后台框架&#xff0c;你或许能够想到antdesign、arcodesign还有fusion等等&#xff0c;这些都是背靠大厂&#xff0c;是市场的主角&#xff0c;而一些小厂框架往往是扮演者陪太子读书的角色。本文将给大家分享市面上有哪些大厂的中后台框架&#xff1f;为什么大厂要开源自…

MySQL修改用户权限(宝塔)

在我们安装好的MySQL中&#xff0c;很可能对应某些操作时&#xff0c;不具备操作的权限&#xff0c;如下是解决这些问题的方法 我以宝塔创建数据库为例&#xff0c;创建完成后&#xff0c;以创建的用户名和密码登录 这里宝塔中容易发生问题的地方&#xff0c;登录不上去&#…

29. 透镜阵列

导论&#xff1a; 物理传播光学&#xff08;POP&#xff09;不仅可以用于简单系统&#xff0c;也可以设计优化复杂的光学系统&#xff0c;比如透镜阵列。 设计流程&#xff1a; 透镜阵列建模 在孔径类型中选择“入瞳直径”&#xff0c;并输入2 在视场设定中。设置一个视场&…

求二叉树最大深度-二叉树

104. 二叉树的最大深度 - 力扣&#xff08;LeetCode&#xff09; 1、用层序遍历&#xff0c;一层层遍历 class Solution { public:int maxDepth(TreeNode* root) {if(root nullptr)return 0;vector<TreeNode*> que;que.push_back(root);int res 0;//记层数while(!que.e…

【GIS技术】Shp矢量图斑数据的四至点坐标或四至坐标计算

经常有从事gis相关、地信相关行业的朋友或者是需要对图斑矢量进行四至坐标的计算的时候&#xff0c;按照各类搜索引擎或者是教学文章中的教学步骤求出来的四至坐标是错的。特别是比如林业图斑求四至点、农田四至点等等。 错的原因在于很多文章中教学的四至坐标实际上指的是图斑…

带你走进CCS光源——环形低角度光源LDR2-LA系列

机器视觉系统中&#xff0c;光源起着重要作用&#xff0c;不同类型的光源应用也不同&#xff0c;选择合适的光源成像效果非常明显。今天我们一起来看看CCS光源——工业用环形低角度光源LDR2-LA系列。 LDR2-LA系列 采用柔性基板&#xff0c;创造最佳倾斜角度。 通过从低角度向…

目标检测——室内服务机器人LifelongSLAM数据集

引言 亲爱的读者们&#xff0c;您是否在寻找某个特定的数据集&#xff0c;用于研究或项目实践&#xff1f;欢迎您在评论区留言&#xff0c;或者通过公众号私信告诉我&#xff0c;您想要的数据集的类型主题。小编会竭尽全力为您寻找&#xff0c;并在找到后第一时间与您分享。 …

scratch3编程01-山地足球+射击游戏

目录 一&#xff0c;山地足球 1&#xff0c;基础&#xff08;需要的素材&#xff09; 1&#xff09;使用“重复执行直到”语句块 2&#xff09;使用“如果那么否则”语句 2&#xff0c;效果 3&#xff0c;sb3文件 一&#xff0c;击败小怪兽 1&#xff0c;基础&#xff0…

gsoap2.8交叉编译方法(详细、亲测可用)

环境搭建 交叉编译器安装&#xff0c;过程略。 注意&#xff1a;如果要使用脚本配置环境变量&#xff0c;在运行脚本时&#xff0c;应该使用 . /绝对路径的方式&#xff0c;而不是直接/绝对路径或者./绝对路径&#xff0c;否则会导致配置失败。&#xff08;亲测如此&#xff0…

《失败的逻辑》|别再无效复盘了!学会认清每一次失败的必然性

为什么铁路信号系统工作正常时&#xff0c;列车仍然会发生撞车事故&#xff1f; 为什么所有操作人员都警觉地坚守着工作岗位&#xff0c;核反应堆依然会发生灾难性的熔化事故&#xff1f; 为什么我们制定得甚好的那么多专业和个人计划&#xff0c;会如此频繁地出岔子&#xff1…

让工业智能更简单

聚焦平台&#xff0c;深耕行业&#xff0c;蓝卓通过工业互联网平台赋能工厂数字化转型。 supOS是蓝卓自主研发的中国首个自主知识产权的工业操作系统&#xff0c;蓝卓数字科技有限公司总经理谭彰把它称为“嵌入到工厂内部的一个工业安卓系统”。如果把工厂看作智能手机&#x…

线下教育暑假班招生:教育行业营销短信群发平台是不二之选!

确实&#xff0c;对于线下教育暑假班的招生工作来说&#xff0c;利用教育行业营销短信群发平台是一个高效且实用的策略。以下是一些关于使用此类平台的好处&#xff1a; 1.快速传播&#xff1a;短信具有即时到达的特性&#xff0c;可以确保信息在第一时间传递给目标受众。这对…

汽车陪练app开发,安全上路,无忧前行

驾考作为我国报名人数最多的考试&#xff0c;每年都有无数人拿到驾照。但是这其中又有很多人望“本”兴叹&#xff0c;不敢上路。为此&#xff0c;试后练车成为驾考人熟练上路不可或缺的一环。大规模的需求与科技的不断发展&#xff0c;对汽车陪练行业提出了新的需求与挑战&…

代码随想录算法训练营第六十三天 | 42. 接雨水、84.柱状图中最大的矩形

42. 接雨水 文字讲解&#xff1a;代码随想录 视频讲解&#xff1a;单调栈&#xff0c;经典来袭&#xff01;LeetCode:42.接雨水_哔哩哔哩_bilibili 解题思路 思路一&#xff1a;单调栈 我们要找到矩形作为底时&#xff0c;左边和右边第一个比它大的元素 1.使用单调栈内元素的…

2024年退休金上调3%后一个扎心的现实。。。

个人原创&#xff0c;手写码字1050 你好&#xff0c;我是郭震 今天正式宣布&#xff0c;退休养老金上调3%&#xff1a; 1 现状分析 我对此做一些分析&#xff0c;从数据中帮助老铁们看清一些事实。 根据去年数据&#xff0c;农村老人1.2亿&#xff1a; 去年数据显示&#xff0c…

宝藏速成秘籍(8)基数排序法

一、前言 1.1、概念 基数排序法是一种非比较性的排序算法&#xff0c;通过将待排序的元素分割成独立的位数&#xff0c;然后按照每个位数的值进行排序。它可以用于对整数或字符串等数据类型进行排序。 1.2、排序步骤 确定待排序元素的位数&#xff0c;并根据最大位数创建对应…

LiveCharts2:简单灵活交互式且功能强大的.NET图表库

前言 之前的文章中提到过ScottPlot、与oxyplot&#xff0c;这两个是比较常用的.NET图表库&#xff0c;今天介绍一款新的.NET图表库&#xff1a;LiveCharts2。 LiveCharts2介绍 LiveCharts2 是一个现代化的数据可视化库&#xff0c;用于创建动态和交互式图表&#xff0c;支持…

分数限制下,选好专业还是选好学校

目录 1.概述 1.1.综合考虑 1.2.个人经验分享 2.专业解析 2.1. 计算机科学与技术 2.2. 英语 2.3. 法学 2.4.专业VS学校 2.5.建议 3.名校效应分析 3.1. 名校声誉&#xff08;品牌效应&#xff09; 3.2. 资源获取 3.3. 学术氛围 3.4. 就业优势 3.5.小结 4.好专业和…

移植案例与原理 - startup子系统之bootstrap_lite服务启动引导部件(1)

bootstrap_lite服务启动引导组件提供了各服务和功能的启动入口标识。在 SAMGR&#xff08;System ability manager&#xff0c;系统服务管理&#xff09; 启动时&#xff0c;会调用bootstrap_lite标识的入口函数&#xff0c;并启动系统服务。本文介绍下移植开发板时如何适配服务…