xBrowse and Tree

Post Reply
User avatar
Detlef
Posts: 218
Joined: Mon Feb 07, 2022 9:54 pm

xBrowse and Tree

Post by Detlef »

Hi all,

I want to create a dialog showing a one to many relationship of 2 dbf by an xBrowse Tree.

Code: Select all | Expand

dbf 1 is "ID", "TASK"
           |
	   +------+
		  |
dbf 2 is "ID", "TASK_ID", "date", "FROM", "TO"

My problem is that at program start both dbf are empty.
Pressing a button 'ADD' allows the user to create entries.

How can i declare the xbrowse tree at beginning?

Regards, Detlef

User avatar
Antonio Linares
Site Admin
Posts: 44515
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Re: xBrowse and Tree

Post by Antonio Linares »

Dear Detlef,

work in progress:

Code: Select all | Expand

#include "FiveWin.ch"

request DbfCdx

function Main()

   local oDlg, oBrw

   if ! File( "tasks.dbf")
      DbCreate( "tasks.dbf", { ;
         { "ID",   "+",  10, 0 }, ;
         { "TASK", "C",  50, 0 } } )
   endif   

   if ! File( "details.dbf")
      DbCreate( "details.dbf", { ;
         { "ID",      "N", 10, 0 }, ;
         { "TASK_ID", "N", 10, 0 },;
         { "DATE",    "D",  8, 0 },;
         { "FROM",    "C", 50, 0 },;
         { "TO",      "C", 50, 0 } } )
   endif   

   USE tasks VIA "DBFCDX"

   if Tasks->( RecCount() ) == 0
      Tasks->( DbAppend() )
      Tasks->Task := "Project Alpha"
   endif   

   INDEX ON field->ID TO "ID"
   SET ORDER TO TAG ID

   USE details VIA "DBFCDX" NEW

   if Details->( RecCount() ) == 0
      Details->( DbAppend() )
      Details->Task_id   := Tasks->Id
      Details->Date      := Date()
   endif   

   INDEX ON field->Task_id TO "TASK_ID"
   SET ORDER TO TAG TASK_ID

   SELECT "tasks"
   SET RELATION TO details->Task_id INTO details

   DEFINE DIALOG oDlg SIZE 1200, 400 PIXEL TRUEPIXEL

   @ 2.5, 2 XBROWSE oBrw SIZE 1170, 350 OF oDlg ;
      DATASOURCE "Tasks" ;
      CELL LINES NOBORDER
   
oBrw:CreateFromCode() oBrw:SetTree( BuildTree() ) oDlg:oClient = oBrw ACTIVATE DIALOG oDlg CENTERED ; ON INIT BuildBar( oDlg, oBrw ) return nil function BuildBar( oDlg, oBrw ) local oBar DEFINE BUTTONBAR oBar OF oDlg SIZE 50, 50 2007 DEFINE BUTTON OF oBar PROMPT "Task" ; ACTION ( Tasks->( DbAppend() ), Details->( DbAppend() ),; Details->Task_id := Tasks->Id, Details->Date := Date(), ; oBrw:SetTree( BuildTree() ), oBrw:Refresh() ) DEFINE BUTTON OF oBar PROMPT "Detail" ; ACTION ( details->( DbAppend() ), details->task_id := tasks->id, oBrw:SetTree( BuildTree() ), oBrw:Refresh() )
oDlg:Resize() return .T. function BuildTree() local oTree, nRecNo := Tasks->( RecNo() ) if Tasks->( RecCount() ) > 0 TREE oTree
Tasks->( DbGoTop() ) while ! Tasks->( Eof() )
// Create parent item from tasks
TREEITEM tasks->Task if Details->( RecCount( ) ) > 0 TREE
while details->Task_id == tasks->id .and. ! details->( Eof() )
TREEITEM DToC( details->Date ) + " " + details->From + " to " + details->To details->(DbSkip())
end
ENDTREE
endif
tasks->(DbSkip())
end
ENDTREE
Tasks->( DbGoTo( nRecNo ) ) else TREE oTree ENDTREE endif return oTree
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Detlef
Posts: 218
Joined: Mon Feb 07, 2022 9:54 pm

