Click here to Skip to main content
15,895,423 members
Articles / Programming Languages / C#

Updated Extended ListView

Rate me:
Please Sign up or sign in to vote.
4.63/5 (24 votes)
30 Sep 20033 min read 364.2K   4.5K   97  
An update to Jon Rista's treeview/listview control
// Author:  Bill Seddon
// Company: Lyquidity Solutions Limited
// 
// This work builds on code posted to SourceForge by
// Jon Rista (jrista@hotmail.com)
// 
// ContainerListView provides a listview type control
// that allows controls to be contained in sub item
// cells. The control is also fully compatible with
// Windows XP visual styles.
//
// This version fixes up drawing, mouse and keyboard
// event handling issues.
//
// This code is provided "as is" and no warranty about
// it fitness for any specific task is expressed or 
// implied.  If you choose to use this code, you do so
// at your own risk.
//
////////////////////////////////////////////////////////

using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.ComponentModel.Design.Serialization;
using System.Collections;
using System.Collections.Specialized;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Design;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Globalization;
using System.Reflection;
using System.Threading;
using System.Windows.Forms;
using System.Windows.Forms.Design;

namespace Lyquidity.Controls.ExtendedListViews
{
	#region Enumerations

	public enum WinMsg
	{
		WM_GETDLGCODE = 0x0087,
		WM_SETREDRAW = 0x000B,
		WM_CANCELMODE = 0x001F,
		WM_NOTIFY = 0x4e,
		WM_KEYDOWN = 0x100,
		WM_KEYUP = 0x101,
		WM_CHAR = 0x0102,
		WM_SYSKEYDOWN = 0x104,
		WM_SYSKEYUP = 0x105,
		WM_COMMAND = 0x111,
		WM_MENUCHAR = 0x120,
		WM_MOUSEMOVE = 0x200,
		WM_LBUTTONDOWN = 0x201,
		WM_MOUSELAST = 0x20a,
		WM_USER = 0x0400,
		WM_REFLECT = WM_USER + 0x1c00
	}

	public enum DialogCodes
	{
		DLGC_WANTARROWS =     0x0001,      /* Control wants arrow keys         */
		DLGC_WANTTAB =        0x0002,      /* Control wants tab keys           */
		DLGC_WANTALLKEYS =    0x0004,      /* Control wants all keys           */
		DLGC_WANTMESSAGE =    0x0004,      /* Pass message to control          */
		DLGC_HASSETSEL =      0x0008,      /* Understands EM_SETSEL message    */
		DLGC_DEFPUSHBUTTON =  0x0010,      /* Default pushbutton               */
		DLGC_UNDEFPUSHBUTTON = 0x0020,     /* Non-default pushbutton           */
		DLGC_RADIOBUTTON =    0x0040,      /* Radio button                     */
		DLGC_WANTCHARS =      0x0080,      /* Want WM_CHAR messages            */
		DLGC_STATIC =         0x0100,      /* Static item: don't include       */
		DLGC_BUTTON =         0x2000,      /* Button item: can be checked      */
	}

	public enum ColumnScaleStyle
	{
		Slide,
		Spring
	}

	public enum MultiSelectMode
	{
		Single,
		Range,
		Selective
	}
	#endregion

	#region Event Handlers and Arguments
	public class ItemsChangedEventArgs: EventArgs
	{
		public int IndexChanged = -1;

		public ItemsChangedEventArgs(int indexChanged)
		{
			IndexChanged = indexChanged;
		}
	}

	public delegate void ItemsChangedEventHandler(object sender, ItemsChangedEventArgs e);
	#endregion

	#region ContainerListViewItem classes
	[DesignTimeVisible(false), TypeConverter("Lyquidity.Controls.ExtendedListViews.ListViewItemConverter")]
	public class ContainerListViewItem: ICloneable
	{
		public event MouseEventHandler MouseDown;

		#region Variables
		private Color backcolor;
		// private Rectangle bounds;
		private bool ischecked;
		private bool focused;
		private Font font;
		private Color forecolor;
		private int imageindex;
		private int stateimageindex;
		private int index;
		private ContainerListView listview;
		private bool selected;		
		private ContainerSubListViewItemCollection subitems;
		private object tag;
		private string text;
		private bool styleall;
		private bool hovered;
		#endregion

		#region Constructors
		public ContainerListViewItem()
		{
			subitems = new ContainerSubListViewItemCollection();
			subitems.ItemsChanged += new ItemsChangedEventHandler(OnSubItemsChanged);
		}

		public ContainerListViewItem(ContainerListView Listview, int Index)
		{
			index = Index;
			listview = Listview;
		}
		#endregion

		private void OnSubItemsChanged(object sender, ItemsChangedEventArgs e)
		{
			subitems[e.IndexChanged].MouseDown += new MouseEventHandler(OnSubItemMouseDown);
		}

		private void OnSubItemMouseDown(object sender, MouseEventArgs e)
		{
			if (MouseDown != null)
				MouseDown(this, e);
		}

		#region Properties
		public Color BackColor
		{
			get { return backcolor;	}
			set { backcolor = value; }
		}
/*
		public Rectangle Bounds
		{
			get { return bounds; }
		}
*/
		public bool Checked
		{
			get { return ischecked; }
			set { ischecked = value; }
		}

		public bool Focused
		{
			get { return focused; }
			set { focused = value; }
		}

		public Font Font
		{
			get { return font; }
			set { font = value; }
		}

		public Color ForeColor
		{
			get { return forecolor; }
			set { forecolor = value; }
		}

		public int ImageIndex
		{
			get { return imageindex; }
			set { imageindex = value; }
		}

		public int Index
		{
			get { return index; }
			set { index = value; }
		}

		public ContainerListView ListView
		{
			get { return listview; }
		}

		public bool Selected
		{
			get { return selected; }
			set { selected = value; }
		}

		[
		Category("Behavior"),
		Description("The items collection of sub controls."),
		DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
		Editor(typeof(CollectionEditor), typeof(UITypeEditor))		 
		]
		public ContainerSubListViewItemCollection SubItems
		{
			get { return subitems; }
		}

		public int StateImageIndex
		{
			get { return stateimageindex; }
			set { stateimageindex = value; }
		}

		public object Tag
		{
			get { return tag; }
			set { tag = value; }
		}

		public string Text
		{
			get { return text; }
			set { text = value; }
		}

		public bool UseItemStyleForSubItems
		{
			get { return styleall; }
			set { styleall = value; }
		}

		[Browsable(false)]
		public bool Hovered
		{
			get { return hovered; }
			set { hovered = value; }
		}
		#endregion

		#region Methods
		public object Clone()
		{
			ContainerListViewItem lvi = new ContainerListViewItem();
			lvi.BackColor = backcolor;
			lvi.Focused = focused;
			lvi.Font = font;
			lvi.ForeColor = forecolor;
			lvi.ImageIndex = imageindex;
			lvi.Selected = selected;
			lvi.Tag = tag;
			lvi.Text = text;
			lvi.UseItemStyleForSubItems = styleall;

			return lvi;
		}
		#endregion
	}

	public class ContainerListViewItemCollection: CollectionBase
	{
		public event MouseEventHandler MouseDown;

		private void OnMouseDown(object sender, MouseEventArgs e)
		{
			if (MouseDown != null)
				MouseDown(sender, e);
		}

		#region Interface Implementations
		public ContainerListViewItem this[int index]
		{
			get { return List[index] as ContainerListViewItem; }
			set
			{
				List[index] = value;
				((ContainerListViewItem)List[index]).MouseDown += new MouseEventHandler(OnMouseDown);
			}
		}
		
		public int this[ContainerListViewItem item]
		{
			get { return List.IndexOf(item); }
		}
		public int Add(ContainerListViewItem item)
		{
			item.MouseDown += new MouseEventHandler(OnMouseDown);
			return item.Index = List.Add(item);
		}

		public void AddRange(ContainerListViewItem[] items)
		{
			lock(List.SyncRoot)
			{
				for (int i=0; i<items.Length; i++)
				{
					items[i].MouseDown += new MouseEventHandler(OnMouseDown);
					items[i].Index = List.Add(items[i]);
				}
			}
		}

		public void Remove(ContainerListViewItem item)
		{
			item.MouseDown -= new MouseEventHandler(OnMouseDown);
			List.Remove(item);
		}

		public new void Clear()
		{
			for (int i=0; i<List.Count; i++)
			{
				ContainerSubListViewItemCollection col = ((ContainerListViewItem)List[i]).SubItems;
				for (int j=0; j<col.Count; j++)
				{
					if (col[j].ItemControl != null)
					{
						col[j].ItemControl.Parent = null;
						col[j].ItemControl.Visible = false;
						col[j].ItemControl = null;
					}
				}
				
			}
			List.Clear();
		}
		public int IndexOf(ContainerListViewItem item)
		{
			return List.IndexOf(item);
		}
		#endregion
	}

	public class SelectedContainerListViewItemCollection: CollectionBase
	{
		#region Interface Implementations
		public ContainerListViewItem this[int index]
		{
			get { return List[index] as ContainerListViewItem; }
			set
			{
				List[index] = value;
			}
		}
		
		public int this[ContainerListViewItem item]
		{
			get { return List.IndexOf(item); }
		}
		public int Add(ContainerListViewItem item)
		{
			return item.Index = List.Add(item);
		}

		public void AddRange(ContainerListViewItem[] items)
		{
			lock(List.SyncRoot)
			{
				for (int i=0; i<items.Length; i++)
				{
					items[i].Index = List.Add(items[i]);
				}
			}
		}

		public void Remove(ContainerListViewItem item)
		{
			List.Remove(item);
		}

		public new void Clear()
		{
			List.Clear();
		}

		public int IndexOf(ContainerListViewItem item)
		{
			return List.IndexOf(item);
		}
		#endregion
	}

	#endregion

	#region ContainerSubListViewItem classes
	[DesignTimeVisible(false), TypeConverter("Lyquidity.Controls.ExtendedListViews.SubListViewItemConverter")]
	public class ContainerSubListViewItem: ICloneable
	{
		public event MouseEventHandler MouseDown;

