|
|||||||||||||||||||||
|
|||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionThe I made such a control that enables this features:
TreeListView controlThis class inherits from the PropertiesSome properties have been changed: For example, the public new TreeListView.TreeListViewItemCollection Items{
get{return(_items);}}
new public SelectedTreeListViewItemCollection SelectedItems
{
get
{
SelectedTreeListViewItemCollection sel = new
SelectedTreeListViewItemCollection(this);
return(sel);
}
}
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);
}
}
// Gets the informations of the current edited item
public EditItemInformations EditedItem
{
get{return _editeditem;}
}
// Gets wether an item is currently edited
public bool InEdit
{
get{return _inedit;}
}
// 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));}
}
// 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();}
}
// Gets or Sets whether the control draw XP-Style highlight color
public bool UseXPHighlightStyle
{
get{return _useXPHighLightStyle;}
set{_useXPHighLightStyle = value;
if(Created) Invalidate();}
}
Expand and collapse eventsLike in the Here is a list of the new events (the handlers and arguments are not described here) : [Description("Occurs before the tree node is collapsed")]
public event TreeListViewCancelEventHandler BeforeExpand;
[Description("Occurs before the tree node is collapsed")]
public event TreeListViewCancelEventHandler BeforeCollapse;
[Description("Occurs after the tree node is expanded")]
public event TreeListViewEventHandler AfterExpand;
[Description("Occurs after the tree node is collapsed")]
public event TreeListViewEventHandler AfterCollapse;
[Description("Occurs when the label for an item is edited by the user.")]
public new event TreeListViewLabelEditEventHandler AfterLabelEdit;
[Description("Occurs when the user starts editing the label of an item.")]
public new event TreeListViewBeforeLabelEditEventHandler BeforeLabelEdit;
TreeListViewItem classThis class inherits from the private bool _isexpanded;
public bool IsExpanded{
get{return(_isexpanded);}}
public void Expand()
{
if(ListView != null)
if(ListView.InvokeRequired)
throw(new Exception("Invoke Required"));
// The item wasn't expanded -> raise an event
if(Visible && !_isexpanded && ListView != null)
{
TreeListViewCancelEventArgs e = new TreeListViewCancelEventArgs(
this, TreeListViewAction.Expand);
ListView.RaiseBeforeExpand(e);
if(e.Cancel) return;
}
if(Visible)
for(int i = Items.Count - 1 ; i >= 0 ;i--)
{
TreeListViewItem item = this.Items[i];
if(!item.Visible)
{
ListView LView = this.ListView;
LView.Items.Insert(
this.Index + 1,
item);
item.SetIndentation();
}
if(item.IsExpanded)
item.Expand();
}
// The item wasn't expanded -> raise an event
if(Visible && !_isexpanded && ListView != null)
{
this._isexpanded = true;
TreeListViewEventArgs e = new TreeListViewEventArgs(
this, TreeListViewAction.Expand);
ListView.RaiseAfterExpand(e);
if(AfterExpand != null) AfterExpand(this);
}
this._isexpanded = true;
}
public void Collapse()
{
if(ListView != null)
if(ListView.InvokeRequired)
throw(new Exception("Invoke Required"));
// The item was expanded -> raise an event
if(Visible && _isexpanded && ListView != null)
{
TreeListViewCancelEventArgs e = new TreeListViewCancelEventArgs(
this, TreeListViewAction.Collapse);
ListView.RaiseBeforeCollapse(e);
if(e.Cancel) return;
}
// Collapse
if(this.Visible)
foreach(TreeListViewItem item in Items)
item.Hide();
// The item was expanded -> raise an event
if(Visible && _isexpanded && ListView != null)
{
this._isexpanded = false;
TreeListViewEventArgs e = new TreeListViewEventArgs(
this, TreeListViewAction.Collapse);
ListView.RaiseAfterCollapse(e);
if(AfterCollapse != null) AfterCollapse(this);
}
this._isexpanded = false;
}
The item is indented with the public int Level
{
get{return(this.Parent == null ? 0 : this.Parent.Level + 1);}
}
public void SetIndentation()
{
if(this.ListView == null) return;
LV_ITEM lvi = new LV_ITEM();
lvi.iItem = this.Index;
lvi.iIndent = this.Level;
if(TreeListView.ShowPlusMinus) lvi.iIndent++;
lvi.mask = ListViewMessages.LVIF_INDENT;
SendMessage(
this.ListView.Handle,
ListViewMessages.LVM_SETITEM,
0,
ref lvi);
}
TreeListViewItemCollection classThis class works like the PropertiesThe class has a There are two properties FunctionsThe public virtual int Add(TreeListViewItem item)
{
if(TreeListView != null)
if(TreeListView.InvokeRequired)
throw(new Exception("Invoke required"));
// Do not add the item if the collection owns a TreeListView
// recursively and the item already owns a TreeListView
if(TreeListView != null && item.ListView != null)
throw(new Exception("The Item is already in a TreeListView"));
int index = GetInsertCollectionIndex(item);
if(index == -1) return(-1);
if(Parent != null) item.SetParent(Parent);
item.Items.Comparer = this.Comparer;
int treelistviewindex = GetInsertTreeListViewIndex(item, index);
// Insert in the ListView
if(treelistviewindex > -1)
{
ListView listview = (ListView) TreeListView;
listview.Items.Insert(treelistviewindex, (ListViewItem) item);
if(item.IsExpanded) item.Expand();
item.SetIndentation();
}
// Insert in this collection
if(index > -1) List.Insert(index, item);
if(index > -1) OnItemAdded(new TreeListViewEventArgs(item,
TreeListViewAction.Unknown));
return(index);
}
The public virtual void Remove(TreeListViewItem item)
{
if(TreeListView != null)
if(TreeListView.InvokeRequired)
throw(new Exception("Invoke required"));
int index = GetIndexOf(item);
if(index == -1) return;
RemoveAt(index);
}
public new void RemoveAt(int index)
{
if(TreeListView != null)
if(TreeListView.InvokeRequired)
throw(new Exception("Invoke required"));
TreeListViewItem item = this[index];
if(this[index].Visible && this.TreeListView != null) item.Hide();
List.RemoveAt(index);
item.SetParent(null);
OnItemRemoved(new TreeListViewEventArgs(item,
TreeListViewAction.Unknown));
}
Other functions that not described here : The public void Sort(bool recursively)
{
if(TreeListView != null)
if(TreeListView.InvokeRequired)
throw(new Exception("Invoke required"));
// Gets an array of the items
TreeListViewItem[] thisarray = ToArray();
// Removes the items
Clear();
// Adds the items
foreach(TreeListViewItem item in thisarray)
Add(item);
if(recursively)
foreach(TreeListViewItem item in thisarray)
item.Items.Sort(true);
}
Other functions descriptionSubitem editTo edit subitems, the control handles the 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
The XP-style selectionWhen the 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;
}
}
}
The The Indeterminate state itemThe property new public CheckState Checked
{
get
{
if(!Checkable) return HasCheckedChild ? CheckState.Indeterminate :
CheckState.Unchecked;
else return(base.Checked ? CheckState.Checked : CheckState.Unchecked);
}
set
{
if(ListView != null)
if(ListView.FreezeCheckBoxes) return;
if(Checkable && value == CheckState.Indeterminate)
throw(new Exception(
"A checkable item can only have checked and unchecked values"));
if(Checkable)
try{base.Checked = value == CheckState.Checked;}
catch{}
if(Items.Count > 0 && !Checkable)
{
CheckState checkvalue = value == CheckState.Unchecked ?
CheckState.Unchecked : CheckState.Checked;
if(ListView != null)
{ListView.Invoke(
new ChangeChildrenCheckStateRecursivelyHandler(
ChangeChildrenCheckStateRecursively),
new object[]{checkvalue});}
else
ChangeChildrenCheckStateRecursively(checkvalue);
}
}
}
ConclusionMost of the functionalities have been described here. I do not guarantee that this control works 100%. If you find mistakes, you can correct them. This control is not a final release and you can send me mails with explains if you find bugs or mistakes and join the modifications if you did them. The objective is to give a good starting point for those which want to have a control which mixes the History
| ||||||||||||||||||||