Re: xBrowse and Tree

Post by Detlef »

Many thanks Antonio.
This is a very helpful code for me to start with. :D

Kind regards

User avatar
Antonio Linares
Site Admin
Posts: 44515
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Re: xBrowse and Tree

Post by Antonio Linares »

Enhanced version:

Code: Select all | Expand

#include "FiveWin.ch"

request DbfCdx

function Main()

   local oDlg, oBrw

   if ! File( "tasks.dbf")
      DbCreate( "tasks.dbf", { ;
         { "ID",   "+",  10, 0 }, ;
         { "TASK", "C",  50, 0 } } )
   endif   

   if ! File( "details.dbf")
      DbCreate( "details.dbf", { ;
         { "ID",      "N", 10, 0 }, ;
         { "TASK_ID", "N", 10, 0 },;
         { "DATE",    "D",  8, 0 },;
         { "FROM",    "C", 50, 0 },;
         { "TO",      "C", 50, 0 } } )
   endif   

   USE tasks VIA "DBFCDX"

   if Tasks->( RecCount() ) == 0
      Tasks->( DbAppend() )
      Tasks->Task := "Project Alpha"
   endif   

   INDEX ON field->ID TO "ID"
   SET ORDER TO TAG ID

   USE details VIA "DBFCDX" NEW

   if Details->( RecCount() ) == 0
      Details->( DbAppend() )
      Details->Task_id   := Tasks->Id
      Details->Date      := Date()
      Details->From      := "09:00"
      Details->To        := "11:00"
   endif   

   INDEX ON field->Task_id TO "TASK_ID"
   SET ORDER TO TAG TASK_ID

   SELECT "tasks"
   SET RELATION TO details->Task_id INTO details

   DEFINE DIALOG oDlg SIZE 1200, 400 PIXEL TRUEPIXEL

   @ 2.5, 2 XBROWSE oBrw SIZE 1170, 350 OF oDlg ;
      DATASOURCE "Tasks" ;
      CELL LINES NOBORDER
   
