Click here to Skip to main content
15,897,187 members
Articles / Programming Languages / C#

TreeListView

Rate me:
Please Sign up or sign in to vote.
4.79/5 (147 votes)
31 Aug 2003Ms-PL5 min read 1.6M   38.5K   436  
A custom control that ties a ListView and a TreeView together
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.APIs;
using System.Diagnostics;

namespace System.Windows.Forms
{
	/// <summary>
	/// TreeListView control (simulates a TreeView in a ListView)
	/// </summary>
	public class TreeListView : System.Windows.Forms.ListView
	{
		#region Private delegates
		private delegate void VoidHandler();
		private delegate int IntHandler();
		#endregion

		#region Events, Delegates, and internal calls
			#region Events
		/// <summary>
		/// Occurs when the label for an item is edited by the user.
		/// </summary>
		[Description("Occurs when the label for an item is edited by the user.")]
		public new event TreeListViewLabelEditEventHandler AfterLabelEdit;
		/// <summary>
		/// Occurs when the user starts editing the label of an item.
		/// </summary>
		[Description("Occurs when the user starts editing the label of an item."),Browsable(true)]
		public new event TreeListViewBeforeLabelEditEventHandler BeforeLabelEdit;
		/// <summary>
		/// Occurs before the tree node is collapsed.
		/// </summary>
		[Description("Occurs before the tree node is collapsed")]
		public event TreeListViewCancelEventHandler BeforeExpand;
		/// <summary>
		/// Occurs before the tree node is collapsed.
		/// </summary>
		[Description("Occurs before the tree node is collapsed")]
		public event TreeListViewCancelEventHandler BeforeCollapse;
		/// <summary>
		/// Occurs after the tree node is expanded
		/// </summary>
		[Description("Occurs after the tree node is expanded")]
		public event TreeListViewEventHandler AfterExpand;
		/// <summary>
		/// Occurs after the tree node is collapsed
		/// </summary>
		[Description("Occurs after the tree node is collapsed")]
		public event TreeListViewEventHandler AfterCollapse;
			#endregion
			#region On???
		/// <summary>
		/// Raises the AfterLabelEdit event.
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnAfterLabelEdit(TreeListViewLabelEditEventArgs e)
		{
			if(AfterLabelEdit != null) AfterLabelEdit(this, e);
		}
		/// <summary>
		/// Please use OnAfterLabelEdit(TreeListViewLabelEditEventArgs e)
		/// </summary>
		/// <param name="e"></param>
		protected override void OnAfterLabelEdit(LabelEditEventArgs e)
		{
			throw(new Exception("Please use OnAfterLabelEdit(TreeListViewLabelEditEventArgs e)"));
		}
		/// <summary>
		/// Raises the BeforeLabelEdit event.
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnBeforeLabelEdit(TreeListViewBeforeLabelEditEventArgs e)
		{
			if(BeforeLabelEdit != null) BeforeLabelEdit(this, e);
		}
		/// <summary>
		/// Please use OnBeforeLabelEdit(TreeListViewLabelEditEventArgs e)
		/// </summary>
		/// <param name="e"></param>
		protected override void OnBeforeLabelEdit(LabelEditEventArgs e)
		{
			throw(new Exception("Please use OnBeforeLabelEdit(TreeListViewLabelEditEventArgs e)"));
		}
		/// <summary>
		/// Raises the BeforeExpand event.
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnBeforeExpand(TreeListViewCancelEventArgs e)
		{
			if(BeforeExpand != null) BeforeExpand(this, e);
		}
		/// <summary>
		/// Raises the AfterExpand event.
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnAfterExpand(TreeListViewEventArgs e)
		{
			if(AfterExpand != null) AfterExpand(this, e);
		}
		/// <summary>
		/// Raises the BeforeCollapse event.
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnBeforeCollapse(TreeListViewCancelEventArgs e)
		{
			if(BeforeCollapse != null) BeforeCollapse(this, e);
		}
		/// <summary>
		/// Raises the AfterCollapse event.
		/// </summary>
		/// <param name="e"></param>
		protected virtual void OnAfterCollapse(TreeListViewEventArgs e)
		{
			if(AfterCollapse != null) AfterCollapse(this, e);
		}
			#endregion
			#region Internal calls
		internal void RaiseBeforeExpand(TreeListViewCancelEventArgs e)
		{
			OnBeforeExpand(e);
		}
		internal void RaiseBeforeCollapse(TreeListViewCancelEventArgs e)
		{
			OnBeforeCollapse(e);
		}
		internal void RaiseAfterExpand(TreeListViewEventArgs e)
		{
			OnAfterExpand(e);
		}
		internal void RaiseAfterCollapse(TreeListViewEventArgs e)
		{
			OnAfterCollapse(e);
		}
			#endregion
		#endregion
		#region Modified properties
			#region FullRowSelect
			/// <summary>
			/// Gets or sets a value indicating whether clicking an item selects all its subitems
			/// </summary>
			[Browsable(true), Description("Gets or sets a value indicating whether clicking an item selects all its subitems"),
			DefaultValue(true)]
			new public bool FullRowSelect
			{
				get
				{
					return base.FullRowSelect;
				}
				set
				{
					base.FullRowSelect = value;
				}
			}
			#endregion
			#region StateImageList
			/// <summary>
			/// Not supported
			/// </summary>
			[Browsable(false)]
			new public ImageList StateImageList
			{
				get{return base.StateImageList;}
				set{base.StateImageList = value;}
			}
			#endregion
			#region LargeImageList
			/// <summary>
			/// Not supported
			/// </summary>
			[Browsable(false)]
			new public ImageList LargeImageList
			{
				get{return base.LargeImageList;}
				set{base.LargeImageList = value;}
			}
			#endregion
			#region SmallImageList
			/// <summary>
			/// Gets or sets the ImageList to use when displaying items as small icons in the control
			/// </summary>
			[Browsable(false)]
			new public ImageList SmallImageList
			{
				get{return base.SmallImageList;}
				set{base.SmallImageList = value;}
			}
			#endregion
			#region Sorting
			/// <summary>
			/// Get or set the sort order
			/// </summary>
			[DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
			[Browsable(true), Description("Get or Set the sort order")]
			new public SortOrder Sorting
			{
				get{return(Items.SortOrder);}
				set{Items.SortOrderRecursively = value;}
			}
			#endregion
			#region ExpandMethod
			private TreeListViewExpandMethod _expandmethod;
			/// <summary>
			/// Get or set the expand method
			/// </summary>
			[Browsable(true), Description("Get or Set the expand method")]
			public TreeListViewExpandMethod ExpandMethod
			{
				get{return(_expandmethod);}
				set{_expandmethod = value;}
			}
			#endregion
			#region View
			/// <summary>
			/// View (always Details)
			/// </summary>
			[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
			[Browsable(false)]
			public new View View
			{
				get{return(base.View);}
				set{base.View = View.Details;}
			}
			#endregion
			#region Items
			/// <summary>
			/// Items of the TreeListView
			/// </summary>
			//		[Browsable(true),
			//		Editor(typeof(TreeListViewItemsEditor), typeof(System.Drawing.Design.UITypeEditor))]
			[Browsable(false)]
			[Description("Items of the TreeListView")]
			new public TreeListViewItemCollection Items
			{
				get{return(_items);}
			}
			#endregion
			#region SelectedItems
			/// <summary>
			/// Get currently selected items
			/// </summary>
			[Browsable(false)]
			new public SelectedTreeListViewItemCollection SelectedItems
			{
				get
				{
					SelectedTreeListViewItemCollection sel = new SelectedTreeListViewItemCollection(this);
					return(sel);
				}
			}
			#endregion
			#region CheckedItems
			/// <summary>
			/// Get currently checked items
			/// </summary>
			[Browsable(false)]
			public new TreeListViewItem[] CheckedItems
			{
				get
				{
					TreeListViewItem[] array = new TreeListViewItem[base.CheckedIndices.Count];
					for(int i = 0 ; i < base.CheckedIndices.Count ; i++)
						array[i] = (TreeListViewItem) base.CheckedItems[i];
					return(array);
				}
			}
			#endregion
			#region MultiSelect
			/// <summary>
			/// Not supported (always false)
			/// </summary>
			[DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)]
			[Browsable(false)]
			new public bool MultiSelect
			{
				get{return(false);}
				set{base.MultiSelect = false;}
			}
			#endregion
			#region FocusedItem
			/// <summary>
			/// Gets the item in the control that currently has focus.
			/// </summary>
			new public TreeListViewItem FocusedItem
			{
				get{return (TreeListViewItem) base.FocusedItem;}
			}
			#endregion
		#endregion
		#region Properties
			#region Private Properties
			int _comctl32Version;
			private DateTime _lastdoubleclick;
			private EditItemInformations _lastitemclicked;
			private CustomEdit _customedit;
			private TreeListViewItemCollection _items;
			private System.ComponentModel.IContainer components;
			private System.Windows.Forms.ImageList imageList1;
			internal bool FreezeCheckBoxes = false;
			private Point _mousescrollposition = new Point(0, 0);
			private DateTime _dblclicktime = DateTime.Now;
			#endregion

			#region EditedItem
			private EditItemInformations _editeditem = new EditItemInformations();
			private System.Windows.Forms.ImageList plusMinusImageList;
			/// <summary>
			/// Gets the informations of the current edited item
			/// </summary>
			[Browsable(false)]
			public EditItemInformations EditedItem
			{
				get{return _editeditem;}
			}
			#endregion
			#region InEdit
			private bool _inedit;
			/// <summary>
			/// Gets wether an item is currently edited
			/// </summary>
			[Browsable(false)]
			public bool InEdit
			{
				get{return _inedit;}
			}
			#endregion
			#region ItemsCount
			/// <summary>
			/// Get the number of items recursively
			/// </summary>
			[Browsable(false)]
			public int ItemsCount
			{
				get
				{
					return (int) Invoke(new IntHandler(CountItems));
				}
			}
			private int CountItems()
			{
				TreeListViewItem[] items = _items.ToArray();
				int count = items.Length;
				foreach(TreeListViewItem item in items) count += item.ChildrenCount;
				return count;
			}
			#endregion
			#region Comparer
			/// <summary>
			/// Get or set the comparer
			/// </summary>
			[Browsable(false)]
			public ITreeListViewItemComparer Comparer
			{
				get{return(Items.Comparer);}
				set{Items.Comparer = value;}
			}
			#endregion
			#region ShowPlusMinus
			private bool _showplusminus = true;
			/// <summary>
			/// Gets or sets a value indicating whether plus-sign (+) and minus-sign (-) buttons are displayed next to TreeListView that contain child TreeListViews
			/// </summary>
			[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible), DefaultValue(true)]
			[Browsable(true), Description("Gets or sets a value indicating whether plus-sign (+) and minus-sign (-) buttons are displayed next to TreeListView that contain child TreeListViews")]
			public bool ShowPlusMinus
			{
				get
				{
					return _showplusminus;
				}
				set
				{
					if(_showplusminus == value) return;
					_showplusminus = value;
					if(Created) Invoke(new VoidHandler(VisChanged));
				}
			}
			#endregion
			#region PlusMinusLineColor
			private Color _plusMinusLineColor = Color.DarkGray;
			/// <summary>
			/// Gets or Sets the color of the lines if ShowPlusMinus property is enabled
			/// </summary>
			[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible), DefaultValue(typeof(Color), "DarkGray")]
			[Browsable(true), Description("Gets or Sets the color of the lines if ShowPlusMinus property is enabled")]
			public Color PlusMinusLineColor
			{
				get
				{
					return _plusMinusLineColor;
				}
				set
				{
					_plusMinusLineColor = value;
					if(Created) Invalidate();
				}
			}
			#endregion
			#region UseXPHighlightStyle
			private bool _useXPHighLightStyle = true;
			/// <summary>
			/// Gets or Sets whether the control draw XP-Style highlight color
			/// </summary>
			[DesignerSerializationVisibility(DesignerSerializationVisibility.Visible), DefaultValue(true)]
			[Browsable(true), Description("Gets or Sets whether the control draw XP-Style highlight color")]
			public bool UseXPHighlightStyle
			{
				get
				{
					return _useXPHighLightStyle;
				}
				set
				{
					_useXPHighLightStyle = value;
					if(Created) Invalidate();
				}
			}
			#endregion
		#endregion

