#region CPL License /* Nuclex Framework Copyright (C) 2002-2007 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #endregion 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 seperate 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, /// I chose the less clean but more doable latter option. /// public partial class ContainerListView : System.Windows.Forms.ListView { /// Initializes a new ContainerListView public ContainerListView() { this.embeddedControlClickedHandler = new EventHandler(embeddedControlClicked); this.embeddedControls = new ListViewEmbeddedControlCollection(); this.embeddedControls.Added += new EventHandler( embeddedControlAdded ); this.embeddedControls.Removed += new EventHandler( embeddedControlRemoved ); this.embeddedControls.Clearing += new EventHandler(embeddedControlsClearing); InitializeComponent(); base.View = View.Details; base.AllowColumnReorder = false; } /// Called when the list of embedded controls has been cleared /// Collection that has been cleared of its controls /// Not used private void embeddedControlsClearing(object sender, EventArgs e) { this.BeginUpdate(); try { foreach(ListViewEmbeddedControl embeddedControl in this.embeddedControls) { embeddedControl.Control.Click -= this.embeddedControlClickedHandler; this.Controls.Remove(embeddedControl.Control); } } finally { this.EndUpdate(); } } /// 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(); } } /// 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 /// /// When the specified sub item index is not in the range of valid sub items /// 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"); // Determine the border of the entire ListViewItem, including all sub items Rectangle itemBounds = item.GetBounds(ItemBoundsPortion.Entire); int subItemX = itemBounds.Left; // Find the horizontal position of the sub item. Because the column order can vary, // we need to use Columns[order[i]] instead of simply doing Columns[i] here! 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 ); } /// Obtains the current column order of the list /// An array indicating the order of the list's columns 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; } /// 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