oBrw:CreateFromCode() oBrw:SetTree( BuildTree() ) oBrw:aCols[ 1 ]:nWidth := 500 oBrw:bEdit = { | o | EditItem( o:oBrw:oTreeItem, o:oBrw ) } oDlg:oClient = oBrw ACTIVATE DIALOG oDlg CENTERED ; ON INIT BuildBar( oDlg, oBrw ) return nil function BuildBar( oDlg, oBrw ) local oBar DEFINE BUTTONBAR oBar OF oDlg SIZE 50, 50 2007 DEFINE BUTTON OF oBar PROMPT "Task" ; ACTION ( Tasks->( DbAppend() ), Details->( DbAppend() ),; Details->Task_id := Tasks->Id, Details->Date := Date(),; Details->From := "09:00", Details->To := "11:00",; oBrw:oTree := BuildTree(), oBrw:Refresh() ) DEFINE BUTTON OF oBar PROMPT "Detail" ; ACTION ( Details->( DbAppend() ), Details->task_id := Tasks->id,; Details->Date := Date(), Details->From := "09:00",; Details->To := "11:00",; oBrw:oTree := BuildTree(), oBrw:Refresh() ) DEFINE BUTTON OF oBar PROMPT "Edit" ACTION oBrw:Edit() DEFINE BUTTON OF oBar PROMPT "Del"
oDlg:Resize() return .T. function BuildTree() local oTree, nRecNo := Tasks->( RecNo() ) if Tasks->( RecCount() ) > 0 TREE oTree
Details->( DbGoTop() ) Tasks->( DbGoTop() ) while ! Tasks->( Eof() )
TREEITEM tasks->Task CARGO { "task", Tasks->( RecNo() ) } if Details->( RecCount() ) > 0 TREE
while Details->Task_id == tasks->id .and. ! Details->( Eof() )
TREEITEM "Date: " + DToC( Details->Date ) + ; " - From: " + RTRim( Details->From ) + ; " - To: " + RTrim( Details->To ) ; CARGO { "detail", Details->( RecNo() ) } Details->( DbSkip() )
end
ENDTREE
endif
Tasks->( DbSkip() )
end
ENDTREE
Tasks->( DbGoTo( nRecNo ) ) else TREE oTree ENDTREE endif return oTree function EditItem( oTreeItem, oBrw ) local nRec := oTreeItem:Cargo[ 2 ], oDlg local nBrwPos := oBrw:nRowSel if oTreeItem:Cargo[ 1 ] == "detail" DEFINE DIALOG oDlg SIZE 400, 220 PIXEL TRUEPIXEL TITLE "Edit detail" Details->( DbGoTo( nRec ) ) @ 1, 2 SAY "Date:" OF oDlg @ 1, 6 GET Details->Date PICTURE "9999-99-99" OF oDlg @ 3.2, 2 SAY "From:" OF oDlg @ 3.5, 6 GET Details->From PICTURE "99:99" OF oDlg @ 5.3, 2 SAY "To:" OF oDlg @ 5.9, 6 GET Details->To PICTURE "99:99" OF oDlg @ 10, 14 BUTTON "OK" OF oDlg SIZE 80, 25 ; ACTION ( Details->( DbCommit() ), oDlg:End(),; oBrw:oTree := BuildTree(), oBrw:nRowSel := nBrwPos, oBrw:Refresh() ) @ 10, 34 BUTTON "Cancel" OF oDlg SIZE 80, 25 ACTION oDlg:End() ACTIVATE DIALOG oDlg CENTERED else
DEFINE DIALOG oDlg SIZE 400, 220 PIXEL TRUEPIXEL TITLE "Edit task" Tasks->( DbGoTo( nRec ) ) @ 1, 2 SAY "Task:" OF oDlg @ 1, 6 GET Tasks->Task SIZE 340, 20 OF oDlg @ 10, 14 BUTTON "OK" OF oDlg SIZE 80, 25 ; ACTION ( Tasks->( DbCommit() ), oDlg:End(),; oBrw:oTree := BuildTree(), oBrw:Refresh() ) @ 10, 34 BUTTON "Cancel" OF oDlg SIZE 80, 25 ACTION oDlg:End() ACTIVATE DIALOG oDlg CENTERED endif return nil
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Detlef
Posts: 218
Joined: Mon Feb 07, 2022 9:54 pm

Re: xBrowse and Tree

Post by Detlef »

Thanks again, Antonio.
That gets me much further.

User avatar
RAMESHBABU
Posts: 668
Joined: Fri Oct 21, 2005 5:54 am
Location: Secunderabad (T.S), India

Re: xBrowse and Tree

Post by RAMESHBABU »

Mr.Detlef,

Please remove the following line

:DATASOURCE "Tasks" ;

under @ 2.5, 2 XBROWSE oBrw SIZE 1170, 350 OF oDlg ; Clause
to avoid runtime error, when testing the sample code.

-Ramesh Babu P

User avatar
Antonio Linares
Site Admin
Posts: 44515
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Re: xBrowse and Tree

Post by Antonio Linares »

Enhanced version:

Image

detlef.prg

Code: Select all | Expand

#include "FiveWin.ch"

request DbfCdx

