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
}