iId iParentId iSortOrder 1 0 1 2 1 1 3 1 2 4 1 3 5 2 1 6 2 2 7 2 3 8 0 2 The values from iSortOrder column are written when a node is added first time. The code for moving a node up (under the same parent) follows: <pre> IF VARTYPE(THISFORM.Tree.SELECTEDITEM) = "O" *!* User did select a node, but I'll do some checking first liCurrentKey = THISFORM.Tree.SELECTEDITEM.KEY IF VARTYPE(THISFORM.Tree.SELECTEDITEM.PARENT) # "O" *!* Is a root node. Out. This is my own rule, *!* but if this check is skipped, the root nodes will work, too. RETURN ENDIF IF VARTYPE(THISFORM.TreePlanContabil.SELECTEDITEM.Previous) # "O" *!* The selected node is the first child. No room to move up. Out. RETURN ENDIF LockWindowUpdate(THISFORM.HWND) && lock the window (api call) SELECT table LOCATE FOR iId = liCurrentKey *!* Get the parent key for the selected node - I'll need it later liParentKey = table.iParentId *!* Get the current position liCurrentPosition = table.iSortOrder *!* Search the next record (cannot use Skip - they could be sorted somehow else) LOCATE FOR iParentKey = liParentKey AND iSortOrder = liCurrentPosition - 1 *!* Change the value in iSortOrder column REPLACE iSortorder WITH iSortorder + 1 *!* Go back to the first record LOCATE FOR iId = liCurrentKey REPLACE iSortOrder WITH iSortorder - 1 TABLEUPDATE(.T.,.T.,"table") vp_parameter = some_specific_parameter && i use a parameterised view here, but no problem at all REQUERY("table") THISFORM.Filltree(.T.) *!* This one fills the tree back, erasing all the nodes first - *!* that's .T. parameter stand for THISFORM.Tree.Nodes(liCurrentKey).EnsureVisible && just to keep it in window THISFORM.Tree.Nodes(liCurrentKey).SELECTED = .T. && I can click again on 'Move up' button LockWindowUpdate(0) && release the display lock ENDIFIt works like a charm. It even could be optimized (for example, sort the nodes as the user wants and after that TABLEUPDATE()...