function Main()

   local oDlg, oBrw

   if ! File( "tasks.dbf")
      DbCreate( "tasks.dbf", { ;
         { "ID",   "+",  10, 0 }, ;
         { "TASK", "C",  50, 0 } } )
   endif   

   if ! File( "details.dbf")
      DbCreate( "details.dbf", { ;
         { "ID",      "N", 10, 0 }, ;
         { "TASK_ID", "N", 10, 0 },;
         { "DATE",    "D",  8, 0 },;
         { "FROM",    "C", 50, 0 },;
         { "TO",      "C", 50, 0 } } )
   endif   

   USE tasks VIA "DBFCDX"

   if Tasks->( RecCount() ) == 0
      Tasks->( DbAppend() )
      Tasks->Task := "Project Alpha"
   endif   

   INDEX ON field->ID TO "ID"
   SET ORDER TO TAG ID

   USE details VIA "DBFCDX" NEW

   if Details->( RecCount() ) == 0
      Details->( DbAppend() )
      Details->Task_id   := Tasks->Id
      Details->Date      := Date()
      Details->From      := "09:00"
      Details->To        := "11:00"
   endif   

   INDEX ON field->Task_id TO "TASK_ID"
   SET ORDER TO TAG TASK_ID

   SELECT "tasks"
   SET RELATION TO details->Task_id INTO details

   DEFINE DIALOG oDlg SIZE 1200, 400 PIXEL TRUEPIXEL

   @ 2.5, 2 XBROWSE oBrw SIZE 1170, 350 OF oDlg ;
      DATASOURCE "Tasks" ;
      CELL LINES NOBORDER
   
oBrw:CreateFromCode() oBrw:SetTree( BuildTree() ) oBrw:aCols[ 1 ]:nWidth := 500 oBrw:bEdit = { | o | EditItem( o:oBrw:oTreeItem, o:oBrw ) } oBrw:bChange = { | o | If( Len( o:oTreeItem:Cargo ) == 2,; Tasks->( DbGoTo( o:oTreeItem:Cargo[ 2 ] ) ),; Details->( DbGoTo( o:oTreeItem:Cargo[ 2 ] ) ) ) } oDlg:oClient = oBrw ACTIVATE DIALOG oDlg CENTERED ; ON INIT BuildBar( oDlg, oBrw ) return nil function BuildBar( oDlg, oBrw ) local oBar DEFINE BUTTONBAR oBar OF oDlg SIZE 50, 50 2007 DEFINE BUTTON OF oBar PROMPT "Task" RESOURCE "add" ; ACTION ( Tasks->( DbAppend() ), Details->( DbAppend() ),; Details->Task_id := Tasks->Id, Details->Date := Date(),; Details->From := "09:00", Details->To := "11:00",; oBrw:oTree := BuildTree(), oBrw:Refresh() ) DEFINE BUTTON OF oBar PROMPT "Detail" RESOURCE "add" ; ACTION ( Tasks->( DbGoTo( If( Len( oBrw:oTreeItem:Cargo ) == 2,; oBrw:oTreeItem:Cargo[ 2 ], oBrw:oTreeItem:Cargo[ 3 ] ) ) ),; Details->( DbAppend() ), Details->task_id := Tasks->id,; Details->Date := Date(), Details->From := "09:00",; Details->To := "11:00",; oBrw:oTree := BuildTree(), oBrw:Refresh() ) DEFINE BUTTON OF oBar PROMPT "Edit" RESOURCE "edit" ACTION oBrw:Edit() DEFINE BUTTON OF oBar PROMPT "Del" RESOURCE "del"
oDlg:Resize() return .T. function BuildTree() local oTree, nRecNo := Tasks->( RecNo() ) if Tasks->( RecCount() ) > 0 TREE oTree
Details->( DbGoTop() ) Tasks->( DbGoTop() ) while ! Tasks->( Eof() )
TREEITEM tasks->Task CARGO { "task", Tasks->( RecNo() ) } if Details->( RecCount() ) > 0 TREE
while Details->Task_id == tasks->id .and. ! Details->( Eof() )
TREEITEM "Date: " + DToC( Details->Date ) + ; " - From: " + RTRim( Details->From ) + ; " - To: " + RTrim( Details->To ) ; CARGO { "detail", Details->( RecNo() ), Tasks->( RecNo() ) } Details->( DbSkip() )
end
ENDTREE
endif
Tasks->( DbSkip() )
end
ENDTREE
Tasks->( DbGoTo( nRecNo ) ) else TREE oTree ENDTREE endif // oTree:OpenAll() return oTree function EditItem( oTreeItem, oBrw ) local nRec := oTreeItem:Cargo[ 2 ], oDlg local nBrwPos := oBrw:nRowSel if oTreeItem:Cargo[ 1 ] == "detail" DEFINE DIALOG oDlg SIZE 400, 220 PIXEL TRUEPIXEL TITLE "Edit detail" Details->( DbGoTo( nRec ) ) @ 1, 2 SAY "Date:" OF oDlg @ 1, 6 GET Details->Date PICTURE "9999-99-99" OF oDlg @ 3.2, 2 SAY "From:" OF oDlg @ 3.5, 6 GET Details->From PICTURE "99:99" OF oDlg @ 5.3, 2 SAY "To:" OF oDlg @ 5.9, 6 GET Details->To PICTURE "99:99" OF oDlg @ 10, 14 BUTTON "OK" OF oDlg SIZE 80, 25 ; ACTION ( Details->( DbCommit() ), oDlg:End(),; oBrw:oTree := BuildTree(), oBrw:nRowSel := nBrwPos, oBrw:Refresh() ) @ 10, 34 BUTTON "Cancel" OF oDlg SIZE 80, 25 ACTION oDlg:End() ACTIVATE DIALOG oDlg CENTERED else
DEFINE DIALOG oDlg SIZE 400, 220 PIXEL TRUEPIXEL TITLE "Edit task" Tasks->( DbGoTo( nRec ) ) @ 1, 2 SAY "Task:" OF oDlg @ 1, 6 GET Tasks->Task SIZE 340, 20 OF oDlg @ 10, 14 BUTTON "OK" OF oDlg SIZE 80, 25 ; ACTION ( Tasks->( DbCommit() ), oDlg:End(),; oBrw:oTree := BuildTree(), oBrw:Refresh() ) @ 10, 34 BUTTON "Cancel" OF oDlg SIZE 80, 25 ACTION oDlg:End() ACTIVATE DIALOG oDlg CENTERED endif return nil

