Imports System.Runtime.InteropServices
'typedef struct tagTVITEMEX {
' UINT mask;
' HTREEITEM hItem;
' UINT state;
' UINT stateMask;
' LPTSTR pszText;
' int cchTextMax;
' int iImage;
' int iSelectedImage;
' int cChildren;
' LPARAM lParam;
' int iIntegral;
'#if (_WIN32_IE >= 0x0600)
' UINT uStateEx;
' HWND hwnd;
' int iExpandedImage;
'#End If
'#If (NTDDI_VERSION >= NTDDI_WIN7) Then
' int iReserved;
'#End If
'} TVITEMEX, *LPTVITEMEX;
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)> _
Public Structure TVITEMEX
Public mask As UInteger
Public hItem As IntPtr
Public state As UInteger
Public stateMask As UInteger
Public pszText As String
Public cchTextMax As Integer
Public iImage As Integer
Public iSelectedImage As Integer
Public cChildren As Integer
Public lParam As Integer
Public iIntegral As Integer
Public uStateEx As UInteger
Public hwnd As IntPtr
Public iExpandedImage As Integer
'Not sure if this should be defined
'What the flying ***** is NTDDI_WIN7 anyways ?
Public iReserved As Integer
End Structure
'typedef struct {
' HTREEITEM hParent;
' HTREEITEM hInsertAfter;
'#if (_WIN32_IE >= 0x0400)
' union {
' TVITEMEX itemex;
' TVITEM item;
' } DUMMYUNIONNAME;
'#Else
' TVITEM item;
'#End If
'} TVINSERTSTRUCT, *LPTVINSERTST
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode)> _
Public Structure TVInsertStruct
Public hParent As IntPtr
Public hInsertAfter As IntPtr
<MarshalAs(UnmanagedType.Struct)> _
Public itemEx As TVITEMEX
End Structure
Public Class TreeViewEx
Inherits TreeView
Private Const TVM_GETITEMW = (&H1100 + 62)
Private Const TVM_SETITEMW = (&H1100 + 63)
'Use:-
'ins = Marshal.PtrToStructure(m.LParam, GetType(TVInsertStruct))
'to get the Structure used to insert the item
Private Const TVM_INSERTITEMW = (&H1100 + 50)
Private Const TVM_DELETEITEM = (&H1100 + 1)
Private Const TVIF_HANDLE = (&H10)
Private g_lstRemNodes As New List(Of TreeNode)
Public Event NodeAdded As EventHandler(Of NodeAddedEventArgs)
Public Event NodesRemoved As EventHandler(Of NodesRemovedEventArgs)
Protected Overridable Sub OnNodeAdded(ByVal e As NodeAddedEventArgs)
RaiseEvent NodeAdded(Me, e)
End Sub
Protected Overridable Sub OnNodesRemoved(ByVal e As NodesRemovedEventArgs)
RaiseEvent NodesRemoved(Me, e)
End Sub
Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
MyBase.WndProc(m)
Dim it As TVITEMEX
'I've only observed this message being sent
'presumably to select several nodes(1 message per node)
'before deleting them
If m.Msg = TVM_GETITEMW Then
it = Marshal.PtrToStructure(m.LParam, GetType(TVITEMEX))
If it.hItem <> IntPtr.Zero Then
Dim n As TreeNode = FindNodeByHandle(it.hItem)
g_lstRemNodes.Add(n)
End If
End If
'GETITEM messages sent before would have identified the nodes
'that are going to be deleted.
'IMPORTANT!!!!!
'I cannot be certain that there are not other circumstances where
'GETITEM messages would be sent. If this happens then there probably
'will be nodes in the list that were not actually deleted.
If m.Msg = TVM_DELETEITEM Then
OnNodesRemoved(New NodesRemovedEventArgs(g_lstRemNodes.ToArray))
g_lstRemNodes.Clear()
End If
If m.Msg = TVM_SETITEMW Then
it = Marshal.PtrToStructure(m.LParam, GetType(TVITEMEX))
'The handle of a TreeNode object is ReadOnly so
'there is no way that .Net code can assign a handle
'to a TreeNode object. Only the TreeView's low level
'code does therefor when it happens it could only
'mean that a node was just added to the treeview
If it.mask And TVIF_HANDLE Then
If it.hItem <> IntPtr.Zero Then
OnNodeAdded(New NodeAddedEventArgs(FindNodeByHandle(it.hItem)))
End If
End If
End If
End Sub
Private Function FindNodeByHandle(ByVal handle As IntPtr) As TreeNode
For Each N As TreeNode In GetAllTreeNodes(Me.Nodes)
If N.Handle = handle Then Return N
Next
Return Nothing
End Function
Private Function GetAllTreeNodes(ByVal baseCollection As TreeNodeCollection) As TreeNode()
Dim al As New List(Of TreeNode)(100)
For Each N As TreeNode In baseCollection
al.Add(N)
If N.Nodes.Count > 0 Then al.AddRange(GetAllTreeNodes(N.Nodes))
Next
Return al.ToArray
End Function
End Class
Public Class NodeAddedEventArgs
Inherits EventArgs
Private _node As TreeNode
Public Sub New(ByVal n As TreeNode)
_node = n
End Sub
Public ReadOnly Property Node() As TreeNode
Get
Return _node
End Get
End Property
End Class
Public Class NodesRemovedEventArgs
Inherits EventArgs
Private _RemovedNodes As TreeNode()
Private _cancel As Boolean = False
Public Sub New(ByVal nodes As TreeNode())
_RemovedNodes = nodes
End Sub
Public ReadOnly Property RemovedNodes() As TreeNode()
Get
Return _RemovedNodes
End Get
End Property
End Class