		protected void OnMouseDown(object sender, MouseEventArgs e)
		{
			if (MouseDown != null)
				MouseDown(sender, e);
		}

		private string text;
		private Control childControl;

		public ContainerSubListViewItem()
		{
			text = "SubItem";
		}

		public ContainerSubListViewItem(Control control)
		{
			text = "";
			Construct(control);
		}

		public ContainerSubListViewItem(string str)
		{
			text = str;
			Construct(null);
		}

		private void Construct(Control control)
		{
			childControl = control;

			if (childControl != null)
				childControl.MouseDown += new MouseEventHandler(OnMouseDown);
		}

		public Control ItemControl
		{
			get
			{
				return childControl;
			}
			set
			{
				childControl = (Control)value;

				if (childControl != null)
					childControl.MouseDown += new MouseEventHandler(OnMouseDown);
			}
		}

		public object Clone()
		{
			ContainerSubListViewItem slvi = new ContainerSubListViewItem();
			slvi.ItemControl = null;
			slvi.Text = text;

			return slvi;
		}

		public string Text
		{
			get { return text; }
			set { text = value; }
		}

		public override string ToString()
		{
			return (childControl == null ? text : childControl.ToString());
		}
	}

	public class ContainerSubListViewItemCollection: CollectionBase
	{
		public event ItemsChangedEventHandler ItemsChanged;

		protected void OnItemsChanged(ItemsChangedEventArgs e)
		{
			if (ItemsChanged != null)
				ItemsChanged(this, e);
		}

		#region Interface Implementations
		public ContainerSubListViewItem this[int index]
		{
			get 
			{ 
				try
				{
					return List[index] as ContainerSubListViewItem; 
				}
				catch
				{
					return null;
				}
			}
			set
			{
				List[index] = value; 
				OnItemsChanged(new ItemsChangedEventArgs(index));
			}
		}

		public int Add(ContainerSubListViewItem item)
		{
			int index = List.Add(item);
			OnItemsChanged(new ItemsChangedEventArgs(index));
			return index;
		}

		public ContainerSubListViewItem Add(Control control)
		{
			ContainerSubListViewItem slvi = new ContainerSubListViewItem(control);
			lock(List.SyncRoot)
				OnItemsChanged(new ItemsChangedEventArgs(List.Add(slvi)));
			return slvi;
		}

		public ContainerSubListViewItem Add(string str)
		{
			ContainerSubListViewItem slvi = new ContainerSubListViewItem(str);
			lock(List.SyncRoot)
				OnItemsChanged(new ItemsChangedEventArgs(List.Add(slvi)));
			return slvi;
		}

		public void AddRange(ContainerSubListViewItem[] items)
		{
			lock(List.SyncRoot)
			{
				for (int i=0; i<items.Length; i++)
				{
					OnItemsChanged(new ItemsChangedEventArgs(List.Add(items[i])));
				}
			}
		}
		#endregion
	}
	#endregion

	#region Column Header classes
	[DesignTimeVisible(false), TypeConverter("Lyquidity.Controls.ExtendedListViews.ToggleColumnHeaderConverter")]
	public class ToggleColumnHeader: ICloneable
	{
		// send an internal event when a column is resized
		internal event EventHandler WidthResized;
		private void OnWidthResized()
		{
			if (WidthResized != null)
				WidthResized(this, new EventArgs());
		}

		private int index;
		private ContainerListView listview;
		private string text;
		private HorizontalAlignment textAlign;
		private int width;
		private bool visible;
		private bool hovered;
		private bool pressed;
		private bool selected;
		private ColumnScaleStyle scaleStyle;
		private Bitmap image;

		public ToggleColumnHeader()
		{
			index = 0;
			listview = null;
			textAlign = HorizontalAlignment.Left;
			width = 90;
			visible = true;
			hovered = false;
			pressed = false;
			scaleStyle = ColumnScaleStyle.Slide;
		}


		[Browsable(false)]
		public bool Selected
		{
			get { return selected; }
			set { selected = value; }
		}

		[
		Category("Appearance"),
		Description("The image to display in this header.")
		]
		public Bitmap Image
		{
			get { return image; }
			set { image = value; }
		}

		[Category("Behavior"), Description("Determines how a column reacts when another column is scaled.")]
		public ColumnScaleStyle ScaleStyle
		{
			get { return scaleStyle; }
			set { scaleStyle = value; }
		}

		[Category("Data"), Description("The index of this column in the collection.")]
		public int Index
		{
			get { return index; }
			set { index = value; }
		}

		[Category("Data"), Description("The parent listview of this column header.")]
		public ContainerListView ListView
		{
			get { return listview; }
		}

		[Category("Appearance"), Description("The title of this column header.")]
		public string Text
		{
			get { return text; }
			set { text = value; }
		}

		[Category("Behavior"), Description("The alignment of the column headers title.")]
		public HorizontalAlignment TextAlign
		{
			get { return textAlign; }
			set { textAlign = value; }
		}

		[Category("Behavior"), Description("The width in pixels of this column header."), DefaultValue(90)]
		public int Width
		{
			get { return width; }
			set 
			{ 
				width = value; 
				OnWidthResized();
			}
		}

		[Category("Behavior"), Description("Determines wether the control is visible or hidden.")]
		public bool Visible
		{
			get { return visible; }
			set { visible = value; }
		}

		[Browsable(false)]
		public bool Hovered
		{
			get { return hovered; }
			set { hovered = value; }
		}

		[Browsable(false)]
		public bool Pressed
		{
			get { return pressed; }
			set { pressed = value; }
		}

		public object Clone()
		{
			ToggleColumnHeader ch = new ToggleColumnHeader();
			ch.Index = index;
			ch.Text = text;
			ch.TextAlign = textAlign;
			ch.Width = width;

			return ch;
		}

		public override string ToString()
		{
			return text;
		}
	}

	public class ColumnHeaderCollection: CollectionBase
	{
		internal event EventHandler WidthResized;
		private void OnWidthResized(object sender, EventArgs e)
		{
			if (WidthResized != null)
				WidthResized(sender, e);
		}

		#region Interface Implementations		
		public ToggleColumnHeader this[int index]
		{
			get
			{ 
				ToggleColumnHeader tch = new ToggleColumnHeader();
				try
				{
					tch = List[index] as ToggleColumnHeader;
				}
				catch
				{
					Debug.WriteLine("Column at index " + index + " does not exist.");
				}
				return tch;
			}
			set 
			{ 
				List[index] = value; 
				((ToggleColumnHeader)List[index]).WidthResized += new EventHandler(OnWidthResized);
			}
		}

		public virtual int Add(Lyquidity.Controls.ExtendedListViews.ToggleColumnHeader colhead)
		{
			colhead.WidthResized += new EventHandler(OnWidthResized);
			return colhead.Index = List.Add(colhead);
		}

		public virtual ToggleColumnHeader Add(string str, int width, HorizontalAlignment textAlign)
		{
			ToggleColumnHeader tch = new ToggleColumnHeader();
			tch.Text = str;
			tch.Width = width;
			tch.TextAlign = textAlign;
			tch.WidthResized += new EventHandler(OnWidthResized);

			lock(List.SyncRoot)
			{
				tch.Index = List.Add(tch);
			}
			return tch;
		}

		public virtual void AddRange(Lyquidity.Controls.ExtendedListViews.ToggleColumnHeader[] items)
		{
			lock(List.SyncRoot)
			{
				for (int i=0; i< items.Length; i++)
				{
					items[i].WidthResized += new EventHandler(OnWidthResized);
					List.Add(items[i]);
				}
			}
		}
		#endregion
	}
	#endregion

	#region ContainerListView

	#region ContainerListView Delegates
	public delegate void ContextMenuEventHandler(object sender, MouseEventArgs e);
	public delegate void ItemMenuEventHandler(object sender, MouseEventArgs e);
	public delegate void HeaderMenuEventHandler(object sender, MouseEventArgs e);
	#endregion

	/// <summary>
	/// Provides a listview control in detail mode that
	/// provides containers for each cell in a row/column.
	/// The container can hold almost any object that derives
	/// directly or indirectly from Control.
	/// </summary>

	[DefaultProperty("Items")]
	[ToolboxItem(true)]
	[ToolboxBitmap(typeof(ContainerListView), "Resources.listview.bmp")]
	public class ContainerListView: Control
	{
		#region Events
		public event LabelEditEventHandler AfterLabelEdit;
		public event LabelEditEventHandler BeforeLabelEdit;
		public event ColumnClickEventHandler ColumnClick;
		public event EventHandler ItemActivate;
		public event EventHandler SelectedIndexChanged;

		protected void OnAfterLabelEdit(LabelEditEventArgs e)
		{
			if (AfterLabelEdit != null)
				try { AfterLabelEdit(this, e);  /* "this" should be the label?  BMS 2003/05/22 */ }
				catch {}
		}

		protected void OnBeforeLabelEdit(LabelEditEventArgs e)
		{
			if (BeforeLabelEdit != null)
				try { BeforeLabelEdit(this, e);  /* "this" should be the label?  BMS 2003/05/22  */ }
				catch {}
		}

		protected void OnColumnClick(ColumnClickEventArgs e)
		{
			if (ColumnClick != null)
				try { ColumnClick(this, e); }
				catch {}
		}

		protected void OnItemActivate(EventArgs e)
		{
			if (ItemActivate != null)
				try { ItemActivate(this, e); }
				catch {}
		}

		protected void OnSelectedIndexChanged(EventArgs e)
		{
			if (SelectedIndexChanged != null)
				try { SelectedIndexChanged(this, e); } 
				catch {}
		}