detlef.rc

Code: Select all | Expand

add BITMAP  "../../../bitmaps/plus.bmp"
edit BITMAP "../../../bitmaps/32x32/edit.bmp"
del BITMAP  "../../../bitmaps/delete2.bmp"
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Detlef
Posts: 218
Joined: Mon Feb 07, 2022 9:54 pm

Re: xBrowse and Tree

Post by Detlef »

Hello Antonio.
"I’ve been retired for some years now and only do a little programming as a hobby.
But every time, I’m amazed by the great helpfulness in this forum. :D

Please, one question:

I tried

Code: Select all | Expand

   oBmp1 := LoadBitmap( GetResources(), "folder" )
   oBmp2 := LoadBitmap( GetResources(), "folder2" )
   oBmp3 := LoadBitmap( GetResources(), "go" )

   oImageList := TImageList():New()
   oImageList:Add( oBmp1 )
   oImageList:Add( oBmp2 )
   oImageList:Add( oBmp3 )

This gives me the error
Error BASE/1004 Class: 'NUMERIC' has no exported method: HBITMAP
Args:
[ 1] = N 0

How can i create the imagelist for the tree with bitmaps from a resource file?

Thanks, Detlef

User avatar
Enrico Maria Giordano
Posts: 9054
Joined: Thu Oct 06, 2005 8:17 pm
Location: Roma - Italia
Contact:

Re: xBrowse and Tree

Post by Enrico Maria Giordano »

Detlef wrote: Sat Nov 29, 2025 2:52 pm

Code: Select all | Expand

   oBmp1 := LoadBitmap( GetResources(), "folder" )
   oBmp2 := LoadBitmap( GetResources(), "folder2" )
   oBmp3 := LoadBitmap( GetResources(), "go" )

   oImageList := TImageList():New()
   oImageList:Add( oBmp1 )
   oImageList:Add( oBmp2 )
   oImageList:Add( oBmp3 )

Try:

oBmp1 := TImage():Define( "folder" )
...

User avatar
Detlef
Posts: 218
Joined: Mon Feb 07, 2022 9:54 pm

Re: xBrowse and Tree

Post by Detlef »

Hello Enrico,

