using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Text; using System.Windows.Forms; using System.Runtime.InteropServices; namespace Nuclex.Windows.Forms { /// ListView allowing for other controls to be embedded in its cells /// /// There basically were two possible design choices: Provide a specialized /// ListViewSubItem that carries a Control instead of a string or manage the /// embedded controls in seperation of the ListView's items. The first option /// would require a complete rewrite of the ListViewItem class and its related /// support classes, all of which are surprisingly large and complex. Thus, /// the less clean but more doable latter option has been chosen. /// public partial class ContainerListView : System.Windows.Forms.ListView { #region struct EmbeddedControl /// Informationen über ein ins ListView eingebettetes Steuerelement private struct EmbeddedControl { /// Steuerelement das im ListView eingebetttr ist public Control Control; /// Spalte in der das Control eingebettet ist public int Column; /// Zeile in der das Control eingebettet ist public int Row; /// Wie das Control in der ListView-Zelle angedockt ist public DockStyle Dock; /// Das ListView-Element in dem sich das Control befindet public ListViewItem Item; } #endregion // struct EmbeddedControl /// Initialisiert ein neues ListView-Steuerelement public ContainerListView() { this.embeddedControlClickedHandler = new EventHandler(embeddedControlClicked); this.embeddedControls = new ListViewEmbeddedControlCollection(); this.embeddedControls.EmbeddedControlAdded += new EventHandler( embeddedControlAdded ); this.embeddedControls.EmbeddedControlRemoved += new EventHandler( embeddedControlRemoved ); InitializeComponent(); base.View = View.Details; base.AllowColumnReorder = false; } /// Called when a control gets removed from the embedded controls list /// List from which the control has been removed /// Event arguments providing a reference to the removed control private void embeddedControlAdded( object sender, ListViewEmbeddedControlCollection.ListViewEmbeddedControlEventArgs e ) { e.EmbeddedControl.Control.Click += this.embeddedControlClickedHandler; this.Controls.Add(e.EmbeddedControl.Control); } /// Called when a control gets added to the embedded controls list /// List to which the control has been added /// Event arguments providing a reference to the added control private void embeddedControlRemoved( object sender, ListViewEmbeddedControlCollection.ListViewEmbeddedControlEventArgs e ) { if(this.Controls.Contains(e.EmbeddedControl.Control)) { e.EmbeddedControl.Control.Click -= this.embeddedControlClickedHandler; this.Controls.Remove(e.EmbeddedControl.Control); } } /// Called when an embedded control has been clicked on /// Embedded control that has been clicked /// Not used private void embeddedControlClicked(object sender, EventArgs e) { this.BeginUpdate(); try { SelectedItems.Clear(); foreach(ListViewEmbeddedControl embeddedControl in this.embeddedControls) { if(ReferenceEquals(embeddedControl.Control, sender)) { if((embeddedControl.Row > 0) && (embeddedControl.Row < Items.Count)) Items[embeddedControl.Row].Selected = true; } } } finally { this.EndUpdate(); } } private int[] GetColumnOrder() { int[] order = new int[this.Columns.Count]; for(int i = 0; i < this.Columns.Count; ++i) order[this.Columns[i].DisplayIndex] = i; return order; } /// Calculates the boundaries of a cell in the list view /// Item in the list view from which to calculate the cell /// Index der cell whose boundaries to calculate /// The boundaries of the specified list view cell protected Rectangle GetSubItemBounds(ListViewItem item, int subItem) { int[] order = GetColumnOrder(); if (order == null) // No Columns return Rectangle.Empty; if (subItem >= order.Length) throw new IndexOutOfRangeException("SubItem " + subItem + " out of range"); // Rahmen des gesamten ListViewItems ermitteln, inklusive aller SubItems Rectangle itemBounds = item.GetBounds(ItemBoundsPortion.Entire); int subItemX = itemBounds.Left; // Horizontale Position des SubItems berechnen // Da die Spaltenreihenfolge geändert werden kann müssen wir // Columns[order[i]] statt Columns[i] verwenden! ColumnHeader columnHeader; int i; for (i = 0; i < order.Length; ++i) { columnHeader = this.Columns[order[i]]; if (columnHeader.Index == subItem) break; subItemX += columnHeader.Width; } return new Rectangle( subItemX, itemBounds.Top, this.Columns[order[i]].Width, itemBounds.Height ); } /// Event handler for when embedded controls are clicked on private EventHandler embeddedControlClickedHandler; /// Controls being embedded in this ListView private ListViewEmbeddedControlCollection embeddedControls; } } // namespace Nuclex.Windows.Forms