		// This handler links any click events on an items subcontrols
		// to this control. Clicks will activate or deactivate the 
		// row containing the subcontrol.
		protected virtual void OnSubControlMouseDown(object sender, MouseEventArgs e)
		{
			//Debug.WriteLine("Subcontrol clicked.");

			ContainerListViewItem item = ((ContainerListViewItem)sender);

			if (multiSelectMode == MultiSelectMode.Single)
			{
				selectedIndices.Clear();
				selectedItems.Clear();

				for (int i=0; i<items.Count; i++)
				{

					items[i].Focused = false;
					items[i].Selected = false;
					if (items[i].Equals(item))
					{
						items[i].Focused = true;
						items[i].Selected = true;						
						focusedIndex = firstSelected = i;

						// set selected items and indices collections							
						selectedIndices.Add(i);						
						selectedItems.Add(items[i]);
					}
				}
				OnSelectedIndexChanged(new EventArgs());
			}
			else if (multiSelectMode == MultiSelectMode.Range)
			{
			}
			else if (multiSelectMode == MultiSelectMode.Selective)
			{
				// unfocus the previously focused item
				for (int i=0; i<items.Count; i++)
					items[i].Focused = false;

				if (item.Selected)
				{
					item.Focused = false;
					item.Selected = false;
					selectedIndices.Remove(items[item]);
					selectedItems.Remove(item);
					OnSelectedIndexChanged(new EventArgs());
				}
				else
				{
					item.Focused = true;
					item.Selected = true;
					selectedIndices.Add(items[item]);
					selectedItems.Add(item);
					OnSelectedIndexChanged(new EventArgs());
				}
				focusedIndex = items[item];
					
			}			

			Invalidate(this.ClientRectangle);
		}

		// The ContainerListView provides three context menus.
		// One for the header, one for the visible rows, and
		// one for the whole control, which is fallen back
		// on when the header and item menus do not exist.
		public event ContextMenuEventHandler ContextMenuEvent;
		public event ItemMenuEventHandler ItemMenuEvent;
		public event HeaderMenuEventHandler HeaderMenuEvent;

		protected void OnContextMenuEvent(MouseEventArgs e)
		{
			if (ContextMenuEvent != null)
				ContextMenuEvent(this, e);

			PopMenu(contextMenu, e);
		}

		protected void OnItemMenuEvent(MouseEventArgs e)
		{
			if (ItemMenuEvent != null)
				ItemMenuEvent(this, e);
			else if (itemMenu == null)
				OnContextMenuEvent(e);
			else
				PopMenu(itemMenu, e);
		}

		protected void OnHeaderMenuEvent(MouseEventArgs e)
		{
			if (HeaderMenuEvent != null)
				HeaderMenuEvent(this, e);
			else if (headerMenu == null)
				OnContextMenuEvent(e);
			else
				PopMenu(headerMenu, e);
		}

		protected void PopMenu(System.Windows.Forms.ContextMenu theMenu, MouseEventArgs e)
		{
			if (theMenu != null)
				theMenu.Show(this, new Point(e.X, e.Y));
		}

		// Handlers for scrollbars scroll
		protected void OnScroll(object sender, EventArgs e)
		{
			GenerateColumnRects();
			Invalidate();
		}

		// Handler for column width resizing
		protected void OnColumnWidthResize(object sender, EventArgs e)
		{
			GenerateColumnRects();
		}
		#endregion

		#region Variables

		protected ItemActivation activation;
		protected bool allowColumnReorder = false;
		protected BorderStyle borderstyle = BorderStyle.Fixed3D;
		private int borderWid = 2;
		protected Lyquidity.Controls.ExtendedListViews.ColumnHeaderCollection columns;			
		protected ColumnHeaderStyle headerStyle = ColumnHeaderStyle.Nonclickable;
		protected int headerBuffer = 20;

		protected bool hideSelection = true;
		protected bool hoverSelection = false;
		protected bool multiSelect = false;
		protected MultiSelectMode multiSelectMode = MultiSelectMode.Single;

		protected ContainerListViewItemCollection items;
		protected bool labelEdit = false;
		protected ImageList smallImageList, stateImageList;
		protected Comparer sortComparer;		
		protected bool scrollable = true;
		protected SortOrder sorting;
		protected string text;
		protected ContainerListViewItem topItem;

		//protected ContainerListViewItemCollection selectedItems = null;
		protected SelectedContainerListViewItemCollection selectedItems = null;
		protected ArrayList selectedIndices = null;

		protected bool visualStyles = false;

		protected Lyquidity.Controls.ExtendedListViews.ContainerListViewItem focusedItem;	
		protected int focusedIndex = -1;
		protected bool isFocused = false;

		protected Rectangle headerRect;
		protected Rectangle[] columnRects;
		protected Rectangle[] columnSizeRects;
		protected int lastColHovered = -1;
		protected int lastColPressed = -1;
		protected int lastColSelected = -1;
		protected bool doColTracking = false;
		protected Color colTrackColor = Color.WhiteSmoke;
		protected Color colSortColor = Color.Gainsboro;
		protected int allColsWidth = 0;
		protected bool colScaleMode = false;
		protected int colScalePos = 0;
		protected int colScaleWid = 0;
		protected int scaledCol = -1;

		protected Rectangle rowsRect;
		protected Rectangle[] rowRects;
		protected int lastRowHovered = -1;
		protected int rowHeight = 18;
		protected int allRowsHeight = 0;
		protected bool doRowTracking = true;
		protected Color rowTrackColor = Color.WhiteSmoke;
		protected Color rowSelectColor = SystemColors.Highlight;
		protected bool fullRowSelect = true;
		protected int firstSelected=-1, lastSelected=-1;

		protected bool gridLines = false;
		protected Color gridLineColor = Color.WhiteSmoke;

		protected Point lastClickedPoint;

		protected bool captureFocusClick = false;

		protected ContextMenu headerMenu, itemMenu, contextMenu;
		protected HScrollBar hscrollBar;
		protected VScrollBar vscrollBar;

		protected bool ensureVisible = true;

		private Stack m_cUpdateTransactions = new Stack();

		// private System.ComponentModel.Container components = null;

		#endregion

		#region Constructor
		public ContainerListView()
		{
			Construct();
		}

		private void Construct()
		{
			rowHeight = 18;

			//base.BackColor = SystemColors.Window;

			SetStyle(ControlStyles.AllPaintingInWmPaint|ControlStyles.ResizeRedraw|
				ControlStyles.Opaque|ControlStyles.UserPaint|ControlStyles.DoubleBuffer|
				ControlStyles.Selectable|ControlStyles.UserMouse, true);

			this.BackColor = SystemColors.Window;

			columns = new Lyquidity.Controls.ExtendedListViews.ColumnHeaderCollection();
			items = new ContainerListViewItemCollection();
			selectedIndices = new ArrayList();
			// selectedItems = new ContainerListViewItemCollection();
			selectedItems = new SelectedContainerListViewItemCollection();

			hscrollBar = new HScrollBar();
			hscrollBar.Parent = this;
			hscrollBar.Minimum = 0;
			hscrollBar.Maximum = 0;
			hscrollBar.SmallChange = 10;
			hscrollBar.Hide();

			vscrollBar = new VScrollBar();
			vscrollBar.Parent = this;
			vscrollBar.Minimum = 0;
			vscrollBar.Maximum = 0;
			vscrollBar.SmallChange = rowHeight;
			vscrollBar.Hide();

			Attach();  // (Events to scrollbars etc.

			GenerateColumnRects();
			GenerateHeaderRect();
		}
		#endregion

		#region Methods
		public void BeginUpdate()
		{
			m_cUpdateTransactions.Push(this);
		}

		public void EndUpdate()
		{
			if (m_cUpdateTransactions.Count > 0) m_cUpdateTransactions.Pop();
			if (!this.InUpdateTransaction) this.Invalidate();  // Force repaint when update ended
		}

		#endregion

		#region Properties
		[
		Category("Behavior"),
		Description("Specifies wether the selected item is always visible"),
		DefaultValue(true)
		]
		public bool EnsureVisible
		{
			get { return ensureVisible; }
			set { ensureVisible = value; }
		}

		[
		Category("Behavior"),
		Description("Specifies wether the control will capture the click used to focus the control and adjust the selection accordingly, or not."),
		DefaultValue(false)
		]
		public bool CaptureFocusClick
		{
			get { return captureFocusClick; }
			set { captureFocusClick = value; }
		}

		[
		Category("Behavior"),
		Description("The context menu displayed when the header is right-clicked.")
		]
		public ContextMenu HeaderMenu
		{
			get { return headerMenu; }
			set { headerMenu = value; }
		}

		[
		Category("Behavior"),
		Description("The context menu displayed when an item is right-clicked.")
		]
		public ContextMenu ItemMenu
		{
			get { return itemMenu; }
			set { itemMenu = value; }
		}

		[
		Category("Behavior"),
		Description("The context menu displayed when the control is right-clicked.")
		]
		public override ContextMenu ContextMenu
		{
			get { return contextMenu; }
			set { contextMenu = value; }
		}

		[
		Category("Behavior"),
		Description("The lists column headers."),
		DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
		Editor(typeof(CollectionEditor), typeof(UITypeEditor))
		]
		public Lyquidity.Controls.ExtendedListViews.ColumnHeaderCollection Columns
		{
			get { return columns; }
		}

		[
		Category("Data"),
		Description("The items contained in the list."),
		DesignerSerializationVisibility(DesignerSerializationVisibility.Content),
		Editor(typeof(CollectionEditor), typeof(UITypeEditor))
		]
		public virtual ContainerListViewItemCollection Items
		{
			get { return items; }
		}

		[
		Category("Behavior"),
		Description("Specifies what action activates and item."),
		DefaultValue(ItemActivation.Standard)
		]
		public ItemActivation Activation
		{
			get
			{
				return activation;
			}
			set
			{
				activation = value;
			}
		}

		[
		Category("Behavior"),
		Description("Specifies whether column headers may be reordered."),
		DefaultValue(false)
		]
		public bool AllowColumnReorder
		{
			get 
			{
				return allowColumnReorder;
			}
			set
			{
				allowColumnReorder = value;
			}
		}

		[
		Category("Appearance"),
		Description("Specifies what style border the control has."),
		DefaultValue(BorderStyle.Fixed3D)
		]
		public BorderStyle BorderStyle
		{
			get { return borderstyle; }
			set
			{ 
				borderstyle = value;
				if (borderstyle == BorderStyle.Fixed3D)
				{
					borderWid = 2;
				}
				else
				{
					borderWid = 1;
				}
				Invalidate(this.ClientRectangle);
			}
		}

