A Native TreeView (Part IV-b)

Version: 2.00.00 - Last Update: Sunday the 01th, May 2011 - 10:25:00 (I added a more obvious download link:-)

Previous ChapterComplex Controls Home (TOC)Next Chapter


FoxPro Rocks! This is all about how to create complex controls using native VFP only!

Intro

Start download of RightClickMenu_B08.zip here!

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!

Click to download gridtree_alpha_01.zip

 

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 VFP TreeView grid with 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 >>>

 

Start download of gridtree_alpha_01.zip here!

<< 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 :-)


Previous ChapterComplex Controls Home (TOC)Next Chapter

10 comments:

  1. Very cool, Burkhard - although it seems a little slow.

    Any idea when you're posting the code with the source?

    ReplyDelete
  2. 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.
    Stay tuned :-)

    ReplyDelete
  3. Quite cool! I understand this control is Grid based. What about using this to create a drill-down enabled grid?

    Now that would be really something!

    Keep up the excellent work.

    ReplyDelete
  4. 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.

    Will you please make one (or activate-it if bogspot gives this option?)

    Thank you very much!

    ReplyDelete
  5. 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...

    PLEASE PLEASE PLEASE give the source code of the "final" version. I would pay for it...

    ReplyDelete
  6. it seems download link broken. please give me another download link sir. thank you

    eddy

    ReplyDelete
  7. @Eddy

    I hope this works aagin now :-)

    ReplyDelete
  8. EXISTING LINK OF DOWNLOAD NOT WORKING PLS PROVIDE NEW LINK.

    ReplyDelete
  9. The download link doesn't seem to work.
    Could you please see to it that it can work.
    I would love to play around with this treeview.

    ReplyDelete
  10. Hi Burkhard, the download links are again broken. Any chance you have these mapped somewhere?

    ReplyDelete