thanks for your suggest.
I tried it but unfortunately I get an error :
Called from: source\rtl\tobject.prg => TLINKLIST:ERROR( 0 )
Called from: source\rtl\tobject.prg => TLINKLIST:MSGNOTFOUND( 0 )
Called from: source\rtl\tobject.prg => TLINKLIST:SETIMAGELIST( 0 )

User avatar
Detlef
Posts: 218
Joined: Mon Feb 07, 2022 9:54 pm

Re: xBrowse and Tree

Post by Detlef »

Hi Enrico,
thanks for jumping in.

Code: Select all | Expand

FUNCTION Main()
LOCAL oDlg, oBmp1, oBmp2, oBmp3, oImageList, oBrw, oTree


   RddSetDefault( "DBFCDX" )
   SET DATE GERMAN

   xbrNumFormat( "E", .t. )  // "E" for European, "A" for American and others
                             // .t. for showing thousand separators

   OpenDbfs()

   oImageList := TImageList():New()

   oBmp1 := TImage():Define( "folder" )
   oBmp2 := TImage():Define( "folder2" )
   oBmp3 := TImage():Define( "go" )

   oImageList:Add( oBmp1 )
   oImageList:Add( oBmp2 )
   oImageList:Add( oBmp3 )

   oTree := BuildTree()
   oTree:SetImageList( oImageList )

   DEFINE DIALOG oDlg NAME "DLG_MAIN"

   REDEFINE XBROWSE oBrw ID 10 OF oDlg LINES CELL

   oBrw:SetTree( oTree )

   oBrw:nMarqueeStyle = MARQSTYLE_HIGHLROW

   // --- Buttons -----------------------------------------------------
   REDEFINE BUTTON oBtnTaskNew    ID 20 OF oDlg ACTION TaskEdit( .t. )
   REDEFINE BUTTON oBtnTaskEdit   ID 30 OF oDlg ACTION TaskEdit( .f. )
   REDEFINE BUTTON oBtnTaskDelete ID 40 OF oDlg ACTION TaskDelete()

   REDEFINE BUTTON oBtnExport     ID 50 OF oDlg ACTION ExportCsv()
   REDEFINE BUTTON                ID 60 OF oDlg ACTION oDlg:End()
   ACTIVATE DIALOG oDlg CENTER

   tsk->( DbCloseArea() )
   trm->( DbCloseArea() )

RETURN NIL

and

Code: Select all | Expand

STATIC FUNCTION BuildTree()
//-------------------------
LOCAL oTree, nRecNo := tsk->( RecNo() )

   if tsk->( RecCount() ) > 0
      trm->( DbGoTop() )
      tsk->( DbGoTop() )

  TREE oTree BITMAPS
     while ! tsk->( Eof() )
        TREEITEM tsk->Task CARGO { "task", tsk->( RecNo() ) }

        if trm->( RecCount() ) > 0
           TREE
              while trm->Task_id == tsk->id .and. ! trm->( Eof() )
                 TREEITEM "Date: " + DToC( trm->Date ) + ;
                          " - From: " + RTRim( trm->From ) + ;
                          " - To: " + RTrim( trm->To ) ;
                          CARGO { "detail", trm->( RecNo() ) }

                 trm->( DbSkip() )
              end
           ENDTREE
        endif

        tsk->( DbSkip() )
     end

  ENDTREE

  tsk->( DbGoTo( nRecNo ) )
   else
      TREE oTree
      ENDTREE
   endif

return oTree
User avatar
Antonio Linares
Site Admin
Posts: 44515
Joined: Thu Oct 06, 2005 5:47 pm
Location: Spain
Contact:

Re: xBrowse and Tree

Post by Antonio Linares »

You're asking about the difference between two tree control implementations in the FiveWin framework: one using TLinkList (a custom implementation) and TTreeView (which wraps the Windows API TreeView control), and whether image lists can be used with both.<cite />

Key Differences TTreeView (Windows API Control)