		[
		Category("Appearance"),
		Description("Specifies wether to show column headers, and wether they respond to mouse clicks."),
		DefaultValue(ColumnHeaderStyle.Nonclickable)		 
		]
		public ColumnHeaderStyle HeaderStyle
		{
			get { return headerStyle; }
			set 
			{ 
				headerStyle = value; 
				Invalidate(this.ClientRectangle);
				if (headerStyle == ColumnHeaderStyle.None)
					headerBuffer = 0;
				else
					headerBuffer = 20;
			}
		}

		[
		Category("Behavior"),
		Description("Enables column tracking, highlighting the column gray when the mouse hovers over its header."),
		DefaultValue(false)
		]
		public bool ColumnTracking
		{
			get { return doColTracking; }
			set { doColTracking = value; }
		}

		[
		Category("Appearance"),
		Description("Specifies the color used for column hot-tracking."),
		DefaultValue(typeof(Color), "Color.WhiteSmoke")
		]
		public Color ColumnTrackColor
		{
			get { return colTrackColor; }
			set { colTrackColor = value; }
		}

		[
		Category("Appearance"),
		Description("Specifies the color used for the currently selected sorting column."),
		DefaultValue(typeof(Color), "Color.Gainsboro")
		]
		public Color ColumnSortColor
		{
			get { return colSortColor; }
			set { colSortColor = value; }
		}

		[
		Category("Behavior"),
		Description("Enables row tracking, highlighting the row gray when the mouse hovers over it."),
		DefaultValue(true)
		]
		public bool RowTracking
		{
			get { return doRowTracking; }
			set { doRowTracking = value; }
		}

		[
		Category("Appearance"),
		Description("Specifies the color used for row hot-tracking."),
		DefaultValue(typeof(Color), "Color.WhiteSmoke")
		]
		public Color RowTrackColor
		{
			get { return rowTrackColor; }
			set { rowTrackColor = value; }
		}

		[
		Category("Appearance"),
		Description("Specifies the color used for selected rows."),
		DefaultValue(typeof(Color), "SystemColors.Highlight")
		]
		public Color RowSelectColor
		{
			get { return rowSelectColor; }
			set { rowSelectColor = value; }
		}

		[
		Category("Behavior"),
		Description("Determines wether to highlight the full row or just the label of selected items."),
		DefaultValue(true)
		]
		public bool FullRowSelect
		{
			get { return fullRowSelect; }
			set { fullRowSelect = value; }
		}

		[
		Category("Appearance"),
		Description("Specifies the color used for grid lines."),
		DefaultValue(typeof(Color), "Color.WhiteSmoke")
		]
		public Color GridLineColor
		{
			get { return gridLineColor; }
			set { gridLineColor = value; }
		}

		[
		Category("Behavior"),
		Description("Specifies wether to show grid lines."),
		DefaultValue(false)
		]
		public bool GridLines
		{
			get { return gridLines; }
			set 
			{
				gridLines = value; 
				Invalidate(this.ClientRectangle);
			}
		}

		[
		Category("Behavior"),
		Description("The lists small image list (16x16).")
		]
		public ImageList SmallImageList
		{
			get { return smallImageList; }
			set 
			{
				smallImageList = value; 
				Invalidate(this.ClientRectangle);
			}
		}

		[
		Category("Behavior"),
		Description("The lists custom state image list (16x16).")
		]
		public ImageList StateImageList
		{
			get { return stateImageList; }
			set { stateImageList = value; }
		}

		[
		Category("Behavior"),
		Description("Determins wether primary name of each item is editable or not.")
		]
		public bool LabelEdit
		{
			get { return labelEdit; }
			set { labelEdit = value; }
		}

		[
		Category("Behavior"),
		Description("Determines wether to hide the selected rows when the control looses focus."),
		DefaultValue(true)
		]
		public bool HideSelection
		{
			get { return hideSelection; }
			set { hideSelection = value; }
		}
        
		[
		Category("Behavior"),
		Description("Determines wether to automatically select a row when the mouse is hovered over it for a short time."),
		DefaultValue(false)
		]
		public bool HoverSelection
		{
			get { return hoverSelection; }
			set { hoverSelection = value; }
		}

		[
		Category("Behavior"),
		Description("Determins wether the control will allow multiple selections."),
		DefaultValue(false)
		]
		public bool MultiSelect
		{
			get { return multiSelect; }
			set { multiSelect = value; }
		}

		[
		Category("Appearance"),
		Description("Specifies wether to use WindowsXP visual styles on this control."),
		DefaultValue(false)
		]
		public bool	VisualStyles
		{
			get 
			{
				bool val;
				try
				{
					val = visualStyles && Lyquidity.Controls.ExtendedListViews.uxTheme.Wrapper.IsAppThemed();
				}
				catch
				{
					val = visualStyles;
				}
				return val;
			}
			set
			{
				visualStyles = value;				
			}
		}

		[Browsable(false)]
		public ArrayList SelectedIndices
		{
			get { return selectedIndices; }
		}

		[Browsable(false)]
		// public ContainerListViewItemCollection SelectedItems
		public SelectedContainerListViewItemCollection SelectedItems
		{
			get { return selectedItems; }
		}

		protected bool InUpdateTransaction
		{
			get { return this.m_cUpdateTransactions.Count > 0; }
		}

		#endregion

		#region Overrides
		protected const int WM_KEYDOWN = 0x0100;
		protected const int VK_LEFT = 0x0025;
		protected const int VK_UP = 0x0026;
		protected const int VK_RIGHT = 0x0027;
		protected const int VK_DOWN = 0x0028;
/*
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if(components != null)
				{
					components.Dispose();
				}
			}
			base.Dispose( disposing );
		}
*/


		protected override void OnPaint(PaintEventArgs e)
		{
			Rectangle r = ClientRectangle;
			Graphics g = e.Graphics;

			DrawBackground(g, r);
			DrawRows(g, r);
			DrawHeaders(g, r);
			DrawExtra(g, r);			
			DrawBorder(g, r);			
		}

		protected override void OnResize(EventArgs e)
		{
			base.OnResize(e);
			GenerateHeaderRect();
			GenerateRowsRect();
			AdjustScrollbars();
           
			// invalidate subitem controls
			for (int i=0; i<items.Count; i++)
			{
				ContainerListViewItem lvi = ((ContainerListViewItem)items[i]);
				for (int j=0; j< lvi.SubItems.Count; j++)
				{
					ContainerSubListViewItem slvi = lvi.SubItems[j];
					if (slvi.ItemControl != null)
						slvi.ItemControl.Invalidate(slvi.ItemControl.ClientRectangle);
				}
			}
			Invalidate();										   
		}

		protected override void WndProc(ref Message m)
		{
			base.WndProc(ref m);
			if (m.Msg == (int)WinMsg.WM_GETDLGCODE)
			{
				// This line makes Arrow and Tab key events cause OnKeyXXX events to fire
				m.Result = new IntPtr((int)DialogCodes.DLGC_WANTCHARS | (int)DialogCodes.DLGC_WANTARROWS | m.Result.ToInt32());
			}
		}

		private int mouseMoveTicks = 0;
		protected override void OnMouseMove(MouseEventArgs e)
		{
			int i;

			base.OnMouseMove(e);

			//lastColHovered = -1;
			lastRowHovered = -1;

			// if the mouse button is currently
			// pressed down on a header column,
			// moving will attempt to move the
			// position of that column
			if (lastColPressed >= 0 && allowColumnReorder)
			{
				if (Math.Abs(e.X-lastClickedPoint.X) > 3)
				{
					// set rect for drag pos
				}				
			}
			else if (colScaleMode)
			{
				lastColHovered = -1;
				Cursor.Current = Cursors.VSplit;
				colScalePos = e.X-lastClickedPoint.X;
				if (colScalePos+colScaleWid <= 0)
					columns[scaledCol].Width = 1;
				else
					columns[scaledCol].Width = colScalePos+colScaleWid;				
				
				Invalidate();				
			}
			else
			{
				if (columns.Count > 0)
				{
					// check region mouse is in
					//  header			
					if (headerStyle != ColumnHeaderStyle.None)
					{
						//GenerateHeaderRect();
                    
						Cursor.Current = Cursors.Default;
						if (MouseInRect(e, headerRect))
						{
							if (columnRects.Length < columns.Count)
								GenerateColumnRects();

							for (i=0; i<columns.Count; i++)
							{							
								if (MouseInRect(e, columnRects[i]))
								{
									columns[i].Hovered = true;
									lastColHovered = i;
								}
								else
								{
									columns[i].Hovered = false;
								}

								if (MouseInRect(e, columnSizeRects[i]))
								{
									Cursor.Current = Cursors.VSplit;
								}
							}
							Invalidate();
							if (++mouseMoveTicks > 10)
							{
								mouseMoveTicks = 0;
								Thread.Sleep(1);
							}
							return;
						}					
					}
				}

				if (lastColHovered >= 0)
				{
					columns[lastColHovered].Hovered = false;
					lastColHovered = -1;
					Invalidate();
				}
			
				if (items.Count > 0)
				{
					//  rows
					GenerateRowsRect();				
					if (MouseInRect(e, rowsRect))
					{					
						GenerateRowRects();
						for (i=0; i<items.Count; i++)
						{					
							if (MouseInRect(e, rowRects[i]))
							{
								items[i].Hovered = true;
								lastRowHovered = i;
								break;
							}
							items[i].Hovered = false;
						}
						Invalidate();
					}
				}
			}

			if (++mouseMoveTicks > 10)
			{
				mouseMoveTicks = 0;
				Thread.Sleep(1);
			}
		}


		protected override void OnMouseDown(MouseEventArgs e)
		{
			base.OnMouseDown(e);

			if (!isFocused)
			{
				this.Focus();
				
				if (!captureFocusClick)
					return;
			}

			lastClickedPoint = new Point(e.X, e.Y);

			#region Columns Headers

			// determine if a header was pressed
			if (headerStyle != ColumnHeaderStyle.None)
			{
				if (MouseInRect(e, headerRect) && e.Button == MouseButtons.Left)
				{
					for (int i=0; i<columns.Count; i++)
					{
						columns[i].Pressed = false;						
						if (MouseInRect(e, columnSizeRects[i]) && items.Count > 0)
						{
							if (columns[i].ScaleStyle == ColumnScaleStyle.Slide)
							{
								// autosize column
								if (e.Clicks == 2 && e.Button == MouseButtons.Left && items.Count > 0)
								{
									int mwid = 0;
									int twid = 0;
									for (int j=0; j<items.Count; j++)
									{
										if (i > 0 && items[j].SubItems.Count > 0)
											twid = GetStringWidth(items[j].SubItems[i-1].Text) + 10;
										else if (i == 0)
											twid = GetStringWidth(items[j].Text) + 10;
										twid += 5;
										if (twid > mwid)
											mwid = twid;
									}
								
									twid = GetStringWidth(columns[i].Text);
									if (twid > mwid)
										mwid = twid;

									mwid += 5;

									columns[i].Width = mwid;
								}
									// scale column
								else
								{
									colScaleMode = true;
									colScaleWid = columnRects[i].Width;
									scaledCol = i;
								}
							}
						}
						else if (MouseInRect(e, columnRects[i]) && !MouseInRect(e, columnSizeRects[i]))
						{
							columns[i].Pressed = true;
							lastColPressed = i;
						}
					}
					Invalidate();
					return;
				}
			}

			#endregion


			// determine if a row was pressed			
			if (e.Button == MouseButtons.Left || e.Button == MouseButtons.Right)
			{
				if (MouseInRect(e, rowsRect) && items.Count>0)
				{
					#region Rows

					GenerateRowRects();

					for (int i=0; i<items.Count; i++)
					{

						if (MouseInRect(e, rowRects[i]))
						{
							// Found the rect in wich the user clicked so we have the item to select
							switch(this.multiSelectMode)
							{
								case MultiSelectMode.Single:
									this.MoveToIndex(i);
									break;
								case MultiSelectMode.Range:
									this.MoveToIndex(i);
									break;
								case MultiSelectMode.Selective:
									SelectiveSelection(i);
									break;
							}
						}
					}

				
					#endregion
				}		
			}
		}

		protected override void OnMouseUp(MouseEventArgs e)
		{
			int i;

			base.OnMouseUp(e);

			lastClickedPoint = Point.Empty;			

			if (colScaleMode)
			{
				//columns[scaledCol].Width += colScalePos;
				colScaleMode = false;
				colScalePos = 0;
				scaledCol = -1;
				colScaleWid = 0;

				AdjustScrollbars();
			}

			if (lastColPressed >= 0)
			{				
				columns[lastColPressed].Pressed = false;
				if (MouseInRect(e, columnRects[lastColPressed]) && !MouseInRect(e, columnSizeRects[lastColPressed]) && e.Button == MouseButtons.Left)
				{
					// invoke column click event
					OnColumnClick(new ColumnClickEventArgs(lastColPressed));

					// change currently selected column
					if (lastColSelected >= 0)
						columns[lastColSelected].Selected = false;

					columns[lastColPressed].Selected = true;					
					lastColSelected = lastColPressed;
				}
			}			
			lastColPressed = -1;

			// Check for context click
			if (e.Button == MouseButtons.Right)
			{
				if (MouseInRect(e, headerRect))
					OnHeaderMenuEvent(e);
				else if (MouseInRect(e, rowsRect))
				{
					for (i=0; i<items.Count; i++)
					{
						if (MouseInRect(e, rowRects[i]))
						{
							OnItemMenuEvent(e);
							break;
						}
					}
					if (i>=items.Count)
						OnContextMenuEvent(e);
				}
				else
					OnContextMenuEvent(e);
			}
		}

		protected override void OnMouseWheel(MouseEventArgs e)
		{
			if (e.Delta > 0)
			{
				if (vscrollBar.Visible)
					vscrollBar.Value = (vscrollBar.Value-vscrollBar.SmallChange*(e.Delta/100) < 0 ? 0 : vscrollBar.Value-vscrollBar.SmallChange*(e.Delta/100));
				else if (hscrollBar.Visible)
					hscrollBar.Value = (hscrollBar.Value-hscrollBar.SmallChange*(e.Delta/100) < 0 ? 0 : hscrollBar.Value-hscrollBar.SmallChange*(e.Delta/100));
			}
			else if (e.Delta < 0)
			{
				if (vscrollBar.Visible)
					vscrollBar.Value = (vscrollBar.Value-vscrollBar.SmallChange*(e.Delta/100) > vscrollBar.Maximum-vscrollBar.LargeChange ? vscrollBar.Maximum-vscrollBar.LargeChange : vscrollBar.Value-vscrollBar.SmallChange*(e.Delta/100));
				else if (hscrollBar.Visible)
					hscrollBar.Value = (hscrollBar.Value-hscrollBar.SmallChange*(e.Delta/100) > hscrollBar.Maximum-hscrollBar.LargeChange ? hscrollBar.Maximum-hscrollBar.LargeChange : hscrollBar.Value-hscrollBar.SmallChange*(e.Delta/100));
			}
		}

		protected override void OnKeyDown(KeyEventArgs e)
		{
			
			OnCheckShiftState(e);

			switch (e.KeyCode)
			{
				case Keys.Home:
					OnPageKeys(e);
					break;
				case Keys.End:
					OnPageKeys(e);
					break;
				case Keys.PageUp:
					OnPageKeys(e);
					break;
				case Keys.PageDown:
					OnPageKeys(e);
					break;
				case Keys.Up:
					OnUpDownKeys(e);
					break;
				case Keys.Down:
					OnUpDownKeys(e);
					break;
				default:
					base.OnKeyDown(e);
					break;
			}

			Invalidate(ClientRectangle);
		}

		protected virtual void OnPageKeys(KeyEventArgs e)
		{
			switch (e.KeyCode)
			{
				case Keys.Home:
					if (vscrollBar.Visible)
						vscrollBar.Value = 0;
					else 
					{
						MoveToIndex(0);
					}
					if (hscrollBar.Visible)
						hscrollBar.Value = 0;
					e.Handled = true;
					break;

				case Keys.End:
					if (vscrollBar.Visible)
						vscrollBar.Value = vscrollBar.Maximum-vscrollBar.LargeChange;
					else 
					{
						MoveToIndex(this.items.Count-1);
					}
					if (hscrollBar.Visible)
						hscrollBar.Value = hscrollBar.Maximum-hscrollBar.LargeChange;
					e.Handled = true;
					break;

				case Keys.PageUp:
					if (vscrollBar.Visible)
						vscrollBar.Value = (vscrollBar.LargeChange > vscrollBar.Value ? 0 : vscrollBar.Value-vscrollBar.LargeChange);
					else 
					{
						MoveToIndex(0);
					}
					e.Handled = true;
					break;

				case Keys.PageDown:
					if (vscrollBar.Visible)
						vscrollBar.Value = (vscrollBar.Value+vscrollBar.LargeChange > vscrollBar.Maximum-vscrollBar.LargeChange ? vscrollBar.Maximum-vscrollBar.LargeChange : vscrollBar.Value+vscrollBar.LargeChange);
					else
					{
						MoveToIndex(this.items.Count-1);
					}
					e.Handled = true;
					break;

			}
		}

		protected virtual void OnUpDownKeys(KeyEventArgs e)
		{
			if (focusedItem != null && items.Count > 0)
			{
				int iIndex = focusedIndex;

				switch (e.KeyCode)
				{
					case Keys.Down:
						iIndex++;
						break;

					case Keys.Up:
						iIndex--;
						break;
				}

				MoveToIndex(iIndex);
				e.Handled = true;
			}
		}

		protected virtual void OnCheckShiftState(KeyEventArgs e)
		{
			if (multiSelect)
			{
				if (e.KeyCode == Keys.ControlKey)
				{
					multiSelectMode = MultiSelectMode.Selective;
				}
				else if (e.KeyCode == Keys.ShiftKey)
				{
					multiSelectMode = MultiSelectMode.Range;
				}
			}

			if (!multiSelect && e.KeyCode == Keys.Return)
			{
				OnItemActivate(new EventArgs());
			}
		}

		protected override void OnKeyUp(KeyEventArgs e)
		{
			base.OnKeyUp(e);
			if (!e.Shift)
			{
				multiSelectMode = MultiSelectMode.Single;
			}
		}
		protected override void OnGotFocus(EventArgs e)
		{
			base.OnGotFocus(e);
			isFocused = true;
			Invalidate(this.ClientRectangle);
		}

		protected override void OnLostFocus(EventArgs e)
		{
			base.OnLostFocus(e);
			isFocused = false;
			Invalidate(this.ClientRectangle);
		}
		#endregion

		#region Helper Functions
		// wireing of child control events
		protected virtual void Attach()
		{
			items.MouseDown += new MouseEventHandler(OnSubControlMouseDown);
			columns.WidthResized += new EventHandler(OnColumnWidthResize);

			hscrollBar.ValueChanged += new EventHandler(OnScroll);
			vscrollBar.ValueChanged += new EventHandler(OnScroll);
		}

		protected virtual void Detach()
		{
			items.MouseDown -= new MouseEventHandler(OnSubControlMouseDown);
			columns.WidthResized -= new EventHandler(OnColumnWidthResize);

			hscrollBar.ValueChanged -= new EventHandler(OnScroll);
			vscrollBar.ValueChanged -= new EventHandler(OnScroll);
		}

		// Rectangle and region generation functions
		protected void GenerateColumnRects()
		{
			columnRects = new Rectangle[columns.Count];
			columnSizeRects = new Rectangle[columns.Count];
			int lwidth = 2-hscrollBar.Value;
			int colwid = 0;
			allColsWidth = 0;

			CalcSpringWids(ClientRectangle);
			for (int i=0; i<columns.Count; i++)
			{
				colwid = (columns[i].ScaleStyle == ColumnScaleStyle.Spring ? springWid : columns[i].Width);
				columnRects[i] = new Rectangle(lwidth, 2, colwid, 20);
				columnSizeRects[i] = new Rectangle(lwidth+colwid-4, ClientRectangle.Top+2, 4, headerBuffer);
				lwidth += colwid+1;
				allColsWidth += colwid;
			}
		}

		protected void GenerateHeaderRect()
		{
			headerRect = new Rectangle(this.ClientRectangle.Left+2-hscrollBar.Value, this.ClientRectangle.Top+2, this.ClientRectangle.Width-4, 20);
		}

