Code:
' TREEVIEW WALKER ' by Chris Holbrook 15-Feb-2008 public domain ' Program to demonstrate a reuseable approach to getting checked items ' from a TreeView with checkboxes. Actually it retrieves ' items whose parents, etc right back to the TreeView root ' are checked, in other words items with checked paths. ' ' It uses a very simple generic recursive function to walk the tree ' This function should work with any TreeView. In turn it calls ' another, "user-written" function do do the actual processing of the ' selected TreeView item, passing to it a TV_ITEM pointer and the path ' of the checked item. In this case the function just adds the path to ' a listbox control. ' ' There is also a sub to check the checked item's parent, grandparent, etc etc ' ' when checking an item, the selection is moved to the current item to ' avoid "flashing" the selection. ' ' Lots of help from Steve Rossell's highly recommended code at ' http://www.powerbasic.com/support/pbforums/showthread.php?p=276189&posted=1#post276189 ' #COMPILE EXE #DIM ALL #INCLUDE "commctrl.inc" #INCLUDE "comdlg32.inc" #INCLUDE "Encapsule.inc" #INCLUDE "SQLite.inc" %IDC_DBB_DONE_BN = 1316 %idc_DBB_lab1 = 1317 %idc_DBB_lab2 = 1318 %idc_DBB_lab3 = 1319 %IDC_DBB_TREEVIEW = 1320 %IDC_main_bn = 1321 %idc_listbox = 1322 %OURMSG_EDITORHANDLE = %WM_USER + 100 DECLARE FUNCTION DBBrowser(BYVAL hParent AS DWORD) AS LONG DECLARE FUNCTION mainwindow ( BYVAL hParent AS DWORD ) AS LONG GLOBAL gsDBPath AS STRING GLOBAL gEditWnd AS DWORD FUNCTION PBMAIN () AS LONG InitCommonControls DBBrowser(0) mainwindow(0) END FUNCTION '------------------------------------------------------------------------------ FUNCTION SampleTreeViewInsertItem(BYVAL hTree AS DWORD, BYVAL hParent AS DWORD, sItem AS STRING) AS LONG LOCAL tTVItem AS TV_ITEM LOCAL tTVInsert AS TV_INSERTSTRUCT IF hParent THEN tTVItem.mask = %TVIF_CHILDREN OR %TVIF_HANDLE tTVItem.hItem = hParent tTVItem.cchildren = 1 TreeView_SetItem hTree, tTVItem END IF tTVInsert.hParent = hParent tTVInsert.Item.Item.mask = %TVIF_TEXT tTVInsert.Item.Item.pszText = STRPTR(sItem) tTVInsert.Item.Item.cchTextMax = LEN(sItem) FUNCTION = TreeView_InsertItem(hTree, tTVInsert) END FUNCTION '------------------------------------------------------------------------------ FUNCTION SampleTreeView(BYVAL hDlg AS DWORD, BYVAL lID AS LONG, BYVAL lCount AS LONG) AS LONG LOCAL i AS LONG LOCAL j AS LONG LOCAL k AS LONG LOCAL hCtl AS DWORD LOCAL hRoot AS DWORD LOCAL hParent AS DWORD CONTROL HANDLE hDlg, lID TO hCtl FOR i = 1 TO lCount hRoot = SampleTreeViewInsertItem(hCtl, %NULL, USING$("Root#", i)) FOR j = 1 TO lCount hParent = SampleTreeViewInsertItem(hCtl, hRoot, USING$("Item#", j)) FOR k = 1 TO lCount CALL SampleTreeViewInsertItem(hCtl, hParent, USING$("SubItem#_.#", j, k)) NEXT k NEXT j NEXT i END FUNCTION '------------------------------------------------------------------------------ ' this sub ascends the tree from the current item, setting the checked state to "checked" ' as it goes. Assumption: that the hItem part of the structure pointed to by 2nd param ' is valid. Note that the item pointed to does not have its checkstate changed by this code ' SUB CheckParent ( hTVWnd AS DWORD, BYVAL pTV AS TV_ITEM PTR) LOCAL TreeItem AS TV_ITEM LOCAL i AS LONG TreeItem = @pTV TreeItem.mask = %TVIF_STATE OR %TVIF_HANDLE TreeItem.hItem = TreeView_GetParent ( hTVWnd, TreeItem.hitem) WHILE TreeItem.hItem <> %NULL TreeItem.mask = %TVIF_STATE OR %TVIF_HANDLE TreeItem.stateMask = %TVIS_STATEIMAGEMASK i = 2 ' 2 = checked, 1 = not SHIFT LEFT i, 12 TreeItem.state = i SendMessage(hTVWnd, %TVM_SETITEM, 0, BYVAL VARPTR(TreeItem)) TreeItem.hItem = TreeView_GetParent ( hTVWnd , TreeItem.hitem) WEND END SUB '------------------------------------------------------------------------------ ' function prototype for walkies "onHit" fn ' first param is treeview window handle ' second param is pointer to TV_ITEM ' 3rd param is path DECLARE FUNCTION TVfnPrototype ( hTVWnd AS DWORD, BYVAL pTV AS TV_ITEM PTR, BYVAL psz AS ASCIZ PTR) AS LONG '------------------------------------------------------------------------------ FUNCTION Our_TVITEM_proc ( hTVWnd AS DWORD, BYVAL pTV AS TV_ITEM PTR, BYVAL psz AS ASCIZ PTR) AS LONG ' this line sends the TV item name to a listbox sendmessage geditWnd, %LB_ADDSTRING, 0, psz ' send it on, prefixed by parent id END FUNCTION '------------------------------------------------------------------------------ ' walk tree, execute OnHitfn when a checked item is encountered ' params are treeview control window handle, ' OnHitFn address ' TV item handle ' parent identifier (text of parent TV item) ' FUNCTION walkies ( hTVWnd AS DWORD, OnHitfn AS DWORD, hTVItem AS DWORD, BYVAL pszParent AS ASCIZ PTR ) AS LONG LOCAL sz AS ASCIZ * 1024 LOCAL hti AS tv_item PTR LOCAL treeItem AS TV_ITEM LOCAL pTVITEM AS tv_item PTR LOCAL i AS LONG TreeItem.HItem = hTVItem pTVITEM = VARPTR(TreeItem) WHILE TreeItem.HItem <> %NULL TreeItem.mask = %TVIF_TEXT OR %TVIF_STATE OR %TVIF_HANDLE TreeItem.stateMask = %TVIS_STATEIMAGEMASK TreeItem.cchTextMax = SIZEOF(sz) TreeItem.pszText = VARPTR(sz) sendmessage hTVWnd, %TVM_GETITEM, 0, pTVITEM ' get item ' is it checked? i = TreeItem.state SHIFT RIGHT i, 12 IF i = 2 THEN IF @pszParent <> "" THEN sz = @pszParent + "." + sz CALL DWORD OnHitFn USING TVfnPrototype (hTVWnd, pTVITEM, VARPTR(sz)) CALL walkies(hTVWnd, OnHitFn, TreeView_GetChild(hTVwnd, TreeItem.hitem), VARPTR(sz)) END IF TreeItem.HItem = Treeview_GetNextSibling(hTVWnd, TreeItem.hItem) WEND END FUNCTION '------------------------------------------------------------------------------ CALLBACK FUNCTION DBBrowser_dialogProc() STATIC hmenu, hparent, hTVWnd AS DWORD LOCAL r AS rect LOCAL s, s1 AS STRING LOCAL hDB, l, lresult AS LONG LOCAL sz AS ASCIIZ*64 LOCAL psz AS ASCIZ PTR LOCAL lpNmh AS NMHDR PTR LOCAL lpTV AS NM_TREEVIEW PTR LOCAL treeItem AS TV_ITEM LOCAL pTVITEM AS tv_item PTR LOCAL TVdi AS TV_DISPINFO LOCAL TVht AS TV_HITTESTINFO LOCAL DWPOS AS DWORD SELECT CASE AS LONG CBMSG CASE %WM_INITDIALOG ' Initialization handler 'dialog get user cbhndl, 1 to hEditWnd ' hParent = GetParent (CBHNDL) hTVWnd = getdlgitem(CBHNDL, %IDC_DBB_TREEVIEW) ' gsDBPath = selDB(CBHNDL) ' IF ISFALSE sqlOpen( gsDBPath, hDB) THEN ' ? "unable to open database" , %mb_applmodal,"Warning" ' EXIT SELECT ' END IF ' s = "undefined" ' IF TRIM$(gsDBPath) <> "" THEN ' IF ASC(LEFT$(gsDBPath,1)) <> 0 THEN ' s = gsDBPath ' END IF ' END IF CONTROL ADD LABEL, CBHNDL, %idc_DBB_lab1,"check boxes press button" & s ,5, 2, 175,12 'tablelist ( CBHNDL, %IDC_DBB_TABLES_LB, hDB) SampleTreeView(CBHNDL, %IDC_DBB_TREEVIEW,20) ' DBTreeView( hDB, CBHNDL, %IDC_DBB_TREEVIEW,20) ' CASE %WM_NOTIFY lpNmh = CBLPARAM SELECT CASE @lpNmh.Code CASE %TVN_SELCHANGED 'get selected text lpTV = CBLPARAM 'hTreeItem = @lpTV.ItemNew.hItem TreeItem.hItem = @lpTV.ItemNew.hItem TreeItem.mask = %TVIF_TEXT TreeItem.cchTextMax = SIZEOF(sz) TreeItem.psztext = VARPTR(sz) pTVITEM = VARPTR(TreeItem) CONTROL SEND CBHNDL, %IDC_DBB_TREEVIEW, %TVM_GETITEM, 0, pTVITEM TO lresult sendmessage hTVWnd, %LB_ADDSTRING, 0, VARPTR(sz) CASE %NM_CLICK IF @lpNmh.idFrom = %IDC_DBB_TREEVIEW THEN ' This will ensure that when the user either clicks the checkbox or the ' buttons (+/-) the item/node will be selected. You get a strange flashing ' experience if you do not do this, comment out this code and then use a ' checkbox or the buttons and see what occurs. (found this code on MSDN) dwPos = GetMessagePos ' Get the mouse position TVht.pt.x = LO(WORD, dwPos) ' Store the mouse x pos in the hittest udt tvht.pt.y = HI(WORD, dwPos) ' Store the mouse y pos in the hittest udt MapWindowPoints(%HWND_DESKTOP, @LPnMH.hwndFrom, TVht.pt, 1) ' Get the TreeView item/node returned in the hittest udt member of hItem SendMessage(hTVWnd, %TVM_HITTEST, 0, BYVAL VARPTR(TVht)) ' get item TreeItem.mask = %TVIF_STATE OR %TVIF_HANDLE TreeItem.hItem = TVHT.Hitem sendmessage hTVWnd, %TVM_GETITEM, 0, VARPTR(TreeItem) CheckParent ( hTVWnd, TreeItem.hitem) ' Select the TreeView item/node that was clicked on (returned above) SendMessage(hTVWnd, %TVM_SELECTITEM, %TVGN_CARET, TVht.hItem) END IF END SELECT CASE %WM_NCACTIVATE STATIC hWndSaveFocus AS DWORD IF ISFALSE CBWPARAM THEN ' Save control focus hWndSaveFocus = GetFocus() ELSEIF hWndSaveFocus THEN ' Restore control focus SetFocus(hWndSaveFocus) hWndSaveFocus = 0 END IF CASE %WM_LBUTTONDOWN SendMessage CBHNDL, %WM_NCLBUTTONDOWN, %HTCAPTION, BYVAL %NULL ' force drag CASE %WM_COMMAND SELECT CASE CBCTL ' <- look at control's id CASE %IDC_DBB_DONE_BN IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN walkies ( hTVWnd, CODEPTR(Our_TVITEM_proc), TreeView_GetRoot( hTVWnd), BYVAL %null) END IF END SELECT END SELECT END FUNCTION '------------------------------------------------------------------------------ FUNCTION DBBrowser(BYVAL hParent AS DWORD) AS LONG LOCAL lRslt AS LONG LOCAL hDlg AS DWORD LOCAL s AS STRING DIALOG NEW hParent, "", 10, 100, 140, 205, %WS_POPUP OR %WS_THICKFRAME OR _ %WS_CLIPSIBLINGS OR %WS_VISIBLE OR %DS_3DLOOK OR %DS_NOFAILCREATE OR _ %DS_SETFONT, %WS_EX_CONTROLPARENT OR %WS_EX_TOOLWINDOW OR _ %WS_EX_TOPMOST OR %WS_EX_LEFT OR %WS_EX_LTRREADING TO hdlg DIALOG SET COLOR hDlg, -1, RGB(255, 255, 155) CONTROL ADD BUTTON, hDlg, %IDC_DBB_DONE_BN, "Send selection", 70, 190, 70, 15 CONTROL ADD "SysTreeView32", hDlg,%IDC_DBB_TREEVIEW, "SysTreeView32_1", 5, 15, 135, 175, %WS_CHILD OR %WS_VISIBLE OR _ %WS_TABSTOP OR %TVS_HASBUTTONS OR %TVS_HASLINES OR %TVS_CHECKBOXES OR %TVS_LINESATROOT OR %TVS_SHOWSELALWAYS, %WS_EX_CLIENTEDGE OR _ %WS_EX_LEFT OR %WS_EX_LTRREADING OR %WS_EX_RIGHTSCROLLBAR DIALOG SHOW MODELESS hDlg, CALL DBBrowser_dialogProc TO lRslt FUNCTION = lRslt END FUNCTION '-------------------------------------------------------------------------------- CALLBACK FUNCTION mainwindowproc() LOCAL sz AS ASCIZ * 64 SELECT CASE AS LONG CBMSG CASE %WM_COMMAND SELECT CASE CBCTL ' <- look at control's id CASE %IDC_MAIN_BN IF CBCTLMSG = %BN_CLICKED OR CBCTLMSG = 1 THEN DIALOG END CBHNDL, 0 END IF END SELECT END SELECT END FUNCTION '-------------------------------------------------------------------------------- FUNCTION mainwindow ( BYVAL hParent AS DWORD ) AS LONG LOCAL lRslt AS LONG LOCAL hDlg AS DWORD DIALOG NEW hParent, "", 180, 100, 200, 210, %WS_POPUP OR %WS_THICKFRAME OR _ %WS_CLIPSIBLINGS OR %WS_VISIBLE OR %DS_3DLOOK OR %DS_NOFAILCREATE OR _ %DS_SETFONT, %WS_EX_CONTROLPARENT OR _ %WS_EX_TOPMOST OR %WS_EX_LEFT OR %WS_EX_LTRREADING TO hdlg CONTROL ADD LISTBOX, hdlg, %IDC_LISTBOX,, 0,0,200,200, _ %WS_CHILD OR %WS_TABSTOP OR %WS_VISIBLE OR %WS_VSCROLL OR %LBS_NOTIFY, _ %WS_EX_CLIENTEDGE OR %WS_EX_LEFT OR %WS_EX_LTRREADING OR %WS_EX_RIGHTSCROLLBAR gEditWnd = getdlgitem(hdlg, %IDC_LISTBOX) CONTROL ADD BUTTON, hDlg, %IDC_main_bn, "exit", 170, 195, 30, 15 DIALOG SHOW MODAL hdlg CALL mainwindowproc END FUNCTION
Comment