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

来源:https://www.cnblogs.com/arbboter/p/18254162
-Advertisement-
Play Games

功能說明 使用ListView時,希望可以在單元格顯示圖片或其他控制項,發現原生的ListView不支持,於是通過拓展,實現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
    }
}

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 目錄前言學習參考過程總結: 前言 做個自由仔。 學習參考 ChatGpt; https://www.cnblogs.com/zhili/p/DesignPatternSummery.html(大佬的,看了好多次) 過程 原由: 一開始只是想查查鏈式調用原理,以為是要繼承什麼介面,實現什麼方法才可以實 ...
  • 一:背景 1. 講故事 在dump分析的過程中經常會看到很多線程卡在Monitor.Wait方法上,曾經也有不少人問我為什麼用 !syncblk 看不到 Monitor.Wait 上的鎖信息,剛好昨天有時間我就來研究一下。 二:Monitor.Wait 底層怎麼玩的 1. 案例演示 為了方便講述,先 ...
  • 在以前我做程式的時候,一般在登錄視窗裡面顯示程式名稱,登錄視窗一般設置一張背景圖片,由於程式的名稱一般都是確定的,所以也不存在太大的問題,不過如果客戶定製不同的系統的時候,需要使用Photoshop修改下圖層的文字,再生成圖片,然後替換一下也可以了。不過本著減少客戶使用繁瑣性,也可以使用空白名稱的通... ...
  • .Net 中提供了一系列的管理對象集合的類型,數組、可變列表、字典等。從類型安全上集合分為兩類,泛型集合 和 非泛型集合,傳統的非泛型集合存儲為Object,需要類型轉。而泛型集合提供了更好的性能、編譯時類型安全,推薦使用。 ...
  • 今天在技術群里,石頭哥向大家提了個問題:"如何在一個以System身份運行的.NET程式(Windows Services)中,以其它活動的用戶身份啟動可互動式進程(桌面應用程式、控制台程式、等帶有UI和互動式體驗的程式)"? 我以前有過類似的需求,是在GitLab流水線中運行帶有UI的自動化測試程 ...
  • OPC基金會提供了OPC UA .NET標準庫以及示常式序,但官方文檔過於簡單,光看官方文檔和示常式序很難弄懂OPC UA .NET標準庫怎麼用,花了不少時間摸索才略微弄懂如何使用,以下記錄如何從一個控制台程式開發一個OPC UA伺服器。 安裝Nuget包 安裝OPCFoundation.NetSt ...
  • 前言 隨著 DEV24.1.3 的發佈,XAF Blazor 中的屬性編輯器(PropertyEditor)也進行了很大的改動,在使用體驗上也更接近 WinForm 了,由於進行了大量的封裝,理解上沒有 WinForm 直觀,所以本文通過對屬性編輯器的原理進行解析,並對比新舊版本中的變化,使大家能夠 ...
  • 由於.NET Framework 4.0 是比較古老的版本,只有New Relic 7.0以下的版本才會支持.NET Framework 4.0的引用程式。 Technical support for .NET Framework 4.0 or lower 你可以參考這個官方Install New ...
一周排行
    -Advertisement-
    Play Games
  • 一:背景 1. 講故事 前些天有位朋友找到我,說他們的程式會偶發性的卡死一段時間,然後又好了,讓我幫忙看下怎麼回事?窗體類的程式解決起來相對來說比較簡單,讓朋友用procdump自動抓一個卡死時的dump,拿到dump之後,上 windbg 說話。 二:WinDbg 分析 1. 主線程在做什麼 要想 ...
  • 功能說明 使用ListView時,希望可以在單元格顯示圖片或其他控制項,發現原生的ListView不支持,於是通過拓展,實現ListView可以顯示任意控制項的功能,效果如下: 實現方法 本來想著在單元格裡面實現控制項的自繪的,但是沒找到辦法,最後是通過在單元格的錶面顯示對應控制項的,浮於錶面達到目的。 實 ...
  • 由於.NET Framework 4.0 是比較古老的版本,只有New Relic 7.0以下的版本才會支持.NET Framework 4.0的引用程式。 Technical support for .NET Framework 4.0 or lower 你可以參考這個官方Install New ...
  • 前言 隨著 DEV24.1.3 的發佈,XAF Blazor 中的屬性編輯器(PropertyEditor)也進行了很大的改動,在使用體驗上也更接近 WinForm 了,由於進行了大量的封裝,理解上沒有 WinForm 直觀,所以本文通過對屬性編輯器的原理進行解析,並對比新舊版本中的變化,使大家能夠 ...
  • OPC基金會提供了OPC UA .NET標準庫以及示常式序,但官方文檔過於簡單,光看官方文檔和示常式序很難弄懂OPC UA .NET標準庫怎麼用,花了不少時間摸索才略微弄懂如何使用,以下記錄如何從一個控制台程式開發一個OPC UA伺服器。 安裝Nuget包 安裝OPCFoundation.NetSt ...
  • 今天在技術群里,石頭哥向大家提了個問題:"如何在一個以System身份運行的.NET程式(Windows Services)中,以其它活動的用戶身份啟動可互動式進程(桌面應用程式、控制台程式、等帶有UI和互動式體驗的程式)"? 我以前有過類似的需求,是在GitLab流水線中運行帶有UI的自動化測試程 ...
  • .Net 中提供了一系列的管理對象集合的類型,數組、可變列表、字典等。從類型安全上集合分為兩類,泛型集合 和 非泛型集合,傳統的非泛型集合存儲為Object,需要類型轉。而泛型集合提供了更好的性能、編譯時類型安全,推薦使用。 ...
  • 在以前我做程式的時候,一般在登錄視窗裡面顯示程式名稱,登錄視窗一般設置一張背景圖片,由於程式的名稱一般都是確定的,所以也不存在太大的問題,不過如果客戶定製不同的系統的時候,需要使用Photoshop修改下圖層的文字,再生成圖片,然後替換一下也可以了。不過本著減少客戶使用繁瑣性,也可以使用空白名稱的通... ...
  • 一:背景 1. 講故事 在dump分析的過程中經常會看到很多線程卡在Monitor.Wait方法上,曾經也有不少人問我為什麼用 !syncblk 看不到 Monitor.Wait 上的鎖信息,剛好昨天有時間我就來研究一下。 二:Monitor.Wait 底層怎麼玩的 1. 案例演示 為了方便講述,先 ...
  • 目錄前言學習參考過程總結: 前言 做個自由仔。 學習參考 ChatGpt; https://www.cnblogs.com/zhili/p/DesignPatternSummery.html(大佬的,看了好多次) 過程 原由: 一開始只是想查查鏈式調用原理,以為是要繼承什麼介面,實現什麼方法才可以實 ...