		protected void GenerateRowsRect()
		{
			rowsRect = new Rectangle(this.ClientRectangle.Left+2-hscrollBar.Value, this.ClientRectangle.Top+(headerStyle == ColumnHeaderStyle.None ? 2 : 22), this.ClientRectangle.Width-4, this.ClientRectangle.Height-(headerStyle == ColumnHeaderStyle.None ? 2 : 22));
		}

		protected void GenerateRowRects()
		{
			rowRects = new Rectangle[items.Count];
			int lheight = 2+headerBuffer-vscrollBar.Value;
			int lftpos = ClientRectangle.Left+2;
			allRowsHeight = items.Count*rowHeight;
			for (int i=0; i<items.Count; i++)
			{
				rowRects[i] = new Rectangle(lftpos, lheight, ClientRectangle.Width-4, rowHeight-1);
				lheight += rowHeight;				
			}
		}

		// Adjust scroll bar settings and visibility
		private int vsize, hsize;
		public virtual void AdjustScrollbars()
		{
			if (items.Count > 0 || columns.Count > 0 && !colScaleMode)
			{
				allColsWidth = 0;
				for (int i=0; i<columns.Count; i++)
				{
					allColsWidth += columns[i].Width;
				}

				allRowsHeight = items.Count*rowHeight;

				vsize = vscrollBar.Width;
				hsize = hscrollBar.Height;

				hscrollBar.Left = this.ClientRectangle.Left+2;
				hscrollBar.Width = this.ClientRectangle.Width-vsize-4;
				hscrollBar.Top = this.ClientRectangle.Top+this.ClientRectangle.Height-hscrollBar.Height-2;
				hscrollBar.Maximum = allColsWidth;
				hscrollBar.LargeChange = (this.ClientRectangle.Width - vsize - 4 > 0 ? this.ClientRectangle.Width - vsize - 4 : 0);
				if (allColsWidth > this.ClientRectangle.Width-4-vsize)
					hscrollBar.Show();
				else
				{
					hscrollBar.Hide();
					hsize = 0;
					hscrollBar.Value = 0;
				}

				vscrollBar.Left = this.ClientRectangle.Left+this.ClientRectangle.Width-vscrollBar.Width-2;
				vscrollBar.Top = this.ClientRectangle.Top+headerBuffer+2;
				vscrollBar.Height = this.ClientRectangle.Height-hsize-headerBuffer-4;
				vscrollBar.Maximum = allRowsHeight;
				vscrollBar.LargeChange = (this.ClientRectangle.Height-headerBuffer-hsize-4 > 0 ? this.ClientRectangle.Height-headerBuffer-hsize-4 : 0);
				if (allRowsHeight > this.ClientRectangle.Height-headerBuffer-4-hsize)
					vscrollBar.Show();
				else
				{
					vscrollBar.Hide();
					vsize = 0;
					vscrollBar.Value = 0;
				}

				hscrollBar.Width = this.ClientRectangle.Width-vsize-4;
				hscrollBar.Top = this.ClientRectangle.Top+this.ClientRectangle.Height-hscrollBar.Height-2;
				hscrollBar.LargeChange = (this.ClientRectangle.Width - vsize - 4 > 0 ? this.ClientRectangle.Width - vsize - 4 : 0);
				if (allColsWidth > this.ClientRectangle.Width-4-vsize)
					hscrollBar.Show();
				else
				{
					hscrollBar.Hide();
					hsize = 0;
					hscrollBar.Value = 0;
				}
			}
		}
		// mouse movement/click helpers
		private void UnselectAll()
		{
			for (int i=0; i<items.Count; i++)
			{
				items[i].Focused = false;
				items[i].Selected = false;
			}
		}

		protected bool MouseInRect(MouseEventArgs me, Rectangle rect)
		{
			if ((me.X >= rect.Left && me.X <= rect.Left+rect.Width) 
				&& (me.Y >= rect.Top && me.Y <= rect.Top + rect.Height))
			{
				return true;
			}

			return false;
		}

		protected void MakeSelectedVisible()
		{
			if (focusedIndex > -1 && ensureVisible)
			{
				ContainerListViewItem item = items[focusedIndex];
				if (item != null && item.Focused && item.Selected)
				{
					Rectangle r = ClientRectangle;
					int i = items.IndexOf(item);
					int pos = r.Top+(rowHeight*i)+headerBuffer+2-vscrollBar.Value;
					try
					{

						if (pos+rowHeight+rowHeight > r.Top+r.Height)
						{
							vscrollBar.Value += Math.Abs((r.Top+r.Height)-(pos+rowHeight+rowHeight));
						}
						else if (pos < r.Top+headerBuffer)
						{
							vscrollBar.Value -= Math.Abs(r.Top+headerBuffer-pos);
						}
					}
					catch (ArgumentException)
					{
						if (vscrollBar.Value > vscrollBar.Maximum)
							vscrollBar.Value = vscrollBar.Maximum;
						else if (vscrollBar.Value < vscrollBar.Minimum)
							vscrollBar.Value = vscrollBar.Minimum;
					}
				}
			}
		}

		protected int GetStringWidth(string s)
		{
			Graphics g = Graphics.FromImage(new Bitmap(32, 32));
			SizeF strSize = g.MeasureString(s, this.Font);
			return (int)strSize.Width;
		}
		protected string TruncatedString(string s, int width, int offset, Graphics g)
		{
			string sprint = "";
			int swid;
			int i;
			SizeF strSize;

			try
			{
				strSize = g.MeasureString(s, this.Font);
				swid = ((int)strSize.Width);
				i=s.Length;

				for (i=s.Length; i>0 && swid > width-offset; i--)
				{
					strSize = g.MeasureString(s.Substring(0, i), this.Font);
					swid = ((int)strSize.Width);				
				}
			
				if (i < s.Length)
					if (i-3 <= 0)
						sprint = s.Substring(0, 1) + "...";
					else
						sprint = s.Substring(0, i-3) + "...";
				else
					sprint = s.Substring(0, i);
			}
			catch
			{
			}

			return sprint;
		}

		private void ShowSelectedItems()
		{
			if (firstSelected == focusedIndex)
			{
				ShowSelectedItem(firstSelected);
			} 
			else
				if (firstSelected > focusedIndex)
			{
				for(int iCtr=firstSelected; iCtr >= focusedIndex; iCtr--)
				{
					items[iCtr].Selected = true;
					selectedIndices.Add(iCtr);						
					selectedItems.Add(items[iCtr]);
				}
			} 
			else
				if (firstSelected < focusedIndex)
			{
				for(int iCtr=firstSelected; iCtr <= focusedIndex; iCtr++)
				{
					items[iCtr].Selected = true;
					selectedIndices.Add(iCtr);						
					selectedItems.Add(items[iCtr]);
				}

			}

		}

		private void ShowSelectedItem(int iCtr)
		{
			items[iCtr].Selected = true;
			selectedIndices.Add(iCtr);						
			selectedItems.Add(items[iCtr]);
		}

		private void MoveToIndex(int iIndex)
		{
			if ((iIndex < -1) | (iIndex >= this.items.Count)) return;

			if (iIndex == focusedIndex) return; /// Might occur on End on first item or
												///             on Home on the first item or
												///             if the user clicks on the current node

			UnselectAll();

			this.selectedItems.Clear();
			this.selectedIndices.Clear();

			if (focusedItem != null)
			{
				focusedItem.Selected = false;
				focusedItem.Focused = false;
			}

			focusedIndex = iIndex;

			if ((this.multiSelectMode == MultiSelectMode.Single) | (firstSelected == -1))
			{
				firstSelected = focusedIndex;
				items[focusedIndex].Focused = true;
				focusedItem = items[focusedIndex];
			}

			ShowSelectedItems();

			MakeSelectedVisible();
			OnSelectedIndexChanged(new EventArgs());

			Invalidate(this.ClientRectangle);
		}

		private void SelectiveSelection(int iIndex)
		{
			// This is a special case and will be used when the user clicks on an item
			// while the control key is pressed or presses the space bar button while an
			// item has the focus.

			// unfocus the previously focused item
			if (focusedIndex >= 0 && focusedIndex < items.Count)
				items[focusedIndex].Focused = false;

			ContainerListViewItem item = items[iIndex];

			if (items[iIndex].Selected)  // Already selected? De-select.
			{
				items[iIndex].Focused = false;
				items[iIndex].Selected = false;

				int i = selectedItems[item];
				selectedIndices.Remove(i);
				selectedItems.Remove(item);

				if (this.focusedItem == item) focusedItem = null;

				MakeSelectedVisible();

				OnSelectedIndexChanged(new EventArgs());
			}
			else
			{
				item.Focused = true;
				item.Selected = true;

				selectedIndices.Add(iIndex);
				selectedItems.Add(item);
				this.focusedItem = item;

				MakeSelectedVisible();

				OnSelectedIndexChanged(new EventArgs());
			}

			Invalidate();
		}

		// rendering helpers
		protected int springWid = 0;
		protected int springCnt = 0;

		protected void CalcSpringWids(Rectangle r)
		{
			springCnt = 0;
			springWid = (r.Width-borderWid*2);
			for (int i=0; i<columns.Count; i++)
			{
				if (columns[i].ScaleStyle == ColumnScaleStyle.Slide)
					springWid -= columns[i].Width;
				else
					springCnt++;
			}

			if (springCnt > 0 && springWid > 0)
				springWid = springWid/springCnt;
		}

		protected virtual void DrawBorder(Graphics g, Rectangle r)
		{
			// if running in XP with styles
			if (VisualStyles)
			{
				DrawBorderStyled(g, r);
				return;
			}

			Rectangle rect = this.ClientRectangle;
			if (borderstyle == BorderStyle.FixedSingle)
			{
				g.DrawRectangle(SystemPens.ControlDarkDark, r.Left, r.Top, r.Width, r.Height);
			}
			else if (borderstyle == BorderStyle.Fixed3D)
			{
				ControlPaint.DrawBorder3D(g, r.Left, r.Top, r.Width, r.Height, Border3DStyle.Sunken);
			}
			else if (borderstyle == BorderStyle.None)
			{
				// do not render any border
			}
		}

		protected virtual void DrawBackground(Graphics g, Rectangle r)
		{
			int i;
			int lwidth = 2, lheight=1;

			g.FillRectangle(new SolidBrush(BackColor), r);

			// Selected Column
			if (headerStyle == ColumnHeaderStyle.Clickable)
			{
				for (i=0; i<columns.Count; i++)
				{
					if (columns[i].Selected)
					{
						g.FillRectangle(new SolidBrush(colSortColor), r.Left+lwidth-hscrollBar.Value, r.Top+2+headerBuffer, columns[i].Width, r.Height-4-headerBuffer);
						break;
					}
					lwidth += columns[i].Width;
				}
			}

			// hot-tracked column
			if (doColTracking && (lastColHovered >= 0 && lastColHovered < columns.Count))
			{
				g.FillRectangle(new SolidBrush(colTrackColor), columnRects[lastColHovered].Left, 22, columnRects[lastColHovered].Width, r.Height-22);
			}

			// hot-tracked row
			if (doRowTracking && (lastRowHovered >= 0 && lastRowHovered < items.Count))
			{
				g.FillRectangle(new SolidBrush(rowTrackColor), r.Left+2, rowRects[lastRowHovered].Top, r.Left+r.Width-4, rowHeight);
			}		

			// gridlines
			if (gridLines)
			{
				Pen p = new Pen(new SolidBrush(gridLineColor), 1.0f);
				lwidth = lheight = 1;

				// vertical
				for (i=0; i<columns.Count; i++)
				{
					if (r.Left+lwidth+columns[i].Width >= r.Left+r.Width-2)
						break;

					g.DrawLine(p, r.Left+lwidth+columns[i].Width-hscrollBar.Value, r.Top+2+headerBuffer, r.Left+lwidth+columns[i].Width-hscrollBar.Value, r.Top+r.Height-2); 
					lwidth += columns[i].Width;
				}
				
				// horizontal
				for (i=0; i<items.Count; i++)
				{
					g.DrawLine(p, r.Left+2, r.Top+headerBuffer+rowHeight+lheight-vscrollBar.Value, r.Left+r.Width, r.Top+headerBuffer+rowHeight+lheight-vscrollBar.Value);
					lheight += rowHeight;
				}
			}
		}

		protected virtual void DrawHeaders(Graphics g, Rectangle r)
		{
			// if running in XP with styles
			if (VisualStyles)
			{
				DrawHeadersStyled(g, r);
				return;
			}

			if (headerStyle != ColumnHeaderStyle.None)
			{
				g.FillRectangle(new SolidBrush(SystemColors.Control), r.Left+2, r.Top+2, r.Width-2, headerBuffer);

				CalcSpringWids(r);

				// render column headers and trailing column header
				int last = 2;
				int i;

				int lp_scr = r.Left-hscrollBar.Value;
                
				g.Clip = new Region(new Rectangle(r.Left+2, r.Top+2, r.Width-5, r.Top+headerBuffer));
				for (i=0; i<columns.Count; i++)
				{
					if ((lp_scr+last+columns[i].Width > r.Left+2)
						&& (lp_scr+last < r.Left+r.Width-2))
					{						
						if (headerStyle == ColumnHeaderStyle.Clickable && columns[i].Pressed)
							System.Windows.Forms.ControlPaint.DrawButton(g, lp_scr+last, r.Top+2, columns[i].Width, r.Top+headerBuffer, ButtonState.Flat);
						else
							System.Windows.Forms.ControlPaint.DrawButton(g, lp_scr+last, r.Top+2, columns[i].Width, r.Top+headerBuffer, ButtonState.Normal);
					
						if (columns[i].Image != null)
						{
							g.DrawImage(columns[i].Image, new Rectangle(lp_scr+last+4, r.Top+3, 16, 16));						
							g.DrawString(TruncatedString(columns[i].Text, columns[i].Width, 25, g), this.Font, SystemBrushes.ControlText, (float)(lp_scr+last+22), (float)(r.Top+5));
						}
						else
						{
							string sp = "";
							if (columns[i].TextAlign == HorizontalAlignment.Left)
								g.DrawString(TruncatedString(columns[i].Text, columns[i].Width, 0, g), this.Font, SystemBrushes.ControlText, (float)(lp_scr+last+4), (float)(r.Top+5));
							else if (columns[i].TextAlign == HorizontalAlignment.Right)
							{
								sp = TruncatedString(columns[i].Text, columns[i].Width, 0, g);
								g.DrawString(sp, this.Font, SystemBrushes.ControlText, (float)(lp_scr+last+columns[i].Width-Helpers.StringTools.MeasureDisplayStringWidth(g, sp, this.Font)-4), (float)(r.Top+5));
							}
							else
							{
								sp = TruncatedString(columns[i].Text, columns[i].Width, 0, g);
								g.DrawString(sp, this.Font, SystemBrushes.ControlText, (float)(lp_scr+last+(columns[i].Width/2)-(Helpers.StringTools.MeasureDisplayStringWidth(g, sp, this.Font)/2)), (float)(r.Top+5));
							}
						}
					}
					last += columns[i].Width;
				}

				// only render trailing column header if the end of the
				// last column ends before the boundary of the listview 
				if (!(lp_scr+last+5 > r.Left+r.Width))
				{
					g.Clip = new Region(new Rectangle(r.Left+2, r.Top+2, r.Width-5, r.Top+headerBuffer));
					System.Windows.Forms.ControlPaint.DrawButton(g, lp_scr+last, r.Top+2, r.Width-(r.Left+last)-3+hscrollBar.Value , r.Top+headerBuffer, ButtonState.Normal);
				}
			}
		}		

		protected virtual void DrawRows(Graphics g, Rectangle r)
		{
			// Don't paint if in transaction
			if (this.InUpdateTransaction) return;

			CalcSpringWids(r);

			if (columns.Count > 0)
			{
				// render listview item rows
				int last;
				int j, i;

				// set up some commonly used values
				// to cut down on cpu cycles and boost
				// the lists performance
				int lp_scr = r.Left+2-hscrollBar.Value;	// left viewport position less scrollbar position
				int lp = r.Left+2;						// left viewport position
				int tp_scr = r.Top+2+headerBuffer-vscrollBar.Value;	// top viewport position less scrollbar position
				int tp = r.Top+2+headerBuffer;			// top viewport position

				for (i=0; i<items.Count; i++)
				{
					j=0;
					last = 0;

					int iColWidth = (columns[j].ScaleStyle == ColumnScaleStyle.Spring ? springWid : columns[j].Width); // BMS 2003-05-24

					// render item, but only if its within the viewport
					if ((tp_scr+(rowHeight*i)+2 > r.Top+2) 
						&& (tp_scr+(rowHeight*i)+2 < r.Top+r.Height-2-hsize))
					{
						g.Clip = new Region(new Rectangle(r.Left+2, r.Top+headerBuffer+2, r.Width-vsize-5, r.Height-hsize-5));

						int rowSelWidth = (allColsWidth < (r.Width-5) || hscrollBar.Visible ? allColsWidth : r.Width-5);
						if (!fullRowSelect)
							rowSelWidth = iColWidth /* BMS 2003-05-24 */ -2;

						// render selected item highlights
						if (items[i].Selected && isFocused)
						{
							g.FillRectangle(new SolidBrush(rowSelectColor), lp, tp_scr+(rowHeight*i), rowSelWidth, rowHeight);
						}
						else if (items[i].Selected && !isFocused && hideSelection)
						{
							ControlPaint.DrawFocusRectangle(g, new Rectangle(lp_scr, tp_scr+(rowHeight*i), rowSelWidth, rowHeight));
						}
						else if (items[i].Selected && !isFocused && !hideSelection)
						{
							g.FillRectangle(SystemBrushes.Control, lp, tp_scr+(rowHeight*i), rowSelWidth, rowHeight);
						}

						if (items[i].Focused && multiSelect && isFocused)
						{
							ControlPaint.DrawFocusRectangle(g, new Rectangle(lp_scr, tp_scr+(rowHeight*i), rowSelWidth, rowHeight));
						}

						// render item
						if ((lp_scr+2+iColWidth /* BMS 2003-05-24 */  > r.Left+4))
						{	
							g.Clip = new Region(new Rectangle(lp+2, tp, (iColWidth /* BMS 2003-05-24 */ > r.Width ? r.Width-6 : iColWidth /* BMS 2003-05-24 */ -2), r.Height-hsize-5));

							if (smallImageList != null && (items[i].ImageIndex >= 0 && items[i].ImageIndex < smallImageList.Images.Count))
							{
								smallImageList.Draw(g, lp_scr+4, tp_scr+(rowHeight*i)+1, 16, 16, items[i].ImageIndex);					
								g.DrawString(TruncatedString(items[i].Text, iColWidth /* BMS 2003-05-24 */ , 18, g), this.Font, (items[i].Selected && isFocused ? SystemBrushes.HighlightText : new SolidBrush(ForeColor)), (float)(lp_scr+22), (float)(tp_scr+(rowHeight*i)+2));
							}
							else
							{
								g.DrawString(TruncatedString(items[i].Text, iColWidth /* BMS 2003-05-24 */ , 0, g), this.Font, (items[i].Selected && isFocused ? SystemBrushes.HighlightText : new SolidBrush(ForeColor)), (float)(lp_scr+4), (float)(tp_scr+(rowHeight*i)+2));
							}
						}
					}

					// render sub items
					if (columns.Count > 0)
					{
						for (j=0; j<items[i].SubItems.Count && j<columns.Count-1; j++)
						{
							iColWidth = (columns[j].ScaleStyle == ColumnScaleStyle.Spring ? springWid : columns[j].Width);
							int iColWidthPlus1 = (columns[j+1].ScaleStyle == ColumnScaleStyle.Spring ? springWid : columns[j+1].Width);
							last += iColWidth;  // BMS 2003-05-24

							// only render subitem if it is visible within the viewport
							if ((lp_scr+2+last+iColWidthPlus1     /* BMS 2003-05-24 */ > r.Left+4) 
								&& (lp_scr+last < r.Left+r.Width-4)
								&& (tp_scr+(rowHeight*i)+2 >= tp) /* BMS 2003-05-25 */
								&& (tp_scr+(rowHeight*i)+2 < r.Top+r.Height-2-hsize))
							{
								g.Clip = new Region(new Rectangle(lp_scr+last+4, tp, (last+iColWidthPlus1 /* BMS 2003-05-24 */ > r.Width-6 ? r.Width-6 : iColWidthPlus1-6), r.Height-hsize-5)); 
								Control c = items[i].SubItems[j].ItemControl;
								if (c != null)
								{
									c.Visible = true; // (tp_scr+(rowHeight*i)+2) > tp;  // Can only be visible when the 
									c.Location = new Point(lp_scr+last+2, tp_scr+(rowHeight*i)+2);
									c.ClientSize = new Size(iColWidthPlus1 /* BMS 2003-05-24 */ -6, rowHeight-4); 
									c.Parent = this;
								}						
								else
								{
									string sp = "";
									if (columns[j+1].TextAlign == HorizontalAlignment.Left)
									{
										g.DrawString(TruncatedString(items[i].SubItems[j].Text, iColWidthPlus1 /* BMS 2003-05-24 */, 12, g), this.Font, (items[i].Selected && isFocused ? SystemBrushes.HighlightText : SystemBrushes.WindowText), (float)(lp_scr+last+4), (float)(tp_scr+(rowHeight*i)+2));
									}
									else if (columns[j+1].TextAlign == HorizontalAlignment.Right)
									{
										sp = TruncatedString(items[i].SubItems[j].Text, iColWidthPlus1 /* BMS 2003-05-24 */, 12, g);
										g.DrawString(sp, this.Font, (items[i].Selected && isFocused ? SystemBrushes.HighlightText : new SolidBrush(ForeColor)), (float)(lp_scr+last+iColWidthPlus1 /* BMS 2003-05-24 */-Helpers.StringTools.MeasureDisplayStringWidth(g, sp, this.Font)-2), (float)(tp_scr+(rowHeight*i)+2));
									}
									else
									{
										sp = TruncatedString(items[i].SubItems[j].Text, iColWidthPlus1 /* BMS 2003-05-24 */ , 12, g);
										g.DrawString(sp, this.Font, (items[i].Selected && isFocused ? SystemBrushes.HighlightText : new SolidBrush(ForeColor)), (float)(lp_scr+last+(iColWidthPlus1 /* BMS 2003-05-24 */ /2)-(Helpers.StringTools.MeasureDisplayStringWidth(g, sp, this.Font)/2)), (float)(tp_scr+(rowHeight*i)+2));
									}
								}
							}
							else /* BMS 2003-05-25 */
							{
								Control c = items[i].SubItems[j].ItemControl;
								if (c != null)
								{
									c.Visible = false;
								}						
							}
						}
					}
				}
			}
		}

