铝合金沙锅 发表于 2024-6-18 20:19:21

C#控件ListView支持添加TextBox\PictureBox等其他控件

功能说明

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

实现方法

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

[*]ListView需要设置OwnerDraw=true,并重载自绘函数OnDrawColumnHeader、OnDrawItem、OnDrawSubItem
[*]支持按单元格添加对应的控件,其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;
            if (oc != null)
            {
                oc.Dispose();
            }
            OwnerDraw = true;
            IsViewChanged = true;
            c.Parent = this;
            _CellControls = 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;
            #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;
                        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;
                        if(item.Bounds.Y < 0 || item.Bounds.Y >= this.Height)
                        {
                            continue;
                        }
                        if(item.SubItems.Bounds.X < 0 || item.SubItems.Bounds.X >= this.Width)
                        {
                            continue;
                        }
                        ShowCellControl(cell_control, item.SubItems);
                  }
                  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
页: [1]
查看完整版本: C#控件ListView支持添加TextBox\PictureBox等其他控件