A completely custom tree-view control for AutoHotkey (AHK).
- Introduction
- TreeViewEx_Tab.ahk
- TreeViewEx_ContextMenu.ahk
- TreeViewEx: Tested methods and properties
- TreeViewEx - Tested notification handlers
- TreeViewEx - Tested notification handlers - Node
- TreeViewEx - Tested notification handlers - Node_C
- TreeViewEx - Tested notification handlers - Node_Ptr
- TreeViewEx_Node
- Table of contents generated by Headers2ToC.ahk
With over 30 classes, over 250 methods, and over 60 functions, TreeViewEx implements nearly all
functionality related to tree-view controls offered by the Windows API.
TreeViewEx does not extend Gui.TreeView, nor does it inherit from Gui.Control. It is a
completely custom class built from the ground up to provide the developer with a familiar AHK
wrapper around one of Microsoft's most complex Win32 common controls.
You can use TreeViewEx as a standard tree-view control, taking advantage of its many helpful tools
and customization options:
- Provides AHK wrappers for almost all of the TVM messages
- Exposes all dynamic attributes to external code, including window styles, extended window styles, line / text color, state icons, and more
- Includes custom functions to handle complex tasks such as recursive processing, sending and handling TVN notifications, defining window message handlers, etc...
- Maps structure byte offsets to object properties so you can quickly fill in structure members using convenient property notation (no NumGet / NumPut needed)
The library's scope goes beyond that of just a tree-view control. TreeViewEx exposes the lower-level
processes that AHK hides from our top-level code. Your code has direct access to the window messages
and notifications sent to both the parent gui window and the tree-view control itself. These tools
are packaged into familiar "OnCommand", "OnNotify", and "OnMessage" functions so you can seamlessly
bridge old code with this advanced functionality.
Along with TreeViewEx is TreeViewEx_Node, an object-oriented approach to working with tree-view
items that will feel more familiar and comfortable for those who have not explored Windows API concepts
such as window messages and notifications. TreeViewEx_Node is an extensible framework for systematically
controlling the content and presentation of the tree-view items. TreeViewEx_Node makes it easy
for our AHK code to respond to all of the tree-view notifications
like TVN_GETDISPINFO and NM_CLICK.
Check back regularly because I will soon be releasing code that subclasses TreeViewEx to:
- Explore objects visually by recursively mapping object properties to tree-view items
- Integrate with UIA to view a window's entire UIA tree
- Inspect UIA element properties and available patterns
To use the library #include VENV.ahk.
VENV.ahk packages all of the files together. To make the library available to any script, I recommend
this setup:
- Clone the repository.
git clone https://github.com/Nich-Cebolla/AutoHotkey-TreeViewEx
- Make a copy of the cloned repository and work with the copy. This is to avoid a situation where
pulling an update breaks our scripts; by using a separate copy we can give ourselves time to review
updates before updating the active copy.
xcopy AutoHotkey-TreeViewEx AutoHotkey-TreeViewEx-Active /I /E - Add a file TreeViewEx.ahk to your lib folder.
In the file is a single statement.
#include C:\users\you\path\to\AutoHotkey-TreeViewEx-Active\src\VENV.ahk
With this setup, we can use #include <TreeViewEx> from any other script. Also, when we want to test
updates to the repository, we just need to go into that file and change the path to the VENV.ahk
file in the git clone, then run our scripts that use TreeViewEx to check for errors. Then change
the path back when done.
TreeViewEx requires the following dependencies. I recommend preparing the dependencies with a
similar approach to the above steps.
Rect- Clone AutoHotkey-LibV2, create a copy of
the directory to use as your active copy, then create a file Rect.ahk in your lib folder
with
#include C:\users\you\path\to\AutoHotkey-LibV2-Active\structs\Rect.ahk.
- Clone AutoHotkey-LibV2, create a copy of
the directory to use as your active copy, then create a file Rect.ahk in your lib folder
with
LibraryManagerLibraryManageris also in AutoHotkey-LibV2, so just create a file LibraryManager.ahk in your lib folder with#include C:\users\you\path\to\AutoHotkey-LibV2-Active\LibraryManager.ahk.
Container- Clone
Container, create a copy of the directory to use as your active copy, then create a file Container.ahk in your lib folder with#include C:\users\you\path\to\Container-Active\src\Container.ahk.
- Clone
ImageList: Create anImageListfrom an array of file paths or an array of bitmap pointers. For use withTreeViewEx.Prototype.SetImageListand related methods. Requires several dependencies from the same repository.Logfont: A full-featured font object.TreeViewExhas a built-inTreeViewEx_LogFontclass which encapsulates the core functionality necessary for adjusting the control's font, butTreeViewEx_LogFontdoes not include functionality related to enumerating a system's fonts and evaluating the fonts. If your application would benefit from being able to find the optimal font available on the system, check out Logfont.MenuEx: - A class that streamlines the process of creating a context menu. See file test\demo-context-menu.ahk for an example.
See the demo script
test\demo-NotificationHandlers
for a working example. The demo file will run as-is (but still requires the above dependencies). The
demo script focuses on setting event handlers for TVN notifications. All nodes are added to the
tree-view with item.pszText = LPSTR_TEXTCALLBACK and item.cChildren = TVIF_CHILDREN. The following
notifications are handled in the demo:
- TVN_BEGINLABELEDITW
- TVN_DELETEITEMW
- TVN_ENDLABELEDITW
- TVN_GETDISPINFOW
- TVN_GETINFOTIPW
- TVN_ITEMCHANGEDW
- TVN_ITEMCHANGINGW
- TVN_ITEMEXPANDEDW
- TVN_ITEMEXPANDINGW
- TVN_SETDISPINFOW
- NM_CLICK
See test\demo-context-menu.ahk for an example of using TreeViewEx_ContextMenu
TreeViewEx_Tab.ahk is a separate script (not included in VENV.ahk) containing class TreeViewEx_Tab.
TreeViewEx_Tab has one dependency, TabEx.ahk.
TreeViewEx_Tab creates a tab control in a gui window and makes it easy to associate new TreeViewEx
controls with an individual tab so the user can change what information is displayed by selecting
a new tab.
For the developer, TreeViewEx_Tab handles all of the legwork around adding, positioning, displaying,
and removing TreeViewEx controls and the tabs they are associated with. These are the general steps
for using the class:
- Prepare TabEx.ahk in the same way as described above.
- Add a file TreeViewEx_Tab.ahk to your lib folder.
In the file is a single statement.
#include C:\users\you\path\to\AutoHotkey-TreeViewEx-Active\src\TreeViewEx_Tab.ahk
- In your script that will use
TreeViewEx_Tab, include:#include <TreeViewEx_Tab>
- (Optional) Create a context menu class using MenuEx.
- (Optional) Define a function that all new
TreeViewExcontrols will be passed to to use custom instantiation logic. - Define 0-3 options objects, each associated with a parameter of
TreeViewEx_Tab.Prototype.__New.None of the options are strictly necessary, but generally you'll want to create the__New(GuiObj, Options?, DefaultAddOptions := '', DefaultTreeViewExOptions := '')
Optionsobject to specify the tab control's options, and optionally supply the objects described by steps 4 and 5 above. The other two,DefaultAddOptionsandDefaultTreeViewExOptions, can be left the default for general use.Options- The options forTreeViewEx_Taband the tab control it will create. These are described in the parameter hint aboveTreeViewEx_Tab.Prototype.__New.DefaultAddOptions- The default options that will be used when adding aTreeViewExcontrol viaTreeViewEx_Tab.Prototype.Add. These are described in the parameter hint aboveTreeViewEx_Tab.Prototype.Add.DefaultTreeViewExOptions- The default options that will be used when adding aTreeViewExcontrol viaTreeViewEx_Tab.Prototype.Add. These are described in the parameter hint aboveTreeViewEx.Prototype.__New.
- Create the object
options := { opt: 'w400 r15', name: 'tab' } ; no context menu or instantiation callback seen here tvexTab := TreeViewEx_Tab(GuiObj, options) ; assume GuiObj reference a Gui object - Add
TreeViewExcontrols to the tab control by supplying a name. If using the default add options, callingTreeViewEx_Tab.Prototype.Addwith just a name will:
- Create a new tab using the name.
- Create a new
TreeViewExcontrol positioned neatly in the center of the tab control's client area. The control is associated with the newly created tab. This does not automatically display the tab (unless its the first tab). - Return a
TreeViewEx_Tab.Itemobject. TheTreeViewExcontrol is set to property "tvex".
See the test file test\test-TreeViewEx_Tab.ahk for a working demo.
TreeViewEx_ContextMenu.ahk is a separate script (not included in VENV.ahk) containing class TreeViewEx_ContextMenu.
TreeViewEx_ContextMenu extends MenuEx, which
is required to use TreeViewEx_ContextMenu.
TreeViewEx_ContextMenu currently includes the following context menu items:
- Copy node ID: Copies the handle of the item beneath the cursor.
- Copy label: Copies the label of the item beneath the cursor.
- Collapse recursive: Calls
TreeViewEx.Prototype.CollapseRecursiveNotifyfor the item beneath the cursor. - Collapse all recursive: Calls
TreeViewEx.Prototype.CollapseRecursiveNotifyfor all root items. - Collapse parent: Calls
TreeViewEx.Prototype.CollapseNotifyfor the parent of the item beneath the cursor. - Collapse parent recursive: Calls
TreeViewEx.Prototype.CollapseRecursiveNotifyfor the parent of the item beneath the cursor. - Collapse siblings: Calls
TreeViewEx.Prototype.CollapseNotifyfor each sibling of the item beneath the cursor. - Collapse siblings recursive: Calls
TreeViewEx.Prototype.CollapseRecursiveNotifyfor each sibling of the item beneath the cursor. - Expand recursive: Calls
TreeViewEx.Prototype.ExpandRecursiveNotifyfor the item beneath the cursor. - Expand all recursive: Calls
TreeViewEx.Prototype.ExpandRecursiveNotifyfor all root items. - Scroll to top: Sends TVM_ENSUREVISIBLE for the item identified by TVGN_ROOT.
- Scroll to bottom: Sends TVM_ENSUREVISIBLE for the item identified by TVGN_LASTVISIBLE.
- Select parent: Selects and sends TVM_ENSUREVISIBLE for the parent of the item beneath the cursor.
- Select previous sibling: Selects and sends TVM_ENSUREVISIBLE for the sibling above the item beneath the cursor.
- Select next sibling: Selects and sends TVM_ENSUREVISIBLE for the sibling beneath the item beneath the cursor.
The following is a list of methods and properties. The items with an "X" next to them have been tested. The items with no "X" have not been tested. Most of the methods and properties probably work, but only the marked items have been verified.
| Name | Is Tested |
|---|---|
| Add | X |
| Delete | X |
| Get | X |
| Name | Is Tested |
|---|---|
| __New | X |
| AddNode | X |
| AddNode_C | |
| AddObj | X |
| AddObjList | X |
| AddObjListFromTemplate | X |
| AddTemplate | X |
| Collapse | X |
| CollapseNotify | X |
| CollapseRecursive | X |
| CollapseRecursiveNotify | X |
| CollapseReset | |
| CollapseResetNotify | |
| CopyText | X |
| CreateDragImage | |
| CreateParentSubclass | X |
| DeleteAll | X |
| DeleteAll_C | X |
| DeleteChildren | X |
| DeleteCommandCode | X |
| DeleteItem | X |
| DeleteMessageCode | X |
| DeleteNode_C | |
| DeleteNotifyCode | X |
| Destroy | X |
| Dispose | X |
| EditLabel | X |
| EndEditLabel | X |
| EnsureVisible | X |
| EnumChildren | X |
| EnumChildrenRecursive | X |
| Expand | X |
| ExpandNotify | X |
| ExpandPartial | |
| ExpandRecursive | X |
| ExpandRecursiveNotify | X |
| ExpandRecursiveNotifySelective | X |
| ExpandRecursiveSelective | X |
| GetAdjacentRect | X |
| GetBkColor | X |
| GetChild | X |
| GetCount | X |
| GetEditControl | X |
| GetExtendedStyle | X |
| GetFont | X |
| GetImageList | |
| GetIndent | |
| GetInsertMarkColor | |
| GetISearchString | |
| GetItem | X |
| GetItemHeight | X |
| GetItemRect | X |
| GetItemState | X |
| GetLineColor | |
| GetLineRect | |
| GetNext | X |
| GetNode | X |
| GetNode_C | X |
| GetNode_Ptr | X |
| GetParent | X |
| GetPos | X |
| GetRect | X |
| GetRoot | |
| GetScrollTime | |
| GetSelected | X |
| GetTemplate | X |
| GetTemplateDispInfo | |
| GetTemplateInfoTip | |
| GetTemplateItemChange | |
| GetTemplateKeyDown | |
| GetTemplateNmtv | X |
| GetText | X |
| GetTextColor | |
| GetTooltips | |
| GetVisibleCount | |
| HasChildren | X |
| Hide | X |
| HitTest | X |
| Insert | X |
| IsAncestor | X |
| IsExpanded | X |
| IsRoot | X |
| MapAccIdToHTreeItem | |
| MapHTreeItemToAccId | |
| OnCommand | |
| OnMessage | X |
| OnNotify | X |
| Redraw | X |
| RemoveParentSubclass | X |
| ScrollToBottom | X |
| ScrollToTop | X |
| Select | X |
| SendBeginDrag | |
| SendBeginLabelEdit | X |
| SendBeginRDrag | |
| SendDeleteItem | |
| SendEndLabelEdit | X |
| SendGetDispInfo | |
| SendGetInfoTip | |
| SendItemChanged | |
| SendItemChanging | |
| SendItemExpanded | X |
| SendItemExpanding | X |
| SendKeyDown | |
| SendSetDispInfo | |
| SetAutoScrollInfo | |
| SetBkColor | |
| SetContextMenu | X |
| SetExtendedStyle | |
| SetHeight | X |
| SetImageList | |
| SetIndent | |
| SetInsertMark | |
| SetInsertMarkColor | |
| SetItem | X |
| SetItemHeight | |
| SetItemState | X |
| SetLineColor | |
| SetNodeConstructor | X |
| SetRedraw | X |
| SetScrollTime | |
| SetStatus | X |
| SetTextColor | |
| SetTooltips | |
| SetTvexTabId | X |
| Show | X |
| ShowInfoTip | |
| SortChildren | |
| SortChildrenCb | |
| Toggle |
| Name | Is Tested |
|---|---|
| AutoHScroll | |
| Checkboxes | |
| ContextMenuActive | X |
| DimmedCheckboxes | |
| DisableDragDrop | |
| DoubleBuffer | |
| DrawImageAsync | |
| EditLabels | |
| Enabled | X |
| ExclusionCheckboxes | |
| FadeInOutExpandos | |
| FullRowselect | |
| Gui | X |
| HandlerChildrenGet | |
| HandlerGetDispInfo | |
| HandlerImageGet | |
| HandlerImageSet | |
| HandlerNameGet | |
| HandlerNameSet | |
| HandlerSelectedImageGet | |
| HandlerSelectedImageSet | |
| HandlerSetDispInfo | |
| HasButtons | |
| HasLines | |
| Infotip | |
| LinesAtRoot | |
| MultiSelect | |
| NoHScroll | |
| NoIndentState | |
| NonEvenHeight | |
| NoScroll | |
| NoSingleCollapse | |
| NoTooltips | |
| PartialCheckboxes | |
| RichTooltip | |
| RtlReading | |
| ShowSelAlways | |
| SingleExpand | |
| TrackSelect | |
| TvexTab | X |
| Visible | X |
TreeViewEx currently offers three sets of notification handlers, each using a different approach to
managing the item collection on our side of the code.
These are the functions in file "src\notify-node.ahk". They get the node object by calling
TreeViewEx.Prototype.GetNode(Handle), which calls TreeViewExObj.NodeConstructor(Handle).
| Name | Is Tested |
|---|---|
| TreeViewEx_HandlerBeginLabelEdit_Node | |
| TreeViewEx_HandlerDeleteItem_Node | |
| TreeViewEx_HandlerEndLabelEdit_Node | |
| TreeViewEx_HandlerGetDispInfo_Node | |
| TreeViewEx_HandlerGetInfoTip_Node | |
| TreeViewEx_HandlerItemChanged_Node | |
| TreeViewEx_HandlerItemChanging_Node | |
| TreeViewEx_HandlerItemExpanded_Node | |
| TreeViewEx_HandlerItemExpanding_Node | |
| TreeViewEx_HandlerSetDispInfo_Node | |
| TreeViewEx_HandlerSingleExpand_Node |
These are the functions in file "src\notify-node-c.ahk". They get the node object by calling
TreeViewEx.Prototype.GetNode_C(Handle), which accesses TreeViewExObj.Collection.Get(Handle).
| Name | Is Tested |
|---|---|
| TreeViewEx_HandlerBeginLabelEdit_Node_C | |
| TreeViewEx_HandlerDeleteItem_Node_C | |
| TreeViewEx_HandlerEndLabelEdit_Node_C | |
| TreeViewEx_HandlerGetDispInfo_Node_C | |
| TreeViewEx_HandlerGetInfoTip_Node_C | |
| TreeViewEx_HandlerItemChanged_Node_C | |
| TreeViewEx_HandlerItemChanging_Node_C | |
| TreeViewEx_HandlerItemExpanded_Node_C | |
| TreeViewEx_HandlerItemExpanding_Node_C | |
| TreeViewEx_HandlerSetDispInfo_Node_C | |
| TreeViewEx_HandlerSingleExpand_Node_C |
These are the functions in file "src\notify-node-ptr.ahk". They get the node object by calling
node := ObjFromPtrAddRef(struct.lParam).
| Name | Is Tested |
|---|---|
| TreeViewEx_HandlerBeginLabelEdit_Node_Ptr | X |
| TreeViewEx_HandlerDeleteItem_Node_Ptr | X |
| TreeViewEx_HandlerEndLabelEdit_Node_Ptr | X |
| TreeViewEx_HandlerGetDispInfo_Node_Ptr | X |
| TreeViewEx_HandlerGetInfoTip_Node_Ptr | X |
| TreeViewEx_HandlerItemChanged_Node_Ptr | X |
| TreeViewEx_HandlerItemChanging_Node_Ptr | X |
| TreeViewEx_HandlerItemExpanded_Node_Ptr | X |
| TreeViewEx_HandlerItemExpanding_Node_Ptr | X |
| TreeViewEx_HandlerSetDispInfo_Node_Ptr | X |
| TreeViewEx_HandlerSingleExpand_Node_Ptr |
Each instance of TreeViewEx_Node has a property "Handle" which is set with the HTREEITEM handle.
This is the same value as the ItemID parameter used by various TreeView methods as described in
the AutoHotkey documentation. For example,
the first parameter of TreeView.Prototype.Modify,
expects an HTREEITEM handle. When you call a method or access a property from a TreeViewEx_Node
instance, in most cases it calls a function which sends a TVM message where the wParam or lParam
is expected to be an HTREEITEM handle.
TreeViewEx_Node is intended to be extended and coupled with callback functions passed to
TreeViewEx.Prototype.OnNotify. This allows our code to reduce memory overhead and increase
customizability by controlling characteristics of the tree-view items dynamically.
This section needs more work and there may be some delay before I get back to writing it. Use the demo file as a guide, and if something is unclear or you have a question, message @Cebolla on AutoHotkey.com.
For TVN_GETDISPINFO notifications, your function is expected to fill the members of the TVITEMW structure with the requested information. Your code does not need to calculate any of the byte offsets, this is already done for you by the TvDispInfoEx class. Your code can simply assign a value to the appropriate property.
TVN_GETDISPINFO is only sent if the relevant member of a tree-view item's TVITEMW structure has been set with a specific value:
- The
pszTextmember of the item's TVITEMW structure is theLPSTR_TEXTCALLBACKvalue (-1). - The
iImagemember of the item's TVITEMW structure is theI_IMAGECALLBACKvalue (-1). - The
iSelectedImagemember of the item's TVITEMW structure is theI_IMAGECALLBACKvalue (-1). - The
cChildrenmember of the item's TVITEMW structure is theI_CHILDRENCALLBACKvalue (-1).
TVN_SETDISPINFO is only
sent if the relevant member of a tree-view item's
TVITEMW structure
has been set with a specific value. Unlike
TVN_GETDISPINFO which has
four related members, TVN_SETDISPINFO only has three:
- The
pszTextmember of the item's TVITEMW structure is theLPSTR_TEXTCALLBACKvalue (-1). - The
iImagemember of the item's TVITEMW structure is theI_IMAGECALLBACKvalue (-1). - The
iSelectedImagemember of the item's TVITEMW structure is theI_IMAGECALLBACKvalue (-1).
The tree-view control sends TVN_SETDISPINFO notifications to inform your code that an event occurred that caused a change to a characteristic of a tree-view item. Typically the events are raised by the user's actions. The notification provides information about what characteristic was changed, and provides the new value.
The following are the contexts in which the tree-view control sends TVN_SETDISPINFO:
- If the
pszTextmember of the item's The user edits the label of an item (if this functionality is enabled for the control). Themaskmember will have theTVIF_FLAGset. - An item's selected state has changed. If the item's selected state has changed from being selected
to being not selected, the
maskmember will have theTVIF_IMAGEflag set. If the item's selected state has changed from being not selected to being selected, themaskmember will have theTVIF_SELECTEDIMAGEflag set.
The following is a list of methods and properties. The items with an "X" next to them have been tested. The items with no "X" have not been tested. Most of the methods and properties probably work, but only the marked items have been verified.
| Name | Is Tested |
|---|---|
| SetChildrenHandler | |
| SetNameHandler | |
| SetImageHandler | |
| SetSelectedImageHandler |
| Name | Is Tested |
|---|---|
| __New | |
| AddChild | |
| Copy | |
| CopyItemId | |
| Collapse | |
| CollapseReset | |
| CreateDragImage | |
| EnsureVisible | |
| EnumChildren | |
| EnumChildrenRecursive | |
| Expand | |
| ExpandPartial | |
| GetItemState | |
| GetText | |
| MapHTreeItemToAccId | |
| Select | |
| SetInsertMark | |
| SetTreeView | |
| ShowInfoTip | |
| SortChildren | |
| SortChildrenCb | |
| Toggle |
| Name | Is Tested |
|---|---|
| Child | |
| Ctrl | |
| Gui | |
| InfoChildren | |
| InfoImage | |
| InfoName | |
| InfoSelectedImage | |
| IsParent | |
| IsRoot | |
| LineRect | |
| Next | |
| Parent | |
| Previous | |
| Root | |
| Rect |