功能说明
使用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 ListViewExpublic 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];#endregionreturn 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;}#endregionbase.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}
}