		#region Constructor
		/// <summary>
		/// Create a new instance of a TreeListView
		/// </summary>
		public TreeListView()
		{
			InitializeComponent();
			_items = new TreeListViewItemCollection(this);
			base.MultiSelect = false;
//			if(SmallImageList == null) base.SmallImageList = imageList1;
			if(!IsHandleCreated) CreateHandle();
			_comctl32Version = APIsComctl32.GetMajorVersion();

			// Set double buffer style
			SetStyle(ControlStyles.DoubleBuffer, true);
		}
		#endregion
		#region WndProc
		/// <summary>
		/// WndProc
		/// </summary>
		/// <param name="m"></param>
		protected override void WndProc(ref System.Windows.Forms.Message m)
		{
			TreeListViewItem item = null; Rectangle rec; APIsStructs.LV_ITEM lvitem;
			switch((APIsEnums.WindowMessages) m.Msg)
			{
				#region NOTIFY
				case APIsEnums.WindowMessages.NOTIFY:
				case (APIsEnums.WindowMessages) APIsEnums.ReflectedMessages.NOTIFY:
					APIsStructs.NMHDR nmhdr = (APIsStructs.NMHDR) m.GetLParam(typeof(APIsStructs.NMHDR));
					APIsStructs.NMHEADER nmheader =(APIsStructs.NMHEADER) m.GetLParam(typeof(APIsStructs.NMHEADER));
					switch((APIsEnums.ListViewNotifications) nmhdr.code)
					{
						#region APIsEnums.ListViewNotifications.BEGINLABELEDIT
						case APIsEnums.ListViewNotifications.BEGINLABELEDIT:
							System.Diagnostics.Debug.Assert(FocusedItem != null);
							// Cancel label edit if the message is sent just after a double click
							if(_lastdoubleclick.AddMilliseconds(450) > DateTime.Now)
							{
								Message canceledit = Message.Create(Handle, (int) APIsEnums.ListViewMessages.CANCELEDITLABEL, IntPtr.Zero, IntPtr.Zero);
								WndProc(ref canceledit);
								m.Result = (IntPtr) 1;
								return;
							}
							item = FocusedItem;
							int column = 0;
							if(_lastitemclicked.Item == item &&
								_lastitemclicked.CreationTime.AddMilliseconds(2*SystemInformation.DoubleClickTime) > DateTime.Now)
								column = _lastitemclicked.ColumnIndex;
							if(column == -1) column = 0;
							// Add subitems if needed
							while(item.SubItems.Count-1 < column) item.SubItems.Add("");
							TreeListViewBeforeLabelEditEventArgs beforeed = new TreeListViewBeforeLabelEditEventArgs(
								FocusedItem, column, item.SubItems[column].Text);
							OnBeforeLabelEdit(beforeed);
							if(beforeed.Cancel)
							{
								Message canceledit = Message.Create(Handle, (int) APIsEnums.ListViewMessages.CANCELEDITLABEL, IntPtr.Zero, IntPtr.Zero);
								WndProc(ref canceledit);
								m.Result = (IntPtr) 1;
								return;
							}
							_inedit = true;
							// Get edit handle
							Message mess = Message.Create(Handle, (int)APIsEnums.ListViewMessages.GETEDITCONTROL, IntPtr.Zero, IntPtr.Zero);
							WndProc(ref mess);
							IntPtr edithandle = mess.Result;
							_customedit = new CustomEdit(edithandle, this, beforeed.Editor);
							_editeditem = new EditItemInformations(
								FocusedItem, beforeed.ColumnIndex, FocusedItem.SubItems[beforeed.ColumnIndex].Text);
							m.Result = IntPtr.Zero;
							return;
						#endregion
						#region APIsEnums.ListViewNotifications.ENDLABELEDIT
						case APIsEnums.ListViewNotifications.ENDLABELEDIT:
							if(_customedit != null)
								_customedit.HideEditControl();
							_customedit = null;
							_inedit = false;
							_editeditem = new EditItemInformations();
							m.Result = IntPtr.Zero;
							return;
						#endregion
						#region CUSTOMDRAW
						case (APIsEnums.ListViewNotifications) APIsEnums.NotificationMessages.CUSTOMDRAW:
							base.WndProc(ref m);
							CustomDraw(ref m);
							return;
						#endregion
						#region ENDSCROLL
						case APIsEnums.ListViewNotifications.ENDSCROLL:
							// Disable display bug with vertical lines
							if(ShowPlusMinus) Invalidate();
							break;
						#endregion
						#region APIsEnums.HeaderControlNotifications.BEGINDRAG
						case (APIsEnums.ListViewNotifications) APIsEnums.HeaderControlNotifications.BEGINDRAG:
							nmheader =(APIsStructs.NMHEADER) m.GetLParam(typeof(APIsStructs.NMHEADER));
							if(nmheader.iItem == 0)
							{
								m.Result = (IntPtr)1;
								return;
							}
							break;
						#endregion
						#region APIsEnums.HeaderControlNotifications.ENDDRAG
						case (APIsEnums.ListViewNotifications) APIsEnums.HeaderControlNotifications.ENDDRAG:
							nmheader =(APIsStructs.NMHEADER) m.GetLParam(typeof(APIsStructs.NMHEADER));
							// Get mouse position in header coordinates
							IntPtr headerHandle = (IntPtr) APIsUser32.SendMessage(Handle, (int) APIsEnums.ListViewMessages.GETHEADER, IntPtr.Zero, IntPtr.Zero);
							APIsStructs.POINTAPI pointapi = new APIsStructs.POINTAPI(MousePosition);
							APIsUser32.ScreenToClient(headerHandle, ref pointapi);
							// HeaderItem Rect
							APIsStructs.RECT headerItemRect = new APIsStructs.RECT();
							APIsUser32.SendMessage(headerHandle, (int)APIsEnums.HeaderControlMessages.GETITEMRECT, 0, ref headerItemRect);
							int headerItemWidth = headerItemRect.right - headerItemRect.left;
							// Cancel the drag operation if the first column is moved
							// or destination is the first column
							if(pointapi.x <= headerItemRect.left + headerItemWidth / 2 ||
								nmheader.iItem == 0)
							{
								m.Result = (IntPtr)1;
								return;
							}
							break;
						#endregion
						#region APIsEnums.HeaderControlNotifications.TRACK / ENDTRACK
						case (APIsEnums.ListViewNotifications)APIsEnums.HeaderControlNotifications.TRACK:
						case (APIsEnums.ListViewNotifications)APIsEnums.HeaderControlNotifications.ENDTRACK:
							Invalidate();
							break;
						#endregion
					}
					break;
				#endregion
				#region CANCELEDITLABEL
				case (APIsEnums.WindowMessages)(int) APIsEnums.ListViewMessages.CANCELEDITLABEL:
					if(m.LParam == IntPtr.Zero) break;
					string text = Marshal.PtrToStringAuto(m.LParam);
					ListViewItem lvi = (ListViewItem) EditedItem.Item;
					TreeListViewLabelEditEventArgs aftered = new TreeListViewLabelEditEventArgs(
						EditedItem.Item, EditedItem.ColumnIndex, text);
					EditItemInformations infos = EditedItem;
					OnAfterLabelEdit(aftered);
					if(aftered.Cancel) break;
					infos.Item.SubItems[infos.ColumnIndex].Text = aftered.Label;
					m.LParam = IntPtr.Zero;
					break;
				#endregion
				#region SETITEMSTATE
				// Disable the selection after a double click (normaly, if the control scrolls after
				// a collapse, the new item under the cursor is automatically selected...)
				case (APIsEnums.WindowMessages)(int) APIsEnums.ListViewMessages.SETITEMSTATE:
					lvitem = (APIsStructs.LV_ITEM) m.GetLParam(typeof(APIsStructs.LV_ITEM));
					if(_dblclicktime.AddMilliseconds(500).CompareTo(DateTime.Now) > 0 &&
						(lvitem.stateMask == APIsEnums.ListViewItemStates.SELECTED || lvitem.stateMask == APIsEnums.ListViewItemStates.FOCUSED) &&
						lvitem.mask == APIsEnums.ListViewItemFlags.STATE)
						return;
					break;
				#endregion
				#region LBUTTONDOWN
					// Cancel the click on checkboxes if the item is not "checkable"
				case APIsEnums.WindowMessages.LBUTTONDOWN:
					if(Columns.Count == 0) break;
					// Set the clickeditem and column
					int colclicked = GetColumnAt(MousePosition);
					if(colclicked == -1) colclicked = 0;
					item = GetItemAt(PointToClient(MousePosition));
					_lastitemclicked = new EditItemInformations(item, colclicked, "");
					// Get where the mouse has clicked
					APIsStructs.LVHITTESTINFO lvhittest = new APIsStructs.LVHITTESTINFO();
					lvhittest.pt = new APIsStructs.POINTAPI(PointToClient(MousePosition));
					APIsUser32.SendMessage(Handle, (Int32) APIsEnums.ListViewMessages.HITTEST, 0, ref lvhittest);
					if(lvhittest.flags == (int)APIsEnums.ListViewHitTestFlags.ONITEMSTATEICON && CheckBoxes &&
						!item.Checkable)
					{
						if(Columns[0].Width > (item.Level+(ShowPlusMinus?2:1))*SystemInformation.SmallIconSize.Width)
						{
							Focus();
							item.Selected = true;
							item.Focused = true;
							item.Checked = item.HasCheckedChild ? CheckState.Unchecked : CheckState.Checked;
							Invalidate();
							OnMouseDown(new MouseEventArgs(MouseButtons.Left, 1, PointToClient(MousePosition).X, PointToClient(MousePosition).Y, 0));
						}
						return;
					}
					// Plus / Minus click
					if(item == null) break;
					if(item.GetBounds(TreeListViewItemBoundsPortion.PlusMinus).Contains(PointToClient(MousePosition)) &&
						ShowPlusMinus && item.Items.Count > 0 &&
						Columns[0].Width > (item.Level+1)*SystemInformation.SmallIconSize.Width)
					{
						Focus();
						BeginUpdate();
						if(item.IsExpanded) item.Collapse();
						else item.Expand();
						EndUpdate();
						DrawPlusMinusItemLines(item);
						DrawPlusMinusItem(item);
						OnMouseDown(new MouseEventArgs(MouseButtons.Left, 1, PointToClient(MousePosition).X, PointToClient(MousePosition).Y, 0));
						return;
					}
					break;
				#endregion
				#region LBUTTONDBLCLK
					// Disable this notification to remove the auto-check when
					// the user double-click on an item and append the expand / collapse function
				case APIsEnums.WindowMessages.LBUTTONDBLCLK:
					_lastdoubleclick = DateTime.Now;
					if(this.FocusedItem != null)
					{
						item = this.FocusedItem;
						bool doExpColl = false;
						switch(ExpandMethod)
						{
							case TreeListViewExpandMethod.IconDbleClick:
								rec = item.GetBounds(ItemBoundsPortion.Icon);
								if(rec.Contains(PointToClient(MousePosition))) doExpColl = true;
								break;
							case TreeListViewExpandMethod.ItemOnlyDbleClick:
								rec = item.GetBounds(ItemBoundsPortion.ItemOnly);
								if(rec.Contains(PointToClient(MousePosition))) doExpColl = true;
								break;
							case TreeListViewExpandMethod.EntireItemDbleClick:
								rec = item.GetBounds(ItemBoundsPortion.Entire);
								if(rec.Contains(PointToClient(MousePosition))) doExpColl = true;
								break;
							default:
								break;
						}
						if(doExpColl)
						{
							_dblclicktime = DateTime.Now;
							Cursor = Cursors.WaitCursor;
							BeginUpdate();
							if(item.IsExpanded) item.Collapse();
							else item.Expand();
							EndUpdate();
							Cursor = Cursors.Default;
						}
					}
					OnDoubleClick(new EventArgs());
					return;
				#endregion
				#region UNICHAR, CHAR, KEYDOWN
				case APIsEnums.WindowMessages.UNICHAR:
				case APIsEnums.WindowMessages.CHAR:
					CharPressed((char) m.WParam);
					return;
				case APIsEnums.WindowMessages.KEYDOWN:
					OnKeyDown(new KeyEventArgs((Keys)(int) m.WParam));
					return;
				#endregion
				#region PAINT
				case APIsEnums.WindowMessages.PAINT:
					if(InEdit)
					{
						APIsStructs.RECT rect = new APIsStructs.RECT(
							EditedItem.Item.GetBounds(ItemBoundsPortion.Entire));
						APIsUser32.ValidateRect(Handle, ref rect);
					}
					base.WndProc(ref m);
					DrawIntermediateStateItems();
					DrawSelectedItemsFocusCues();
					return;
				#endregion
			}
			base.WndProc(ref m);
		}
		#region OnKeyDown
		/// <summary>
		/// Raises the KeyDown event
		/// </summary>
		/// <param name="e"></param>
		protected override void OnKeyDown(KeyEventArgs e)
		{
			Keys key = e.KeyCode;
			if(FocusedItem == null)
			{
				if(base.Items.Count > 0 &&
					(key == Keys.Down || key == Keys.Up || key == Keys.Left || key == Keys.Right))
				{
					base.Items[0].Selected = true;
					base.Items[0].Focused = true;
					base.Items[0].EnsureVisible();
				}
				return;
			}
			TreeListViewItem item = FocusedItem;
			switch(key)
			{
				case Keys.Down:
					if(item.Index < base.Items.Count - 1)
					{
						ListViewItem listviewitem = base.Items[item.Index + 1];
						listviewitem.Selected = true;
						listviewitem.Focused = true;
						listviewitem.EnsureVisible();
					}
					break;
				case Keys.Up:
					if(item.Index > 0)
					{
						ListViewItem listviewitem = base.Items[item.Index - 1];
						listviewitem.Selected = true;
						listviewitem.Focused = true;
						listviewitem.EnsureVisible();
					}
					break;
				case Keys.Enter:
					BeginUpdate();
					if(item.IsExpanded) item.Collapse();
					else item.Expand();
					item.Selected = true;
					item.Focused = true;
					item.EnsureVisible();
					EndUpdate();
					break;
				case Keys.Left:
					BeginUpdate();
					if(item.IsExpanded)
					{
						item.Selected = true;
						item.Focused = true;
						item.Collapse();
						item.EnsureVisible();
					}
					else if(item.Parent != null)
					{
						item.Parent.Selected = true;
						item.Parent.Focused = true;
						item.Parent.EnsureVisible();
					}
					EndUpdate();
					break;
				case Keys.Right:
					if(item.Items.Count == 0) break;
					BeginUpdate();
					if(!item.IsExpanded)
					{
						item.Selected = true;
						item.Focused = true;
						item.Expand();
						item.EnsureVisible();
					}
					else
					{
						item.Items[item.Items.Count-1].Selected = true;
						item.Items[item.Items.Count-1].Focused = true;
						item.Items[item.Items.Count-1].EnsureVisible();
					}
					EndUpdate();
					break;
			}
			base.OnKeyDown(e);
		}
		#endregion
		#region CharPressed
		private void CharPressed(char character)
		{
			Debug.Assert(!InvokeRequired);
			string begin = character.ToString().ToUpper();
			if(FocusedItem == null) return;
			TreeListViewItem item = FocusedItem;
			// Select an item begining with the specified character
			if((begin.CompareTo("A") >= 0 && begin.CompareTo("Z") <= 0) || begin == " ")
			{
				// Get the collection in wich the item is
				TreeListViewItemCollection collection = item.Parent == null ? this.Items : item.Parent.Items;
				bool founded = false;
				// Search in the next items
				for(int i = collection.GetIndexOf(item) + 1 ; i < collection.Count ; i++)
					if(collection[i].Text.ToUpper().StartsWith(begin))
					{
						collection[i].Selected = true;
						collection[i].Focused = true;
						collection[i].EnsureVisible();
						founded = true;
						break;
					}
				// Search in the previous items
				if(!founded)
					for(int i = 0 ; i < collection.GetIndexOf(item) ; i++)
						if(collection[i].Text.ToUpper().StartsWith(begin))
						{
							collection[i].Selected = true;
							collection[i].Focused = true;
							collection[i].EnsureVisible();
							founded = true;
							break;
						}
			}
		}
		#endregion
		#endregion
		#region Draw
		#region CustomDraw
		private void CustomDraw(ref Message m)
		{
			int iRow, iCol; bool bSelected;
			unsafe
			{
				APIsStructs.NMLVCUSTOMDRAW * nmlvcd = (APIsStructs.NMLVCUSTOMDRAW *)m.LParam.ToPointer();
				switch((APIsEnums.CustomDrawDrawStateFlags)nmlvcd->nmcd.dwDrawStage)
				{
					case APIsEnums.CustomDrawDrawStateFlags.PREPAINT:
						m.Result = (IntPtr)APIsEnums.CustomDrawReturnFlags.NOTIFYITEMDRAW;
						break;
					case APIsEnums.CustomDrawDrawStateFlags.ITEMPREPAINT:
						m.Result = (IntPtr)APIsEnums.CustomDrawReturnFlags.NOTIFYSUBITEMDRAW;
						break;
					case APIsEnums.CustomDrawDrawStateFlags.ITEMPREPAINT |
						APIsEnums.CustomDrawDrawStateFlags.SUBITEM:
						iRow = (int)nmlvcd->nmcd.dwItemSpec;
						iCol = (int)nmlvcd->iSubItem;
						bSelected = base.Items[iRow].Selected;// && this.Focused;
						if(bSelected && _useXPHighLightStyle)
						{
							Color color = Focused ? ColorUtil.VSNetSelectionColor : ColorUtil.VSNetSelectionUnfocusedColor;
							if(HideSelection && !Focused) color = BackColor;
							if(FullRowSelect || iCol == 0)
								nmlvcd->clrTextBk = (int)ColorUtil.RGB(color.R, color.G, color.B);
							nmlvcd->nmcd.uItemState &= ~(uint)APIsEnums.CustomDrawItemStateFlags.SELECTED;
							if(iCol == 0) DrawSelectedItemFocusCues(iRow);
						}
						if(iCol == 0)
						{
							DrawIntermediateStateItem((TreeListViewItem)base.Items[iRow]);
							DrawPlusMinusItemLines((TreeListViewItem)base.Items[iRow]);
							DrawPlusMinusItem((TreeListViewItem)base.Items[iRow]);
						}
						m.Result = (IntPtr)APIsEnums.CustomDrawReturnFlags.NEWFONT;
						break;
				}
			}
		}
		#endregion
		#region DrawIntermediateStateItem
		internal void DrawIntermediateStateItems()
		{
			Debug.Assert(!InvokeRequired);
			if(!CheckBoxes) return;
			TreeListViewItemCollection items = GetVisibleItems();
			Graphics g = Graphics.FromHwnd(Handle);
			foreach(TreeListViewItem item in items)
				DrawIntermediateStateItem(g, item);
			g.Dispose();
		}
		internal void DrawIntermediateStateItem(TreeListViewItem item)
		{
			Graphics g = Graphics.FromHwnd(Handle);
			DrawIntermediateStateItem(g, item);
			g.Dispose();
		}
		internal void DrawIntermediateStateItem(Graphics g, TreeListViewItem item)
		{
			Debug.Assert(!InvokeRequired);
			if(!CheckBoxes || Columns.Count == 0) return;
			if(item.HasCheckedChild && !item.Checkable)
			{
				Rectangle rect = item.GetBounds(ItemBoundsPortion.Icon);
				Rectangle r = _comctl32Version >= 6 ?
					new Rectangle(rect.Left - 14, rect.Top + 5, rect.Height-10, rect.Height-10) :
					new Rectangle(rect.Left - 11, rect.Top + 5, rect.Height-10, rect.Height-10);
				Brush brush = new Drawing.Drawing2D.LinearGradientBrush(r, Color.White, Color.Gray, 45, false);
				if(Columns[0].Width > (item.Level + (ShowPlusMinus?2:1)) * SystemInformation.SmallIconSize.Width)
					g.FillRectangle(brush, r);
				brush.Dispose();
			}
		}
		#endregion
		#region DrawSelectedItemsFocusCues
		internal void DrawSelectedItemsFocusCues()
		{
			if((HideSelection && !Focused) || !_useXPHighLightStyle) return;
			Debug.Assert(!InvokeRequired);
			SelectedTreeListViewItemCollection items = SelectedItems;
			if(items.Count == 0 && FocusedItem != null && Focused)
				DrawSelectedItemFocusCues(FocusedItem.Index);
			foreach(TreeListViewItem temp in items)
				DrawSelectedItemFocusCues(temp.Index);
		}
		internal void DrawSelectedItemFocusCues(int index)
		{
			if(HideSelection && !Focused) return;
			Graphics g = Graphics.FromHwnd(Handle);
			TreeListViewItem item = (TreeListViewItem)base.Items[index];
			if(item.Visible)
			{
				Rectangle entireitemrect = GetItemRect(item.Index, ItemBoundsPortion.Entire);
				Rectangle labelitemrect = GetItemRect(item.Index, ItemBoundsPortion.Label);
				Rectangle itemonlyrect = GetItemRect(item.Index, ItemBoundsPortion.ItemOnly);
				Rectangle selecteditemrect = new Rectangle(
					labelitemrect.Left,
					labelitemrect.Top,
					FullRowSelect ? entireitemrect.Width - labelitemrect.Left - 1 : itemonlyrect.Width - SystemInformation.SmallIconSize.Width - 1,
					labelitemrect.Height - 1);
				Pen pen = new Pen(Focused && item.Selected ? Color.Blue : ColorUtil.CalculateColor(SystemColors.Highlight, SystemColors.Window, 130));
				for(int i = 1; i < Columns.Count; i++)
				{
					Rectangle rect = GetSubItemRect(item.Index, i);
					if(rect.X < selecteditemrect.X)
						selecteditemrect = new Rectangle(
							rect.X, selecteditemrect.Y,
							selecteditemrect.Width + (selecteditemrect.X-rect.X),
							selecteditemrect.Height);
				}
				g.DrawRectangle(pen , selecteditemrect);
				pen.Dispose();
			}
			g.Dispose();
		}
		#endregion
		#region DrawPlusMinus
		internal void DrawPlusMinusItem(TreeListViewItem item)
		{
			Graphics g = Graphics.FromHwnd(Handle);
			DrawPlusMinusItem(g, item);
			g.Dispose();
		}
		internal void DrawPlusMinusItem(Graphics g, TreeListViewItem item)
		{
			Debug.Assert(!InvokeRequired);
			if(item.Items.Count == 0 ||Columns.Count == 0) return;
			Drawing.Imaging.ImageAttributes attr = new Drawing.Imaging.ImageAttributes();
			attr.SetColorKey(Color.Transparent, Color.Transparent);
			if(Columns[0].Width > (item.Level + 1) * SystemInformation.SmallIconSize.Width)
				g.DrawImage(
					plusMinusImageList.Images[item.IsExpanded ? 1 : 0],
					item.GetBounds(TreeListViewItemBoundsPortion.PlusMinus),
					0, 0, SystemInformation.SmallIconSize.Width, SystemInformation.SmallIconSize.Height,
					GraphicsUnit.Pixel, attr);
			attr.Dispose();
		}
		#endregion
		#region DrawPlusMinusLines
		internal void DrawPlusMinusItemLines(TreeListViewItem item)
		{
			Graphics g = Graphics.FromHwnd(Handle);
			DrawPlusMinusItemLines(g, item);
			g.Dispose();
		}
		internal void DrawPlusMinusItemLines(Graphics g, TreeListViewItem item)
		{
			Debug.Assert(!InvokeRequired);
			if(!ShowPlusMinus || Columns.Count == 0) return;
			int itemLevel = item.Level;
			Rectangle plusminusRect = item.GetBounds(TreeListViewItemBoundsPortion.PlusMinus);
			Rectangle entireRect = item.GetBounds(TreeListViewItemBoundsPortion.Entire);
			Drawing.Drawing2D.HatchBrush hb = new Drawing.Drawing2D.HatchBrush(Drawing.Drawing2D.HatchStyle.Percent50, _plusMinusLineColor, BackColor);
			Pen pen = new Pen(hb);
			Point point1, point2;
			#region Vertical line
			point1 = new Point(
				plusminusRect.Right - SystemInformation.SmallIconSize.Width / 2 - 1,
				entireRect.Top);
			point2 = new Point( point1.X, entireRect.Bottom);
			// If ListView has no items that have the same level before this item
			if(!HasLevelBeforeItem(item, itemLevel)) point1.Y += SystemInformation.SmallIconSize.Height / 2;
			// If ListView has no items that have the same level after this item
			if(!HasLevelAfterItem(item, itemLevel)) point2.Y -= SystemInformation.SmallIconSize.Height / 2 + 1;
			if(Columns[0].Width > (item.Level + 1) * SystemInformation.SmallIconSize.Width)
				g.DrawLine(pen, point1, point2);
			#endregion
			#region Horizontal line
			point1 = new Point(
				plusminusRect.Right - SystemInformation.SmallIconSize.Width / 2 - 1,
				item.GetBounds(TreeListViewItemBoundsPortion.Entire).Top + SystemInformation.SmallIconSize.Height /2);
			point2 = new Point(plusminusRect.Right + 1, point1.Y);
			if(Columns[0].Width > (item.Level + 1) * SystemInformation.SmallIconSize.Width)
				g.DrawLine(pen, point1, point2);
			#endregion
			#region Lower Level lines
			for(int level = item.Level - 1; level > -1; level--)
				if(HasLevelAfterItem(item, level))
				{
					point1 = new Point(
						SystemInformation.SmallIconSize.Width * (2*level + 1) / 2 + entireRect.X,
						entireRect.Top);
					point2 = new Point(
						point1.X, entireRect.Bottom);
					if(Columns[0].Width > (level + 1) * SystemInformation.SmallIconSize.Width)
						g.DrawLine(pen, point1, point2);
				}
			#endregion
			pen.Dispose();
			hb.Dispose();
		}
		internal bool HasLevelAfterItem(TreeListViewItem item, int level)
		{
			Debug.Assert(!InvokeRequired);
			int lev = item.Level, tempLevel;
			for(int i = item.Index + 1; i < base.Items.Count; i++)
			{
				tempLevel = ((TreeListViewItem)base.Items[i]).Level;
				if(tempLevel == level) return true;
				if(tempLevel < level) return false;
			}
			return false;
		}
		internal bool HasLevelBeforeItem(TreeListViewItem item, int level)
		{
			Debug.Assert(!InvokeRequired);
			int lev = item.Level, tempLevel;
			for(int i = item.Index - 1; i > -1; i--)
			{
				tempLevel = ((TreeListViewItem)base.Items[i]).Level;
				if(tempLevel <= level) return true;
			}
			return false;
		}
		#endregion
		#endregion