		protected virtual void DrawExtra(Graphics g, Rectangle r)
		{
			if (hscrollBar.Visible && vscrollBar.Visible)
			{
				g.ResetClip();
				g.FillRectangle(SystemBrushes.Control, r.Width-vscrollBar.Width-borderWid, r.Height-hscrollBar.Height-borderWid, vscrollBar.Width, hscrollBar.Height);
			}
		}
		// visual styles rendering functions
		protected virtual void DrawBorderStyled(Graphics g, Rectangle r)
		{
			Region oldreg = g.Clip;
			g.Clip = new Region(r);
			g.DrawRectangle(new Pen(SystemBrushes.InactiveBorder), r.Left, r.Top, r.Width-1, r.Height-1);
			g.DrawRectangle(new Pen(BackColor), r.Left+1, r.Top+1, r.Width-3, r.Height-3);
			g.Clip = oldreg;
		}

		protected virtual void DrawHeadersStyled(Graphics g, Rectangle r)
		{
			if (headerStyle != ColumnHeaderStyle.None)
			{
				int colwid = 0;
				int i;
				int last = 2;
				CalcSpringWids(r);

				int lp_scr = r.Left-hscrollBar.Value;
				int lp = r.Left;
				int tp = r.Top+2;

				System.IntPtr hdc = g.GetHdc();
				try
				{
					// render column headers and trailing column header					
					for (i=0; i<columns.Count; i++)
					{
						colwid = (columns[i].ScaleStyle == ColumnScaleStyle.Spring ? springWid : columns[i].Width);
						if (headerStyle == ColumnHeaderStyle.Clickable && columns[i].Pressed)
							Lyquidity.Controls.ExtendedListViews.uxTheme.Wrapper.DrawBackground("HEADER", "HEADERITEM", "PRESSED", hdc,
								lp_scr+last, tp, 
								colwid, headerBuffer,
								lp, tp, r.Width-6, headerBuffer);
						else if (headerStyle != ColumnHeaderStyle.None && columns[i].Hovered)
							Lyquidity.Controls.ExtendedListViews.uxTheme.Wrapper.DrawBackground("HEADER", "HEADERITEM", "HOT", hdc,
								lp_scr+last, tp, 
								colwid, headerBuffer,  
								lp, tp, r.Width-6, headerBuffer);
						else
							Lyquidity.Controls.ExtendedListViews.uxTheme.Wrapper.DrawBackground("HEADER", "HEADERITEM", "NORMAL", hdc,
								lp_scr+last, tp, 
								colwid, headerBuffer,  
								lp, tp, r.Width-6, headerBuffer);
						last += colwid;
					}
					// only render trailing column header if the end of the
					// last column ends before the boundary of the listview 
					if (!(r.Left+last+2-hscrollBar.Value > r.Left+r.Width))
					{
						Lyquidity.Controls.ExtendedListViews.uxTheme.Wrapper.DrawBackground("HEADER", "HEADERITEM", "NORMAL", hdc, lp_scr+last, tp, r.Width-last-2+hscrollBar.Value, headerBuffer,  r.Left, /* r.Top BMS 2003/05/22 */ tp, r.Width, headerBuffer);
					}
				}
				catch
				{
				}
				finally
				{
					g.ReleaseHdc(hdc);
				}

				last = 1;
				for (i=0; i<columns.Count; i++)
				{
					colwid = (columns[i].ScaleStyle == ColumnScaleStyle.Spring ? springWid : columns[i].Width);
					g.Clip = new Region(new Rectangle(lp_scr+last+2, tp, (r.Left+last+colwid > r.Left+r.Width ? (r.Width - (r.Left+last))-4 : colwid-2)+hscrollBar.Value, r.Top+headerBuffer));
					if (columns[i].Image != null)
					{
						g.DrawImage(columns[i].Image, new Rectangle(lp_scr+last+4, r.Top+3, 16, 16));
						g.DrawString(TruncatedString(columns[i].Text, colwid, 25, g), this.Font, SystemBrushes.ControlText, (float)(r.Left+last+22-hscrollBar.Value), (float)(r.Top+5));
					}
					else
					{
						string sp = TruncatedString(columns[i].Text, colwid, 0, g);
						if (columns[i].TextAlign == HorizontalAlignment.Left)
						{
							g.DrawString(TruncatedString(columns[i].Text, colwid, 0, g), this.Font, SystemBrushes.ControlText, (float)(last+4-hscrollBar.Value), (float)(r.Top+5));
						}
						else if (columns[i].TextAlign == HorizontalAlignment.Right)
						{
							g.DrawString(sp, this.Font, SystemBrushes.ControlText, (float)(last+colwid-Helpers.StringTools.MeasureDisplayStringWidth(g, sp, this.Font)-4-hscrollBar.Value), (float)(r.Top+5));
						}
						else
						{
							g.DrawString(sp, this.Font, SystemBrushes.ControlText, (float)(last+(colwid/2)-(Helpers.StringTools.MeasureDisplayStringWidth(g, sp, this.Font)/2)-hscrollBar.Value), (float)(r.Top+5));
						}
					}
					last += colwid;
				}
			}
		}

		#endregion
	}
	#endregion

	#region Type Converters
	public class ToggleColumnHeaderConverter: TypeConverter
	{
		public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
		{
			if (destinationType == typeof(InstanceDescriptor))
			{
				return true;
			}
			return base.CanConvertTo(context, destinationType);
		}

		public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
		{
			if (destinationType == typeof(InstanceDescriptor) && value is ToggleColumnHeader)
			{
				ToggleColumnHeader lvi = (ToggleColumnHeader)value;

				ConstructorInfo ci = typeof(ToggleColumnHeader).GetConstructor(new Type[] {});
				if (ci != null)
				{
					return new InstanceDescriptor(ci, null, false);
				}
			}
			return base.ConvertTo(context, culture, value, destinationType);
		}
	}
	
	public class ListViewItemConverter: TypeConverter
	{
		public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
		{
			if (destinationType == typeof(InstanceDescriptor))
			{
				return true;
			}
			return base.CanConvertTo(context, destinationType);
		}

		public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
		{
			if (destinationType == typeof(InstanceDescriptor) && value is ContainerListViewItem)
			{
				ContainerListViewItem lvi = (ContainerListViewItem)value;

				ConstructorInfo ci = typeof(ContainerListViewItem).GetConstructor(new Type[] {});
				if (ci != null)
				{
					return new InstanceDescriptor(ci, null, false);
				}
			}
			return base.ConvertTo(context, culture, value, destinationType);
		}
	}

	public class SubListViewItemConverter: TypeConverter
	{
		public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
		{
			if (destinationType == typeof(InstanceDescriptor))
			{
				return true;
			}
			return base.CanConvertTo(context, destinationType);
		}

		public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
		{
			if (destinationType == typeof(InstanceDescriptor) && value is ContainerSubListViewItem)
			{
				ContainerSubListViewItem lvi = (ContainerSubListViewItem)value;

				ConstructorInfo ci = typeof(ContainerSubListViewItem).GetConstructor(new Type[] {});
				if (ci != null)
				{
					return new InstanceDescriptor(ci, null, false);
				}
			}
			return base.ConvertTo(context, culture, value, destinationType);
		}
	}
	#endregion
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United Kingdom United Kingdom
Independent software development for clients including Microsoft. Design and development of modules for financial reporting and business intelligence.

Comments and Discussions