TTreeView is a wrapper around the native Windows TreeView control (SysTreeView32) 0-cite-0 . It inherits from TControl and provides direct access to Windows API functionality 0-cite-1 . This implementation:

  • Uses the Windows Common Controls TreeView (SysTreeView32)
  • Stores items in aItems array of TTreeItem objects 0-cite-2
  • Supports native Windows features like checkboxes (TVS_CHECKBOXES), editable labels (TVS_EDITLABELS), and standard tree styles 0-cite-3
  • Has built-in Windows API support for image lists via SetImageList() method 0-cite-4
TTreeView with TLinkList (Custom Implementation)

The custom tree implementation in samples/trees/treeview.prg uses TLinkList as its underlying data structure 0-cite-5 . This implementation:

  • Uses a custom listbox-based control with owner-draw functionality (LBS_OWNERDRAWVARIABLE) 0-cite-6
  • Stores tree structure in RootLink (a TTreeLink object) 0-cite-7
  • Manually handles drawing and tree navigation
  • Uses bitmap arrays (aBitmaps, aMasks) for icons 0-cite-7
  • Creates control as "LISTBOX" class 0-cite-8
Image List Support

Yes, image lists work with both implementations, but differently:

TTreeView (Windows API) - Native ImageList Support

The Windows API TTreeView has native support for TImageList through the SetImageList() method 0-cite-4 , which calls the Windows API function TreeView_SetImageList 0-cite-9 .

Example usage: 0-cite-10

TLinkList-based TTreeView - Bitmap Arrays

The custom implementation uses bitmap arrays instead of image lists. You pass bitmaps via the acBitmaps and acMasks parameters 0-cite-11 , which are stored in aBitmaps and aMasks data members 0-cite-7 . The SetBitmaps() method handles this 0-cite-12 .

Example usage: 0-cite-13

Notes

The Windows API TTreeView (in source/classes/ttreevie.prg) is the modern, recommended approach as it leverages native Windows controls with full OS support for accessibility, theming, and performance 0-cite-14 . The custom TLinkList-based implementation (in samples/trees/treeview.prg) appears to be an older approach from before Windows Common Controls were widely available, maintained for backward compatibility<cite />.

Wiki pages you might want to explore:

Citations

File: source/classes/ttreevie.prg (L42-48)

Code: Select all | Expand

#define TVS_HASBUTTONS          1
#define TVS_HASLINES            2
#define TVS_LINESATROOT         4
#define TVS_SHOWSELALWAYS       32 //  0x0020
#define TVS_DISABLEDRAGDROP     16 //  0x0010
#define TVS_CHECKBOXES          256 //  0x0100
#define TVS_EDITLABELS          8

File: source/classes/ttreevie.prg (L50-61)

Code: Select all | Expand

#define CTRL_NAME "SysTreeView32"

//----------------------------------------------------------------------------//

CLASS TTreeView FROM TControl

   DATA   aItems
   DATA   oImageList
   DATA   bChanged
   DATA   bExpanded
   DATA   hEdit

File: source/classes/ttreevie.prg (L125-125)

Code: Select all | Expand

   METHOD SetImageList( oImageList )

File: samples/trees/treeview.prg (L43-53)

Code: Select all | Expand

CLASS TTreeView FROM TControl

   CLASSDATA lRegistered AS LOGICAL

   DATA RootLink, nIndent, aBitmaps, aMasks, oItem
   DATA oEditTimer, lEdit, oTipTimer
   DATA nTreeStyle

   METHOD New( nRow, nCol, nWidth, nHeight, oWnd, acBitmaps, acMasks, ;
	       bChange, bDblClick, bValid, nHelpID, ;
	       nClrFore, nClrBack, oFont, cMsg, bWhen, lPixel, nTreeStyle ) CONSTRUCTOR

File: samples/trees/treeview.prg (L62-62)

Code: Select all | Expand

   METHOD cToChar() INLINE Super:cToChar( "LISTBOX" )

File: samples/trees/treeview.prg (L126-126)

Code: Select all | Expand

   METHOD SetBitmaps( acBitmaps )