		#region Functions
		/// <summary>
		/// Retrieves the specified portion of the bounding rectangle for a specific item within the list view control
		/// </summary>
		/// <param name="index">The zero-based index of the item within the ListView.ListViewItemCollection whose bounding rectangle you want to return</param>
		/// <param name="portion">One of the TreeListViewItemBoundsPortion values that represents a portion of the TreeListViewItem for which to retrieve the bounding rectangle</param>
		/// <returns>A Rectangle that represents the bounding rectangle for the specified portion of the specified TreeListViewItem</returns>
		public Rectangle GetItemRect(int index, TreeListViewItemBoundsPortion portion)
		{
			if(index >= base.Items.Count || index < 0)
				throw(new Exception("Out of range exception"));
			TreeListViewItem item = (TreeListViewItem) base.Items[index];
			return item.GetBounds(portion);
		}
		/// <summary>
		/// Kill the focus of the control
		/// </summary>
		public void KillFocus()
		{
			Message m = Message.Create(
				Handle,
				(int) APIsEnums.WindowMessages.KILLFOCUS,
				IntPtr.Zero,
				IntPtr.Zero);
			WndProc(ref m);
		}
		private void TreeListView_ItemCheck(object sender, System.Windows.Forms.ItemCheckEventArgs e)
		{
			Invalidate();
		}
		/// <summary>
		/// Gets an item at the specified coordinates.
		/// </summary>
		/// <param name="p">Mouse position</param>
		/// <returns></returns>
		public TreeListViewItem GetItemAt(Point p)
		{
			return((TreeListViewItem) base.GetItemAt(p.X, p.Y));
		}
		/// <summary>
		/// Gets an item at the specified coordinates.
		/// </summary>
		/// <param name="x"></param>
		/// <param name="y"></param>
		/// <returns></returns>
		new public TreeListViewItem GetItemAt(int x, int y)
		{
			return((TreeListViewItem) base.GetItemAt(x, y));
		}
		/// <summary>
		/// Gets the TreeListViewItem from the ListView index of the item
		/// </summary>
		/// <param name="index">Index of the Item</param>
		/// <returns></returns>
		public TreeListViewItem GetTreeListViewItemFromIndex(int index)
		{
			if(base.Items.Count <= index + 1) return(null);
			return((TreeListViewItem) base.Items[index]);
		}
		// Redraw indentation of the visible items
		private void TreeListView_VisibleChanged(object sender, System.EventArgs e)
		{
			Invoke(new VoidHandler(VisChanged));
		}
		private void VisChanged()
		{
			if(!Visible) return;
			BeginUpdate();
			try
			{
				foreach(TreeListViewItem item in this.Items)
					item.RefreshIndentation(true);
			}
			catch{}
			if(SelectedIndices.Count > 0) SelectedItems[0].EnsureVisible();
			EndUpdate();
		}
		/// <summary>
		/// Not supported (use items.Sort)
		/// </summary>
		new public void Sort(){throw(new Exception("Not Supported. Use items.Sort"));}
		/// <summary>
		/// Nettoyage des ressources utilis�es.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if( components != null )
					components.Dispose();
			}
			base.Dispose( disposing );
		}
		#endregion
		#region Component Designer generated code
		private void InitializeComponent()
		{
			this.components = new System.ComponentModel.Container();
			System.Resources.ResourceManager resources = new System.Resources.ResourceManager(typeof(TreeListView));
			this.imageList1 = new System.Windows.Forms.ImageList(this.components);
			this.plusMinusImageList = new System.Windows.Forms.ImageList(this.components);
			// 
			// imageList1
			// 
			this.imageList1.ColorDepth = System.Windows.Forms.ColorDepth.Depth8Bit;
			this.imageList1.ImageSize = new System.Drawing.Size(16, 16);
			this.imageList1.TransparentColor = System.Drawing.Color.Transparent;
			// 
			// plusMinusImageList
			// 
			this.plusMinusImageList.ColorDepth = System.Windows.Forms.ColorDepth.Depth8Bit;
			this.plusMinusImageList.ImageSize = new System.Drawing.Size(16, 16);
			this.plusMinusImageList.ImageStream = ((System.Windows.Forms.ImageListStreamer)(resources.GetObject("plusMinusImageList.ImageStream")));
			this.plusMinusImageList.TransparentColor = System.Drawing.Color.Transparent;
			// 
			// TreeListView
			// 
			this.FullRowSelect = true;
			this.MultiSelect = false;
			this.SmallImageList = this.imageList1;
			this.View = System.Windows.Forms.View.Details;
			this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.TreeListView_MouseDown);
			this.VisibleChanged += new System.EventHandler(this.TreeListView_VisibleChanged);
			this.ColumnClick += new System.Windows.Forms.ColumnClickEventHandler(this.TreeListView_ColumnClick);
			this.ItemCheck += new System.Windows.Forms.ItemCheckEventHandler(this.TreeListView_ItemCheck);

		}
		#endregion

		#region Column Order
		/// <summary>
		/// Get the index of the specified column from its physical position
		/// </summary>
		/// <param name="columnorder"></param>
		/// <returns></returns>
		public int GetColumnIndex(int columnorder)
		{
			if(columnorder < 0 || columnorder > Columns.Count - 1) return(-1);
			return APIsUser32.SendMessage(Handle, (int)APIsEnums.HeaderControlMessages.ORDERTOINDEX, columnorder, 0);
		}
		/// <summary>
		/// Gets the order of a specified column
		/// </summary>
		/// <param name="columnindex"></param>
		/// <returns></returns>
		public int GetColumnOrder(int columnindex)
		{
			if(this.Columns.Count == 0) return(-1);
			if(columnindex < 0 || columnindex > this.Columns.Count - 1) return(-1);
			IntPtr[] colorderarray = new IntPtr[this.Columns.Count];
			APIsUser32.SendMessage(this.Handle, (int) APIsEnums.ListViewMessages.GETCOLUMNORDERARRAY, (IntPtr) this.Columns.Count, ref colorderarray[0]);
			return((int) colorderarray[columnindex]);
		}
		/// <summary>
		/// Gets the columns order
		/// </summary>
		/// <returns>Example {3,1,4,2}</returns>
		public int[] GetColumnsOrder()
		{
			if(this.Columns.Count == 0) return(new int[] {});
			IntPtr[] colorderarray = new IntPtr[this.Columns.Count];
			try
			{
				APIsUser32.SendMessage(this.Handle, (int) APIsEnums.ListViewMessages.GETCOLUMNORDERARRAY, (IntPtr) this.Columns.Count, ref colorderarray[0]);
			}
			catch{}
			int[] colorderarrayint = new int[this.Columns.Count];
			for(int i = 0 ; i < this.Columns.Count ; i ++)
				colorderarrayint[i] = (int) colorderarray[i];
			return(colorderarrayint);
		}
		/// <summary>
		/// Indicates the column order (for example : {0,1,3,2})
		/// </summary>
		/// <param name="colorderarray"></param>
		public void SetColumnsOrder(int[] colorderarray)
		{
			if(this.Columns.Count == 0) return;
			if(colorderarray.Length != this.Columns.Count) return;
			if(colorderarray[0] != 0) return;
			IntPtr[] colorderarrayintptr = new IntPtr[this.Columns.Count];
			for(int i = 0 ; i < this.Columns.Count ; i ++)
				colorderarrayintptr[i] = (IntPtr) colorderarray[i];
			try
			{
				APIsUser32.SendMessage(this.Handle, (int) APIsEnums.ListViewMessages.SETCOLUMNORDERARRAY, (IntPtr) this.Columns.Count, ref colorderarrayintptr[0]);
			}
			catch{}
			this.Refresh();
		}

		private void TreeListView_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e)
		{
			if(e.Button == MouseButtons.Middle)
			{
				_mousescrollposition = MousePosition;
				Threading.Thread th = new Threading.Thread(new Threading.ThreadStart(_scroll));
				th.Start();
			}
		}

		private void _scroll()
		{
			while(MouseButtons == MouseButtons.Middle)
			{
				int dx = MousePosition.Y - _mousescrollposition.Y;
				int dy = MousePosition.Y - _mousescrollposition.Y;
				Scroll(
					dx,
					dy);
				Threading.Thread.Sleep(100);
			}
			Cursor = Cursors.Default;
		}
		
		/// <summary>
		/// Scrolls the control
		/// </summary>
		/// <param name="x"></param>
		/// <param name="y"></param>
		public void Scroll(int x, int y)
		{
			APIsUser32.SendMessage(Handle, (int) APIsEnums.ListViewMessages.SCROLL, x, y);
		}

		private void TreeListView_ColumnClick(object sender, System.Windows.Forms.ColumnClickEventArgs e)
		{
			Cursor = Cursors.WaitCursor;
			FreezeCheckBoxes = true;
			BeginUpdate();
			if(Comparer.Column == e.Column)
				Sorting = Sorting == SortOrder.Ascending ? SortOrder.Descending : SortOrder.Ascending;
			else
			{
				Comparer.Column = e.Column;
				Items.SortOrderRecursivelyWithoutSort = SortOrder.Ascending;
				try{Items.Sort(true);}
				catch{}
			}
			if(FocusedItem != null) FocusedItem.EnsureVisible();
			EndUpdate();
			FreezeCheckBoxes = false;
			Cursor = Cursors.Default;
		}
		/// <summary>
		/// Indicates the column order (for example : "3142")
		/// </summary>
		/// <param name="colorder"></param>
		public void SetColumnsOrder(string colorder)
		{
			if(colorder == null) return;
			int[] colorderarray = new int[colorder.Length];
			for(int i = 0 ; i < colorder.Length ; i++)
				colorderarray[i] = int.Parse(new String(colorder[i], 1));
			SetColumnsOrder(colorderarray);
		}
		#endregion

		#region Item Region
		/// <summary>
		/// Gets the items that are visible in the TreeListView
		/// </summary>
		/// <returns>A collection of items</returns>
		public TreeListViewItemCollection GetVisibleItems()
		{
			if(InvokeRequired) throw(new Exception("Invoke required"));
			TreeListViewItemCollection visibleItems = new TreeListViewItemCollection();
			if(base.Items.Count == 0) return visibleItems;
			int firstItemIndex = TopItem.Index;
			int itemsPerPageCount =
				APIsUser32.SendMessage(Handle, (int) APIsEnums.ListViewMessages.GETITEMCOUNT, IntPtr.Zero, IntPtr.Zero);
			int lastVisibleItemIndex = firstItemIndex + itemsPerPageCount > base.Items.Count ?
				base.Items.Count : firstItemIndex + itemsPerPageCount;
			for(int i = firstItemIndex; i < lastVisibleItemIndex; i++)
				visibleItems.Add((TreeListViewItem) base.Items[i]);
			return visibleItems;
		}
		/// <summary>
		/// Gets the column at the specified position
		/// </summary>
		/// <param name="p">Point in client coordinates</param>
		/// <returns>The nul zero based index of the column (-1 if failed)</returns>
		public int GetColumnAt(Point p)
		{
			APIsStructs.LVHITTESTINFO hittest = new APIsStructs.LVHITTESTINFO();
			hittest.pt = new APIsStructs.POINTAPI(PointToClient(MousePosition));
			APIsUser32.SendMessage(
				Handle,
				(Int32) APIsEnums.ListViewMessages.SUBITEMHITTEST,
				0,
				ref hittest);
			return(hittest.iSubItem);
		}
		/// <summary>
		/// Get SubItem rectangle
		/// </summary>
		/// <param name="item"></param>
		/// <param name="column"></param>
		/// <returns></returns>
		public Rectangle GetSubItemRect(TreeListViewItem item, int column)
		{
			ListViewItem lvitem = (ListViewItem) item;
			return GetSubItemRect(lvitem.Index, column);
		}
		/// <summary>
		/// Get SubItem rectangle
		/// </summary>
		/// <param name="row"></param>
		/// <param name="col"></param>
		/// <returns></returns>
		public Rectangle GetSubItemRect(int row, int col)
		{
			APIsStructs.RECT rc = new APIsStructs.RECT();
			rc.top = col;
			rc.left = (int)APIsEnums.ListViewSubItemPortion.BOUNDS;
			APIsUser32.SendMessage(Handle, (int)APIsEnums.ListViewMessages.GETSUBITEMRECT,  row, ref rc);
			
			if ( col == 0 )
			{
				// The LVM_GETSUBITEMRECT message does not give us the rectangle for the first subitem
				// since it is not considered a subitem
				// obtain the rectangle for the header control and calculate from there
				Rectangle headerRect = GetHeaderItemRect(col);
				return new Rectangle((int)rc.left, (int)rc.top, (int)headerRect.Width, (int)(rc.bottom-rc.top));
			}
			
			return new Rectangle((int)rc.left, (int)rc.top, (int)(rc.right-rc.left), (int)(rc.bottom-rc.top));
		}
		/// <summary>
		/// Get HeaderItem text
		/// </summary>
		/// <param name="index"></param>
		/// <returns></returns>
		public string GetHeaderItemText(int index)
		{
			APIsStructs.HDITEM hdi = new APIsStructs.HDITEM();
			hdi.mask = APIsEnums.HeaderItemFlags.TEXT;
			hdi.cchTextMax =  255;
			hdi.pszText = Marshal.AllocHGlobal(255);
			APIsUser32.SendMessage(Handle, APIsEnums.HeaderControlMessages.GETITEMW, index, ref hdi);
			string text = Marshal.PtrToStringAuto(hdi.pszText);
			return text;
		}
		/// <summary>
		/// Get HeaderItem rect
		/// </summary>
		/// <param name="index"></param>
		/// <returns></returns>
		protected Rectangle GetHeaderItemRect(int index)
		{
			APIsStructs.RECT rc = new APIsStructs.RECT();
			IntPtr header = APIsUser32.GetDlgItem(Handle, 0);
			APIsUser32.SendMessage(header, (int)APIsEnums.HeaderControlMessages.GETITEMRECT, index, ref rc);
			return new Rectangle((int)rc.left, (int)rc.top, (int)(rc.right-rc.left), (int)(rc.bottom-rc.top));
		}
		/// <summary>
		/// Get row rect
		/// </summary>
		/// <param name="row"></param>
		/// <returns></returns>
		public Rectangle GetRowRect(int row)
		{
			APIsStructs.RECT rc = new APIsStructs.RECT();
			rc.top = 0;
			rc.left = (int)APIsEnums.ListViewSubItemPortion.BOUNDS;
			APIsUser32.SendMessage(Handle, (int)APIsEnums.ListViewMessages.GETSUBITEMRECT,  row, ref rc);
			return new Rectangle((int)rc.left, (int)rc.top, (int)(rc.right-rc.left), (int)(rc.bottom-rc.top));
		}
		#endregion
	}
	#region EditItemInformations
	/// <summary>
	/// Class that contains all informations on an edited item
	/// </summary>
	public struct EditItemInformations
	{
		#region Properties
		internal DateTime CreationTime;
		private string _label;
		/// <summary>
		/// Gets the label of the subitem
		/// </summary>
		public string Label
		{
			get{return _label;}
		}
		private TreeListViewItem _item;
		/// <summary>
		/// Gets the item being edited
		/// </summary>
		public TreeListViewItem Item
		{
			get{return _item;}
		}
		private int _colindex;
		/// <summary>
		/// Gets the number of the subitem
		/// </summary>
		public int ColumnIndex
		{
			get{return _colindex;}
		}
		#endregion
		#region Constructor
		/// <summary>
		/// Creates a new instance of EditItemInformations
		/// </summary>
		/// <param name="item"></param>
		/// <param name="column"></param>
		/// <param name="label"></param>
		public EditItemInformations(TreeListViewItem item, int column, string label)
		{
			_item = item; _colindex = column; _label = label; CreationTime = DateTime.Now;
		}
		#endregion
	}
	#endregion

	#region Event Handlers
	/// <summary>
	/// TreeListViewBeforeLabelEditEventHandler delegate
	/// </summary>
	public delegate void TreeListViewBeforeLabelEditEventHandler(object sender, TreeListViewBeforeLabelEditEventArgs e);
	/// <summary>
	/// TreeListViewItemLabelEditHandler delegate
	/// </summary>
	public delegate void TreeListViewLabelEditEventHandler(object sender, TreeListViewLabelEditEventArgs e);
	/// <summary>
	/// TreeListViewCancelEventHandler delegate
	/// </summary>
	public delegate void TreeListViewCancelEventHandler(object sender, TreeListViewCancelEventArgs e);
	/// <summary>
	/// TreeListViewEventHandler delegate
	/// </summary>
	public delegate void TreeListViewEventHandler(object sender, TreeListViewEventArgs e);
	#endregion
	#region TreeListViewLabelEditEventArgs & TreeListViewBeforeLabelEditEventArgs
	/// <summary>
	/// Arguments of a TreeListViewLabelEdit event.
	/// </summary>
	[Serializable]
	public class TreeListViewLabelEditEventArgs : CancelEventArgs
	{
		#region Properties
		private string _label;
		/// <summary>
		/// Gets the label of the subitem
		/// </summary>
		public string Label
		{
			get{return _label;}
		}
		private TreeListViewItem _item;
		/// <summary>
		/// Gets the item being edited
		/// </summary>
		public TreeListViewItem Item
		{
			get{return _item;}
		}
		internal int _colindex;
		/// <summary>
		/// Gets the number of the subitem
		/// </summary>
		public int ColumnIndex
		{
			get{return _colindex;}
		}
		#endregion
		#region Constructor
		/// <summary>
		/// Creates a new instance of TreeListViewLabelEditEventArgs
		/// </summary>
		/// <param name="item"></param>
		/// <param name="column"></param>
		/// <param name="label"></param>
		public TreeListViewLabelEditEventArgs(TreeListViewItem item, int column, string label) : base()
		{
			_item = item; _colindex = column; _label = label;
		}
		#endregion
	}
	/// <summary>
	/// Arguments of a TreeListViewBeforeLabelEdit event.
	/// </summary>
	[Serializable]
	public class TreeListViewBeforeLabelEditEventArgs : TreeListViewLabelEditEventArgs
	{
		#region Properties
		/// <summary>
		/// Gets or sets the index of the subitem
		/// </summary>
		new public int ColumnIndex
		{
			get{return _colindex;}
			set{_colindex = value;}
		}
		private Control _editor;
		/// <summary>
		/// Gets or sets the editor (a TextBox will be displayed if null)
		/// </summary>
		public Control Editor
		{
			get{return _editor;}
			set{_editor = value;}
		}
		#endregion
		#region Constructor
		/// <summary>
		/// Creates a new instance of TreeListViewBeforeLabelEditEventArgs
		/// </summary>
		/// <param name="item"></param>
		/// <param name="column"></param>
		/// <param name="label"></param>
		public TreeListViewBeforeLabelEditEventArgs(TreeListViewItem item, int column, string label) : base(item, column, label)
		{}
		#endregion
	}
	#endregion
	#region TreeListViewEventArgs
	/// <summary>
	/// Arguments of a TreeListViewEvent
	/// </summary>
	[Serializable]
	public class TreeListViewEventArgs : EventArgs
	{
		private TreeListViewItem _item;
		/// <summary>
		/// Item that will be expanded
		/// </summary>
		public TreeListViewItem Item{get{return(_item);}}
		private TreeListViewAction _action;
		/// <summary>
		/// Action returned by the event
		/// </summary>
		public TreeListViewAction Action{get{return(_action);}}
		/// <summary>
		/// Create a new instance of TreeListViewEvent arguments
		/// </summary>
		/// <param name="item"></param>
		/// <param name="action"></param>
		public TreeListViewEventArgs(TreeListViewItem item, TreeListViewAction action)
		{
			_item = item;
			_action = action;
		}
	}
	/// <summary>
	/// Arguments of a TreeListViewCancelEventArgs
	/// </summary>
	[Serializable]
	public class TreeListViewCancelEventArgs : TreeListViewEventArgs
	{
		private bool _cancel = false;
		/// <summary>
		/// True -> the operation is canceled
		/// </summary>
		public bool Cancel
		{
			get{return(_cancel);}
			set{_cancel = value;}
		}
		/// <summary>
		/// Create a new instance of TreeListViewCancelEvent arguments
		/// </summary>
		/// <param name="item"></param>
		/// <param name="action"></param>
		public TreeListViewCancelEventArgs(TreeListViewItem item, TreeListViewAction action) :
			base(item, action)
		{}
	}
	#endregion

	#region TreeListViewAction
	/// <summary>
	/// TreeListView actions
	/// </summary>
	[Serializable]
	public enum TreeListViewAction
	{
		/// <summary>
		/// By Keyboard
		/// </summary>
		ByKeyboard,
		/// <summary>
		/// ByMouse
		/// </summary>
		ByMouse,
		/// <summary>
		/// Collapse
		/// </summary>
		Collapse,
		/// <summary>
		/// Expand
		/// </summary>
		Expand,
		/// <summary>
		/// Unknown
		/// </summary>
		Unknown
	}
	#endregion
	#region TreeListViewExpandMethod
	/// <summary>
	/// Expand / Collapse method
	/// </summary>
	[Serializable]
	public enum TreeListViewExpandMethod
	{
		/// <summary>
		/// Expand when double clicking on the icon
		/// </summary>
		IconDbleClick,
		/// <summary>
		/// Expand when double clicking on the entire item
		/// </summary>
		EntireItemDbleClick,
		/// <summary>
		/// Expand when double clicking on the item only
		/// </summary>
		ItemOnlyDbleClick,
		/// <summary>
		/// None
		/// </summary>
		None
	}
	#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, along with any associated source code and files, is licensed under The Microsoft Public License (Ms-PL)


Written By
Web Developer
France France
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions