Managing TreeView items
An item in a TreeView is a TreeViewItem structure. The preceding
section described how to set the item’s properties in the
structure and then insert it into the TreeView.
This code declares a TreeViewItem structure and sets several
properties:
1 |
TreeViewItem tvi_defined |
1 |
tvi_defined.Label = "Symphony No. 3 Eroica" |
1 |
tvi_defined.StatePictureIndex = 0 |
1 |
tvi_defined.PictureIndex = 3 |
1 |
tvi_defined.SelectedPictureIndex = 4 |
1 |
tvi_defined.OverlayPictureIndex = 0 |
1 |
tvi_defined.Children = TRUE |
For information about Picture properties,
see “Managing TreeView pictures”.
When you insert an item, the inserting function returns a
handle to that item. The TreeViewItem structure is copied to the
TreeView control, and you no longer have access to the item’s
properties:
1 |
itemhandle = This.InsertItemLast(parenthandle, & |
1 |
tvi_defined) |
Procedure for items: get, change, and set
If you want to change the properties of an item in the TreeView,
you:
- Get
the
item, which assigns it to a TreeViewItem structure. - Make the changes
, by setting
TreeViewItem properties. - Set
the item, which copies
it back into the TreeView.
When you work with items that have been inserted in the TreeView,
you work with item handles. Most TreeView events pass one or two
handles as arguments. The handles identify the items the user is
interacting with.
This code for the Clicked event uses the handle of the clicked
item to copy it into a TreeViewItem structure whose property values
you can change:
1 |
treeviewitem tvi |
1 |
This.GetItem(handle, tvi) |
1 |
tvi.OverlayPictureIndex = 1 |
1 |
This.SetItem(handle, tvi) |
Important Remember to call the SetItem function after you change an
item’s property value. Otherwise, nothing happens in the
TreeView.
Items and the hierarchy
You can use item handles with the FindItem function to navigate
the TreeView and uncover its structure. The item’s properties
tell you what its level is, but not which item is its parent. The
FindItem function does:
1 |
long h_parent |
1 |
h_parent = This.FindItem(ParentTreeItem!, handle) |
You can use FindItem to find the children of an item or to
navigate through visible items regardless of level.
For more information, see the FindItem function
in the PowerScript Reference
.
Enabling TreeView functionality in scripts
By setting TreeView properties, you can enable or disable
user actions like deleting or renaming items without writing any
scripts. You can also enable these actions by calling functions.
You can:
- Delete items
- Rename items
- Move items via drag and drop
- Sort items
Deleting items
To allow the user to delete items, enable the TreeView’s
DeleteItems property. When the user presses the delete key,
the selected item is deleted and the DeleteItem event is triggered.
Any children are deleted too.
If you want more control over deleting, such as allowing deleting
of detail items only, you can call the DeleteItem function instead
of setting the property. The function also triggers the DeleteItem
event.
Example
This script is for a TreeView user event. Its event ID is
pbm_keydown and is triggered by key presses when the TreeView
has focus. The script checks whether the delete key
is pressed and whether the selected item is at the detail level.
If both are TRUE, it deletes it.
The value of the TreeView’s DeleteItems property
is FALSE. Otherwise, the user could delete any item, despite this
code:
1 |
TreeViewItem tvi |
1 |
long h_item |
1 |
1 |
IF KeyDown(KeyDelete!) = TRUE THEN |
1 |
h_item = This.FindItem(CurrentTreeItem!, 0) |
1 |
This.GetItem(h_item, tvi) |
1 |
IF tvi.Level = 3 THEN |
1 |
This.DeleteItem(h_item) |
1 |
END IF |
1 |
END IF |
1 |
1 |
RETURN 0 |
Renaming items
If you enable the TreeView’s EditLabels property,
the user can edit an item label by clicking twice on the text.
Events
There are two events associated with editing labels.
The BeginLabelEdit event occurs after the second click when
the EditLabels property is set or when the EditLabel function is
called. You can disallow editing with a return value of 1.
This script for BeginLabelEdit prevents changes to labels
of level 2 items:
1 |
TreeViewItem tvi |
1 |
This.GetItem(handle, tvi) |
1 |
IF tvi.Level = 2 THEN |
1 |
RETURN 1 |
1 |
ELSE |
1 |
RETURN 0 |
1 |
END IF |
The EndLabelEdit event occurs when the user finishes editing
by pressing enter, clicking on another item,
or clicking in the text entry area of another control. A script
you write for the EndLabelEdit event might validate the user’s changes–for
example, it could invoke a spelling checker.
EditLabel function
For control over label editing, the BeginLabelEdit event can
prohibit editing of a label, as shown above. Or you can set the
EditLabels property to FALSE and call the EditLabel function when
you want to allow a label to be edited.
When you call the EditLabel function, the BeginLabelEdit event
occurs when editing begins and the EndLabelEdit event occurs when
the user presses enter or the user clicks another item.
This code for a CommandButton puts the current item into editing
mode:
1 |
long h_tvi |
1 |
h_tvi = tv_1.findItem(CurrentTreeItem!, 0) |
1 |
tv_1.EditLabel(h_tvi) |
Moving items via drag and drop
At the window level, PowerBuilder provides functions and properties
for dragging controls onto other controls. Within the TreeView,
you can also let the user drag items onto other items. Users might
drag items to sort them, move them to another branch, or put child
items under a parent.
When you implement drag and drop as a way to move items, you
decide whether the dragged item becomes a sibling or child of the
target, whether the dragged item is moved or copied, and whether
its children get moved with it.
There are several properties and events that you need to coordinate
to implement drag and drop for items:
Property or event | Setting or purpose |
---|---|
DragAuto property | TRUE or FALSE. If FALSE, you must call the Drag function in the BeginDrag event |
DisableDragDrop property | FALSE |
DragIcon property | An appropriate icon or None!, which means the user drags an image of the item |
BeginDrag event | Script for saving the handle of the dragged item and optionally preventing particular items from being dragged |
DragWithin event | Script for highlighting drop targets |
DragDrop event | Script for implementing the result of the drag operation |
Example
The key to a successful drag-and-drop implementation is in
the details. This section illustrates one way of moving items. In
the example, the dragged item becomes a sibling of the drop target,
inserted after it. All children of the item are moved with it and
the original item is deleted.
A function called recursively moves the children, regardless
of the number of levels. To prevent an endless loop, an item cannot
become a child of itself. This means a drop target that is a child
of the dragged item is not allowed.
BeginDrag event The script saves the handle of the dragged item in an instance
variable:
1 |
ll_dragged_tvi_handle = handle |
If you want to prevent some items from being dragged–such
as items at a particular level–that code goes here too:
1 |
TreeViewItem tvi |
1 |
This.GetItem(handle, tvi) |
1 |
IF tvi.Level = 3 THEN This.Drag(Cancel!) |
DragWithin event The script highlights the item under the cursor so the user can
see each potential drop target. If only some items are drop targets,
your script should check an item’s characteristics before
highlighting it. In this example, you could check whether an item
is a parent of the dragged item and highlight it only if it is not:
1 |
TreeViewItem tvi |
1 |
This.GetItem(handle, tvi) |
1 |
tvi.DropHighlighted = TRUE |
1 |
This.SetItem(handle, tvi) |
DragDrop event This script does all the work. It checks whether the item
can be inserted at the selected location and inserts the dragged
item in its new position–a sibling after the drop target.
Then it calls a function that moves the children of the dragged
item too:
1 |
TreeViewItem tvi_src, tvi_child |
1 |
long h_parent, h_gparent, h_moved, h_child |
1 |
integer rtn |
1 |
1 |
// Get TreeViewItem for dragged item |
1 |
This.GetItem(ll_dragged_tvi_handle, tvi_src) |
1 |
1 |
// Don't allow moving an item into its own branch, |
1 |
// that is, a child of itself |
1 |
h_gparent = This.FindItem(ParentTreeItem!, handle)DO WHILE h_gparent <> -1 |
1 |
IF h_gparent = ll_dragged_tvi_handle THEN |
1 |
MessageBox("No Drag", & |
1 |
"Can't make an item a child of itself.") |
1 |
RETURN 0 |
1 |
END IF |
1 |
h_gparent=This.FindItem(ParentTreeItem!, h_gparent) |
1 |
LOOP |
1 |
1 |
// Get item parent for inserting |
1 |
h_parent = This.FindItem(ParentTreeItem!, handle) |
1 |
// Use 0 if no parent because target is at level 1 |
1 |
IF h_parent = -1 THEN h_parent = 0 |
1 |
1 |
// Insert item after drop target |
1 |
h_moved = This.InsertItem(h_parent, handle, tvi_src) |
1 |
IF h_moved = -1 THEN |
1 |
MessageBox("No Dragging", "Could not move item.") |
1 |
RETURN 0 |
1 |
1 |
ELSE |
1 |
// Args: old parent, new parent |
1 |
rtn = uf_movechildren(ll_dragged_tvi_handle, & |
1 |
h_moved) |
1 |
1 |
/ If all children are successfully moved, |
1 |
// delete original item |
1 |
IF rtn = 0 THEN |
1 |
This.DeleteItem(ll_dragged_tvi_handle) |
1 |
END IF |
1 |
1 |
END IF |
The DragDrop event script shown above calls the function uf_movechildren. The
function calls itself recursively so that all the levels of children
below the dragged item are moved:
1 |
// Function: uf_movechildren |
1 |
// |
1 |
// Arguments: |
1 |
// oldparent - Handle of item whose children are |
1 |
// being moved. Initially, the dragged item in its |
1 |
// original position |
1 |
// |
1 |
// newparent - Handle of item to whom children are |
1 |
// being moved. Initially, the dragged item in its |
1 |
// new position. |
1 |
1 |
long h_child, h_movedchild |
1 |
TreeViewItem tvi |
1 |
1 |
// Return -1 if any Insert action fails |
1 |
1 |
// Are there any children? |
1 |
h_child = tv_2.FindItem(ChildTreeItem!, oldparent) |
1 |
IF h_child <> -1 THEN |
1 |
tv_2.GetItem(h_child, tvi) |
1 |
h_movedchild = tv_2.InsertItemLast(newparent, tvi) |
1 |
IF h_movedchild = -1 THEN RETURN -1 |
1 |
1 |
// Move the children of the child that was found |
1 |
uf_movechildren(h_child, h_movedchild) |
1 |
1 |
// Check for more children at the original level |
1 |
h_child = tv_2.FindItem(NextTreeItem!, h_child) |
1 |
DO WHILE h_child <> -1 |
1 |
tv_2.GetItem(h_child, tvi) |
1 |
h_movedchild= tv_2.InsertItemLast(newparent,tvi) |
1 |
IF h_movedchild = -1 THEN RETURN -1 |
1 |
uf_movechildren(h_child, h_movedchild) |
1 |
1 |
// Any more children at original level? |
1 |
h_child = tv_2.FindItem(NextTreeItem!, h_child) |
1 |
LOOP |
1 |
END IF |
1 |
RETURN 0 // Success, all children moved |
Sorting items
A TreeView can sort items automatically, or you can control
sorting manually. Manual sorting can be alphabetic by label text,
or you can implement a user-defined sort to define your own criteria.
The SortType property controls the way items are sorted. Its values
are of the enumerated data type grSortType.
Automatic alphabetic sorting To enable sorting by the text label, set the SortType property
to Ascending! or Descending!. Inserted items are sorted automatically.
Manual alphabetic sorting For more control over sorting, you can set SortType to Unsorted!
and sort by calling functions:
Use this function | To do this |
---|---|
InsertItemSort | Insert an item at the correct alphabetic position if possible |
Sort | Sort the immediate children of an item |
SortAll | Sort the whole branch below an item |
If users will drag items to organize the list, you should
disable sorting.
Sorting by other criteria To sort items by criteria other than their labels, implement
a user-defined sort by setting the SortType property to UserDefinedSort!
and writing a script for the Sort event. The script specifies how
to sort items.
PowerBuilder triggers the Sort event for each pair of items
it tries to reorder. The Sort script returns a value reporting which
item is greater than the other. The script can have different rules
for sorting based on the type of item. For example, level 2 items
can be sorted differently from level 3. The TreeView is sorted whenever
you insert an item.
Example of Sort event
This sample script for the Sort event sorts the first level
by the value of the Data property and other levels alphabetically
by their labels. The first level displays composers chronologically,
and the Data property contains an integer identifying a composer’s
century:
1 |
//Return values |
1 |
//-1 Handle1 is less than handle2 |
1 |
// 0 Handle1 is equal to handle2 |
1 |
// 1 Handle1 is greater than handle2 |
1 |
1 |
TreeViewItem tvi1, tvi2 |
1 |
1 |
This.GetItem(handle1, tvi1) |
1 |
This.GetItem(handle2, tvi2) |
1 |
1 |
IF tvi1.Level = 1 THEN |
1 |
// Compare century values stored in Data property |
1 |
IF tvi1.data > tvi2.Data THEN |
1 |
RETURN 1 |
1 |
ELSEIF tvi1.data = tvi2.Data THEN |
1 |
RETURN 0 |
1 |
ELSE |
1 |
RETURN -1 |
1 |
END IF |
1 |
ELSE |
1 |
// Sort other levels in alpha order |
1 |
IF tvi1.Label > tvi2.Label THEN |
1 |
RETURN 1 |
1 |
ELSEIF tvi1.Label = tvi2.Label THEN |
1 |
RETURN 0 |
1 |
ELSE |
1 |
RETURN -1 |
1 |
END IF |
1 |
END IF |