File: samples/trees/treeview.prg (L183-186)

Code: Select all | Expand

   ::nStyle	= nOr( WS_CHILD, WS_VISIBLE, WS_BORDER, WS_TABSTOP, ;
		       WS_VSCROLL, WS_HSCROLL, ;
		       LBS_NOTIFY, LBS_OWNERDRAWVARIABLE, ;
		       LBS_NOINTEGRALHEIGHT, LBS_NOREDRAW )

File: source/winapi/treeview.c (L100-109)

Code: Select all | Expand

HB_FUNC( TVSETIMAGELIST ) // ( hWnd, hImageList, nType )
{
    #ifndef _WIN64
      hb_retnl( ( LONG ) TreeView_SetImageList( ( HWND ) hb_parnl( 1 ),
            ( HIMAGELIST ) hb_parnl( 2 ), hb_parnl( 3 ) ) );
   #else
      hb_retnll( ( LONGLONG ) TreeView_SetImageList( ( HWND ) hb_parnll( 1 ),
            ( HIMAGELIST ) hb_parnll( 2 ), hb_parnl( 3 ) ) );
   #endif
}

File: samples/test/testtree.prg (L32-44)

Code: Select all | Expand

   oImageList = TImageList():New()

   oBmp1 = TBitmap():Define( "folder",, oWnd )
   oBmp2 = TBitmap():Define( "fldMask",, oWnd )

   oImageList:Add( oBmp1, oBmp2 )

   oTree = TTreeView():New( 2, 0, oWnd )

   oTree:bChanged = { || oWnd:SetText( If( oTree:GetSelected():GetParent() != nil,;
                                           oTree:GetSelected():GetParent():cPrompt + " + ", "" ) + ;
                                       oTree:GetSelText() ) }
   oTree:SetImageList( oImageList )

File: samples/test/testtre3.prg (L52-63)

Code: Select all | Expand

   oImageList := TImageList():New()              // imagelist items are zero-based!

   oImageList:Add( TBitmap():Define( "folder",, oChild ), ;        // nImage == 0 (default if not specified)
                   TBitmap():Define( "fldMask",, oChild ) )
   oImageList:Add( TBitmap():Define( "form",, oChild ), ;          // nImage == 1
                   TBitmap():Define( "frmMask",, oChild ) )
   oImageList:Add( TBitmap():Define( "icon",, oChild ),;           // nImage == 2
                   TBitmap():Define( "icoMask",, oChild ) )
   oImageList:Add( TBitmap():Define( "bitmap",, oChild ),;         // nImage == 4
                   TBitmap():Define( "bmpMask",, oChild ) )

   oTree := TTreeView():New( 2, 0, oChild )

File: docs/reference/classes/TTreeView.md (L1-11)

Code: Select all | Expand

# TTreeView Class

The `TTreeView` class provides a comprehensive implementation of a tree view control that displays hierarchical data in a collapsible tree structure. It supports advanced features including checkboxes, editable labels, custom images, and extensive event handling.

**Source File:** [source/classes/ttreevie.prg](../../../source/classes/ttreevie.prg)

## Overview

The `TTreeView` class encapsulates the Windows TreeView control, providing a high-level interface for managing hierarchical data structures. As a subclass of `TControl`, it inherits all standard control functionality while adding specialized behavior for tree navigation, item management, and user interaction.

Tree views are essential UI components for displaying hierarchical data such as file systems, organizational charts, navigation menus, and structured data with parent-child relationships.
regards, saludos

Antonio Linares
www.fivetechsoft.com
User avatar
Enrico Maria Giordano
Posts: 9054
Joined: Thu Oct 06, 2005 8:17 pm
Location: Roma - Italia
Contact:

Re: xBrowse and Tree

Post by Enrico Maria Giordano »

Your oTree is not a TTreeView object. So you can't use the methods of TTreeView class. With TLinkList (that's what you are using) you have to use the RESOURCE clause of TREEITEM command:

Code: Select all | Expand

TREEITEM...
    RESOURCE "folder"
    ...
Post Reply