Version: 2.00.00 - Last Update: Sunday the 01th, May 2011 - 10:25:00 (I added a more obvious download link:-)
FoxPro Rocks! This is all about how to create complex controls using native VFP only!
Intro
Wow, finally! I found the time to finish part IV-b of my "Native TreeView" series - gosh...
I also added a link to the latest downloadable ZIP file containing all beta version sources and resources >>> A new (BETA) version of the right-click menu is available (I spent some time on debugging an enhancing it)
<< Download from there the right-click menu thing
Yep! Here it comes: a first (downloadable!)
impression of the all-native VFP TreeView thing :-)
Just click image below!
What you get within the gridtree_alpha_01.zip file are some icons that are/can be used to decorate the nodes, the driving table files treeviews.dbf/fpt/cdx and the demo (a VFP 9.0 executable) Sorry, I dropped the sources for that old demo a long time ago – but you’re welcome to recreate them if you have a de-compiler (like ReFox).
The driving table’s structure looks like:
Column Name | Description |
pNodeid | primary key |
pParentid | parent key |
pCcaption | node caption |
pIorder | sort order (to achieve hierarchical sequence) |
pIindent | indentation level |
pIchildcnt | number of child nodes on next level of indentation |
pLvisible | internal collapsed/expanded marker |
pLexpanded | controls +/- symbols |
pLlastitem | <.T.> if the current node is the last one (child node) in a branch |
pNmark | <1> if node is marked (if option group this is the #pointer# to the selected option node) |
pBit_Objs | bit field: node layout (not implemented yet) |
pBit_Visib | bit field: visible node items |
pBit_State | bit field: node items states |
pBit_Lines | bit field: tree lines states |
pBit_Back | bit field: ?? |
pCimg_Expd | expanded node image |
pCimg_Coll | ?? check it out yourself ;-) |
pCicon_Def | default node image |
pCicon_Sel | selected node image |
pCicon_Foc | focused node image |
pCicon_Grp | grouped node(s) image |
pCicon_Dis | disabled node image |
pIcaptfore | caption fore color |
pIcaptback | caption back color |
pIcaptbold | caption bold flag |
pCtext | sub-caption text |
pItextfore | sub-caption fore color |
pItextback | sub-caption back color |
pLtextbold | sub-caption bold flag |
pMhelp | help text (not implemented) |
pMerror | error text (not implemented) |
Some of these settings are not fully implemented in this alpha version. Thus, don’t get confused if not all features are working. You may try to right click on the tree nodes to get some additional (re-)actions :-) Please keep in mind that my latest version doesn’t use a driving cursor complex like the given one! This solution was one of the first to test if it is possible to create a VFP-native TreeView, at all! To prove that I’m using nothing but a regular VFP GRID internally, try to locate the grid’s row-height resizing area (in the upper left corner of the grid area) and then resize the grid’s row height >>>
<< Download from there the native VFP TreeView thing!
For those of you who want even more background information – here is the listing of a little helper routine I used during initial development to recreate the diverse indices of the driving table. As you can see there are many of them to speed things up (for large tables) using VFP’s rushmore technology. You may read my short remarks in the code fragment to get an idea for what the indices/fields are used, then.
WAIT WINDOW NOWAIT "Deleting all CDX tags" DELETE TAG ALL *\\ *\\ Transferring the flag bits from pBit_Visib and pBit_State to pBit_Objs *// *\\ NODE_IS_ENABLED WAIT WINDOW NOWAIT "Processing : NODE_IS_ENABLED" REPLACE ALL pbit_objs WITH BITSET(pbit_objs, 0) FOR BITTEST(pBit_State,0) *// *\\ NODE_IS_FOCUSED (nur löschen - only deleting) WAIT WINDOW NOWAIT "Processing : NODE_IS_FOCUSED" REPLACE ALL pbit_objs WITH BITCLEAR(pbit_objs, 1) *// *\\ NODE_IS_MARKED WAIT WINDOW NOWAIT "Processing : NODE_IS_MARKED" REPLACE ALL pbit_objs WITH BITSET(pbit_objs, 2) FOR BITTEST(pBit_State,1) *// *\\ NODE_IS_ROOTNODE WAIT WINDOW NOWAIT "Processing : NODE_IS_ROOTNODE" REPLACE ALL pbit_objs WITH BITSET(pbit_objs, 3) FOR BITTEST(pBit_State,4) *// *\\ NODE_IS_OPTIONGROUP WAIT WINDOW NOWAIT "Processing : NODE_IS_OPTIONGROUP" REPLACE ALL pbit_objs WITH BITSET(pbit_objs, 4) FOR BITTEST(pBit_State,2) *// *\\ NODE_HAS_ROOTLINES (nur löschen - only deleting) WAIT WINDOW NOWAIT "Processing : NODE_HAS_ROOTLINES " REPLACE ALL pbit_objs WITH BITCLEAR(pbit_objs, 5) *// *\\ NODE_HAS_LINES WAIT WINDOW NOWAIT "Processing : NODE_HAS_LINES" REPLACE ALL pbit_objs WITH BITSET(pbit_objs, 6) FOR BITTEST(pBit_Visib,0) *// *\\ NODE_HAS_EXPANDER WAIT WINDOW NOWAIT "Processing : NODE_HAS_EXPANDER" REPLACE ALL pbit_objs WITH BITSET(pbit_objs, 7) FOR BITTEST(pBit_Visib,1) *// *\\ NODE_HAS_ICON WAIT WINDOW NOWAIT "Processing : NODE_HAS_ICON" REPLACE ALL pbit_objs WITH BITSET(pbit_objs, 8) FOR BITTEST(pBit_Visib,2) *// *\\ NODE_HAS_DYNAMIC_ICON (nur löschen - only deleting) WAIT WINDOW NOWAIT "Processing : NODE_HAS_DYNAMIC_ICON" REPLACE ALL pbit_objs WITH BITCLEAR(pbit_objs, 9) *// *\\ NODE_HAS_CHECKBOX WAIT WINDOW NOWAIT "Processing : NODE_HAS_CHECKBOX" REPLACE ALL pbit_objs WITH BITSET(pbit_objs, 10) FOR BITTEST(pBit_Visib,3) *// *\\ NODE_HAS_OPTIONBUTTON WAIT WINDOW NOWAIT "Processing : NODE_HAS_OPTIONBUTTON" REPLACE ALL pbit_objs WITH BITSET(pbit_objs, 11) FOR BITTEST(pBit_Visib,4) *// *\\ NODE_HAS_CAPTION WAIT WINDOW NOWAIT "Processing : NODE_HAS_CAPTION" REPLACE ALL pbit_objs WITH BITSET(pbit_objs, 12) FOR BITTEST(pBit_Visib,5) *// *\\ NODE_HAS_DYNAMIC_CAPTION (nur löschen - only deleting) WAIT WINDOW NOWAIT "Processing : NODE_HAS_DYNAMIC_CAPTION" REPLACE ALL pbit_objs WITH BITCLEAR(pbit_objs, 13) *// *\\ NODE_HAS_SUBTEXT WAIT WINDOW NOWAIT "Processing : NODE_HAS_SUBTEXT" REPLACE ALL pbit_objs WITH BITSET(pbit_objs, 14) FOR BITTEST(pBit_Visib,6) *// *\\ NODE_HAS_DYNAMIC_SUBTEXT (nur löschen - only deleting) WAIT WINDOW NOWAIT "Processing : NODE_HAS_DYNAMIC_SUBTEXT" REPLACE ALL pbit_objs WITH BITCLEAR(pbit_objs, 15) *// *\\ NODE_HAS_OVERLAY WAIT WINDOW NOWAIT "Processing : NODE_HAS_OVERLAY" REPLACE ALL pbit_objs WITH BITSET(pbit_objs, 16) FOR BITTEST(pBit_Visib,7) *// *\\ NODE_HAS_DYNAMIC_OVERLAY (nur löschen - only deleting) WAIT WINDOW NOWAIT "Processing : NODE_HAS_DYNAMIC_OVERLAY" REPLACE ALL pbit_objs WITH BITCLEAR(pbit_objs, 17) *// *\\ NODE_HAS_HELPICON WAIT WINDOW NOWAIT "Processing : NODE_HAS_HELPICON" REPLACE ALL pbit_objs WITH BITSET(pbit_objs, 18) FOR BITTEST(pBit_Visib,8) *// *\\ NODE_HAS_DYNAMIC_HELPICON (nur löschen - only deleting) WAIT WINDOW NOWAIT "Processing : NODE_HAS_DYNAMIC_HELPICON" REPLACE ALL pbit_objs WITH BITCLEAR(pbit_objs, 19) *// *\\ NODE_HAS_ERRORICON WAIT WINDOW NOWAIT "Processing : NODE_HAS_ERRORICON" REPLACE ALL pbit_objs WITH BITSET(pbit_objs, 20) FOR BITTEST(pBit_Visib,9) *// *\\ NODE_HAS_DYNAMIC_ERRORICON (nur löschen) WAIT WINDOW NOWAIT "Processing : NODE_HAS_DYNAMIC_ERRORICON" REPLACE ALL pbit_objs WITH BITCLEAR(pbit_objs, 21) *// *\\ NODE_HAS_BACKGROUND WAIT WINDOW NOWAIT "Processing : NODE_HAS_BACKGROUND" REPLACE ALL pbit_objs WITH BITSET(pbit_objs, 22) FOR BITTEST(pBit_Visib,10) *// *\\ NODE_HAS_DYNAMIC_BACKGROUND (nur löschen - only deleting) WAIT WINDOW NOWAIT "Processing : NODE_HAS_DYNAMIC_BACKGROUND" REPLACE ALL pbit_objs WITH BITCLEAR(pbit_objs, 23) *// *\\ NODE_HAS_EXTENSION (nur löschen - only deleting) WAIT WINDOW NOWAIT "Processing : NODE_HAS_EXTENSION" REPLACE ALL pbit_objs WITH BITCLEAR(pbit_objs, 24) *// *\\ NODE_HAS_DYNAMIC_EXTENSION (nur löschen - only deleting) WAIT WINDOW NOWAIT "Processing : NODE_HAS_DYNAMIC_EXTENSION" REPLACE ALL pbit_objs WITH BITCLEAR(pbit_objs, 25) *// *\\ NODE_HAS_USER (nur löschen) WAIT WINDOW NOWAIT "Processing : NODE_HAS_USER" REPLACE ALL pbit_objs WITH BITCLEAR(pbit_objs, 26) *// *\\ NODE_HAS_DYNAMIC_USER (nur löschen) WAIT WINDOW NOWAIT "Processing : NODE_HAS_DYNAMIC_USER" REPLACE ALL pbit_objs WITH BITCLEAR(pbit_objs, 27) *// *\\ (re)creating indices WAIT WINDOW NOWAIT "Deleting all CDX tags" DELETE TAG ALL WAIT WINDOW NOWAIT "Flushing dbf to disk" FLUSH force *// *\\ master indices WAIT WINDOW NOWAIT "Indexing on : PNODEID" INDEX ON PNODEID TAG "PNODEID" *// WAIT WINDOW NOWAIT "Indexing on : PPARENTID" INDEX ON PPARENTID TAG "PPARENTID" *// WAIT WINDOW NOWAIT "Indexing on : PCCAPTION" INDEX ON PCCAPTION TAG "PCCAPTION" *// WAIT WINDOW NOWAIT "Indexing on : PIORDER" INDEX ON PIORDER TAG "PIORDER" *// WAIT WINDOW NOWAIT "Indexing on : PIORDER" INDEX ON PIORDER TAG "PIORDERVIS" FOR PLVISIBLE *// WAIT WINDOW NOWAIT "Indexing on : PIINDENT" INDEX ON PIINDENT TAG "PIINDENT" *// WAIT WINDOW NOWAIT "Indexing on : PICHILDCNT" INDEX ON PICHILDCNT TAG "PICHILDCNT" *// WAIT WINDOW NOWAIT "Indexing on : PLVISIBLE" INDEX ON PLVISIBLE TAG "PLVISIBLE" *// WAIT WINDOW NOWAIT "Indexing on : PLEXPANDED" INDEX ON PLEXPANDED TAG "PLEXPANDED" *// WAIT WINDOW NOWAIT "Indexing on : PLLASTITEM" INDEX ON PLLASTITEM TAG "PLLASTITEM" *// WAIT WINDOW NOWAIT "Indexing on : PNMARK" INDEX ON PNMARK TAG "PNMARK" *// WAIT WINDOW NOWAIT "Indexing on : PBIT_OBJS" INDEX ON PBIT_OBJS TAG "PBIT_OBJS" *// *\\ -------------------------------------------------- *\\ lookup indices (rushmore tags) WAIT WINDOW NOWAIT "Indexing on : DELETED()" INDEX ON DELETED() TAG "_DEL" BINARY *\\ SET ESCAPE ON *// LOCAL lnLoop, lcIdxText FOR m.lnLoop = 0 TO 27 lcIdxText = "BITTEST(pbit_objs, " + ; TRANSFORM(m.lnLoop) + ") TAG 'PBITOBJS" + ; PADL(m.lnLoop,2,'0')+"' BINARY" WAIT WINDOW NOWAIT m.lcIdxText INDEX ON &lcIdxText WAIT clear NEXT FOR m.lnLoop = 0 TO 7 lcIdxText = "BITTEST(pbit_visib, " + ; TRANSFORM(m.lnLoop) + ") TAG 'PBITVISI" + ; PADL(m.lnLoop,2,'0')+"' BINARY" WAIT WINDOW NOWAIT m.lcIdxText INDEX ON &lcIdxText WAIT clear NEXT FOR m.lnLoop = 0 TO 7 lcIdxText = "BITTEST(pbit_state, " + ; TRANSFORM(m.lnLoop) + ") TAG 'PBITSTAT" + ; PADL(m.lnLoop,2,'0')+"' BINARY" WAIT WINDOW NOWAIT m.lcIdxText INDEX ON &lcIdxText WAIT clear NEXT WAIT WINDOW NOWAIT "Flushing DBF to disk" FLUSH force RETURN
*\\ Just to remember: this is how we can compute the different node layouts *\\ work in progress: still has to be refined!!! SELECT DISTINCT pbit_objs, CAST(NULL AS Integer) FROM DBF(ALIAS()) INTO ARRAY atest ACTIVATE SCREEN clear FOR lnLoop = 1 TO ALEN(atest,1) ? atest[m.lnloop,1],atest[m.lnloop,2] NEXT WAIT clear
Note:
My reason for introducing a field called “pBit_Objs” in the driving table and transferring/merging the values of the “pBit_Visib” and “pBit_State” fields into it was to get a list of possible value-combinations (my so-called “layouts”) each representing a special kind of node appearance. This is how my latest VFP TreeView version works: These nodes are prefabricated and stored in classes, then. Thus, no time-consuming code-based re-constructions are necessary during grid refresh-cycles at runtime, coz all different/distinct node objects are loaded once at grid-startup and are referenced then using the DynamicCurrentControl property of the grid’s Column object. Only the most variant entries (like node captions and maybe sub-texts and icons) still are flexibly stored in the underlying driver table. This makes a lot of sense and speeds up VFP’s grid-based TreeView performance substantially!
The only drawback of that approach is that we have to construct our TreeView nodes in advance. Luckily, for most of our scenarios this is no big deal. I’m just working on some kind of node-designer tool, that can be used within VFP’s IDE or during runtime to construct the more ‘outlandish’ looking node types :-) But this will be the topic of another (future) part of this never-ending thread :-)
Very cool, Burkhard - although it seems a little slow.
ReplyDeleteAny idea when you're posting the code with the source?
THX, Andrew. Yep, that version gets slower when dealing with large driving tables (I tested this with over 30.000 recs) but only when it comes to collapse/expand actions on large chunks. But it gets also slower the more Grid-lines have to be displayed/refreshed. This is the #1 thing I'm working on. I hope I'll have a stable beta end of this year.
ReplyDeleteStay tuned :-)
Quite cool! I understand this control is Grid based. What about using this to create a drill-down enabled grid?
ReplyDeleteNow that would be really something!
Keep up the excellent work.
I'd love to follow you on my Google Reader (you have some really cool content), however i wasn't able to find an RSS or Atom feed on your blog.
ReplyDeleteWill you please make one (or activate-it if bogspot gives this option?)
Thank you very much!
I didn't see a way to post a comment on part V, so I'm posting here. I really appreciate what you've done. BUT...
ReplyDeletePLEASE PLEASE PLEASE give the source code of the "final" version. I would pay for it...
it seems download link broken. please give me another download link sir. thank you
ReplyDeleteeddy
@Eddy
ReplyDeleteI hope this works aagin now :-)
EXISTING LINK OF DOWNLOAD NOT WORKING PLS PROVIDE NEW LINK.
ReplyDeleteThe download link doesn't seem to work.
ReplyDeleteCould you please see to it that it can work.
I would love to play around with this treeview.
Hi Burkhard, the download links are again broken. Any chance you have these mapped somewhere?
ReplyDelete