Is there a way to add a button control to a cell in inside a ListView in a WinForms app?
Select the new FlowLayoutPanel, and then open the Toolbox and select Common Controls. Double-click the Button item to add a button control called button1. Double-click Button again to add another button.
The Windows Forms ListView control can display additional text, or subitems, for each item in the Details view. The first column displays the item text, for example an employee number. The second, third, and subsequent columns display the first, second, and subsequent associated subitems.
C# ListView control provides an interface to display a list of items using different views including text, small images, and large images.
Here is a code of a class ListViewExtender
that you can reuse. It's not a derived class of ListView
, basically you just declare that a specific column is displayed as buttons instead of text. The button's text is the subItem's text.
It allows big sized list views without problems, does not use p/invoke, and also works with horizontal scrollbars (some code proposed as answers here don't or are quite slow with a great number of items). Note it requires the extended ListView to have FullRowSelect
set to true
and view type set to Details
.
This is a sample code that uses it:
namespace WindowsFormsApplication1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); // you need to add a listView named listView1 with the designer listView1.FullRowSelect = true; ListViewExtender extender = new ListViewExtender(listView1); // extend 2nd column ListViewButtonColumn buttonAction = new ListViewButtonColumn(1); buttonAction.Click += OnButtonActionClick; buttonAction.FixedWidth = true; extender.AddColumn(buttonAction); for (int i = 0; i < 10000; i++) { ListViewItem item = listView1.Items.Add("item" + i); item.SubItems.Add("button " + i); } } private void OnButtonActionClick(object sender, ListViewColumnMouseEventArgs e) { MessageBox.Show(this, @"you clicked " + e.SubItem.Text); } } }
Here is the ListViewExtender code and associated classes:
using System; using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; using System.Windows.Forms.VisualStyles; namespace WindowsFormsApplication1 { public class ListViewExtender : IDisposable { private readonly Dictionary<int, ListViewColumn> _columns = new Dictionary<int, ListViewColumn>(); public ListViewExtender(ListView listView) { if (listView == null) throw new ArgumentNullException("listView"); if (listView.View != View.Details) throw new ArgumentException(null, "listView"); ListView = listView; ListView.OwnerDraw = true; ListView.DrawItem += OnDrawItem; ListView.DrawSubItem += OnDrawSubItem; ListView.DrawColumnHeader += OnDrawColumnHeader; ListView.MouseMove += OnMouseMove; ListView.MouseClick += OnMouseClick; Font = new Font(ListView.Font.FontFamily, ListView.Font.Size - 2); } public virtual Font Font { get; private set; } public ListView ListView { get; private set; } protected virtual void OnMouseClick(object sender, MouseEventArgs e) { ListViewItem item; ListViewItem.ListViewSubItem sub; ListViewColumn column = GetColumnAt(e.X, e.Y, out item, out sub); if (column != null) { column.MouseClick(e, item, sub); } } public ListViewColumn GetColumnAt(int x, int y, out ListViewItem item, out ListViewItem.ListViewSubItem subItem) { subItem = null; item = ListView.GetItemAt(x, y); if (item == null) return null; subItem = item.GetSubItemAt(x, y); if (subItem == null) return null; for (int i = 0; i < item.SubItems.Count; i++) { if (item.SubItems[i] == subItem) return GetColumn(i); } return null; } protected virtual void OnMouseMove(object sender, MouseEventArgs e) { ListViewItem item; ListViewItem.ListViewSubItem sub; ListViewColumn column = GetColumnAt(e.X, e.Y, out item, out sub); if (column != null) { column.Invalidate(item, sub); return; } if (item != null) { ListView.Invalidate(item.Bounds); } } protected virtual void OnDrawColumnHeader(object sender, DrawListViewColumnHeaderEventArgs e) { e.DrawDefault = true; } protected virtual void OnDrawSubItem(object sender, DrawListViewSubItemEventArgs e) { ListViewColumn column = GetColumn(e.ColumnIndex); if (column == null) { e.DrawDefault = true; return; } column.Draw(e); } protected virtual void OnDrawItem(object sender, DrawListViewItemEventArgs e) { // do nothing } public void AddColumn(ListViewColumn column) { if (column == null) throw new ArgumentNullException("column"); column.Extender = this; _columns[column.ColumnIndex] = column; } public ListViewColumn GetColumn(int index) { ListViewColumn column; return _columns.TryGetValue(index, out column) ? column : null; } public IEnumerable<ListViewColumn> Columns { get { return _columns.Values; } } public virtual void Dispose() { if (Font != null) { Font.Dispose(); Font = null; } } } public abstract class ListViewColumn { public event EventHandler<ListViewColumnMouseEventArgs> Click; protected ListViewColumn(int columnIndex) { if (columnIndex < 0) throw new ArgumentException(null, "columnIndex"); ColumnIndex = columnIndex; } public virtual ListViewExtender Extender { get; protected internal set; } public int ColumnIndex { get; private set; } public virtual Font Font { get { return Extender == null ? null : Extender.Font; } } public ListView ListView { get { return Extender == null ? null : Extender.ListView; } } public abstract void Draw(DrawListViewSubItemEventArgs e); public virtual void MouseClick(MouseEventArgs e, ListViewItem item, ListViewItem.ListViewSubItem subItem) { if (Click != null) { Click(this, new ListViewColumnMouseEventArgs(e, item, subItem)); } } public virtual void Invalidate(ListViewItem item, ListViewItem.ListViewSubItem subItem) { if (Extender != null) { Extender.ListView.Invalidate(subItem.Bounds); } } } public class ListViewColumnMouseEventArgs : MouseEventArgs { public ListViewColumnMouseEventArgs(MouseEventArgs e, ListViewItem item, ListViewItem.ListViewSubItem subItem) : base(e.Button, e.Clicks, e.X, e.Y, e.Delta) { Item = item; SubItem = subItem; } public ListViewItem Item { get; private set; } public ListViewItem.ListViewSubItem SubItem { get; private set; } } public class ListViewButtonColumn : ListViewColumn { private Rectangle _hot = Rectangle.Empty; public ListViewButtonColumn(int columnIndex) : base(columnIndex) { } public bool FixedWidth { get; set; } public bool DrawIfEmpty { get; set; } public override ListViewExtender Extender { get { return base.Extender; } protected internal set { base.Extender = value; if (FixedWidth) { base.Extender.ListView.ColumnWidthChanging += OnColumnWidthChanging; } } } protected virtual void OnColumnWidthChanging(object sender, ColumnWidthChangingEventArgs e) { if (e.ColumnIndex == ColumnIndex) { e.Cancel = true; e.NewWidth = ListView.Columns[e.ColumnIndex].Width; } } public override void Draw(DrawListViewSubItemEventArgs e) { if (_hot != Rectangle.Empty) { if (_hot != e.Bounds) { ListView.Invalidate(_hot); _hot = Rectangle.Empty; } } if ((!DrawIfEmpty) && (string.IsNullOrEmpty(e.SubItem.Text))) return; Point mouse = e.Item.ListView.PointToClient(Control.MousePosition); if ((ListView.GetItemAt(mouse.X, mouse.Y) == e.Item) && (e.Item.GetSubItemAt(mouse.X, mouse.Y) == e.SubItem)) { ButtonRenderer.DrawButton(e.Graphics, e.Bounds, e.SubItem.Text, Font, true, PushButtonState.Hot); _hot = e.Bounds; } else { ButtonRenderer.DrawButton(e.Graphics, e.Bounds, e.SubItem.Text, Font, false, PushButtonState.Default); } } } }
The ListView itself (or ListViewItem) does not function as a container of any kind so no way to add controls directly, however it is doable. I have used this extended ListView with a lot of success: Embedding Controls in a ListView.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With