Working with Tree View Controls

#### Search Results

Search

# Working with Tree View Controls

This document covers the following topics:

- About Tree View Controls

- Setting Item Images

- Item Selection in Tree View Controls

- Item Activation

- Item Data

- Sorting

- Label Editing

- Multiple Context Menus

- Dynamic Item Creation

- Drag and Drop

## About Tree View Controls

A tree view control is used to display data in hierarchical form. Each node in the hierarchy is represented internally as a tree view item. The following shows a simple tree view (with optional check boxes) displaying a 3-level hierarchy containing seven tree view items:

The tree view control shown above is specified with the "+/- buttons (b)", "Lines (l)", "Lines at root (r)" and "Check boxes (c)" `STYLE` flags.

The height of each item and the indentation between levels can be set via the `ITEM-H` and `SPACING` attributes, respectively. If either of these are zero, the default setting is used.

## Setting Item Images

Images for the tree view items may be defined by creating and associating an image list control with the tree view control, then (for each item) selecting the required image from the image list via its index and/or image handle, as described in the section Working with Image List Controls.

Please note that it is not necessary to set the image list control's "Large images (L)" style, unless you are additionally using the same image list for a list view control, because the tree view control only uses small images.

## Item Selection in Tree View Controls

Unlike the list view control, the tree view control only supports one selected item. The current selection (if any) may be retrieved by querying the tree view control's (read-only) `SELECTED-SUCCESSOR` attribute.

In addition to being selected by the user, items may be selected or deselected programmatically by setting or clearing their `SELECTED` attribute.

When an item is selected, a `CLICK` event is raised for the list view control (if not suppressed), with the handle of the corresponding item being set in the control's `ITEM` attribute.

## Item Activation

When a user double-clicks on an item, an `ACTIVATE` event is raised (unless suppressed) for the tree view control. The application, if it decides to handle this event, normally performs a user-defined default action on the selected item, the handle to which may be obtained via either the `ITEM` or `SELECTED-SUCCESSOR` attributes of the tree view control. Note that there may be other, non-default, actions applicable to the selected item, but these are typically accessed via other mechanisms. For example, they may be listed (typically along with the default action) in a context menu displayed by the application.

If the "Dbl. click expand (d)" `STYLE` flag is set, double clicking a tree view item that has child items expands the item in addition to activating it.

The `ACTIVATE` event can also be triggered via the keyboard. This can be done in either of two ways:

1. By pressing the key or key combination defined for the tree view control's `ACCELERATOR` attribute. The control need not currently have the focus.

2. By pressing the ENTER key, if the tree view control currently has the focus. This method only works if the dialog neither contains a default pushbutton, nor a pushbutton with the "OK Button (O)" `STYLE` flag set.

In either case, no `ACTIVATE` event is raised if no item is currently selected.

## Item Data

In order to be able to support sorting correctly (see next section), a tree view item's data does not need to be alphanumeric, as it is per default, but can be one of any of the pre-defined types supported by the column's `FORMAT` attribute. In addition, an edit mask can be applied to the item by setting its `EDIT-MASK` attribute. The item's label, as seen by the user for the item, is the alphanumeric representation of the item's internal data, using the associated edit mask (if any). The conversion between the item's internal data and the displayed data is compatible with the `MOVE EDITED` statement if an edit mask is supplied. Otherwise, the conversion between the internal and displayed data, and vice-versa, is compatible with the conversion involved in copying the data to and from the Natural stack (see the `STACK TOP DATA` and `INPUT` statements). For example, numeric values are displayed using the current decimal character (as defined by the `DC` parameter) if necessary, with a leading minus character if negative, date values are displayed in the format defined by the `DTFORM` parameter setting, logical values are displayed as an "X" if true and as a blank if false, and so on.

An example of use of a non-alpha tree view item is shown below:

    PROCESS GUI ACTION ADD WITH
    PARAMETERS
       HANDLE-VARIABLE = #TVITEM-DATE
       TYPE = TREEVIEWITEM
       PARENT = #TV-1
       STRING = '+123.456'
       FORMAT = FT-DECIMAL
       EDIT-MASK = '+ZZZ,ZZ9.999'
    END-PARAMETERS GIVING *ERROR

In this case, the specified item label (`STRING` attribute value) must of course be a valid number of a form compatible with the specified edit mask (+ZZZ,ZZ9.999). Internally, the label is converted to the data type and length corresponding to the specified format, FT-DECIMAL (= P10.5).

Note that it is not possible to access the underlying data for a tree view item directly. The item's data can only be set or retrieved via the item's label.

## Sorting

Tree view items can either be inserted in sorted sequence or explicitly sorted after insertion, by calling the `SORT-ITEMS` action. Items are inserted in ascending alphabetical sequence if either the tree view control or the tree view item being inserted have their `SORTED` attribute set to `TRUE`. In the latter case, the items are optionally sorted in ascending or descending sequence according to their internal data (see last section). See the documentation for the `SORT-ITEMS` action for more information. Note that it is the responsibility of the programmer to ensure that the underlying item formats of the items concerned (as defined by the `FORMAT` attribute) are of comparable types. For example, it is possible to mix integers and floating point values, but not integers and dates.

## Label Editing

The process of label editing for tree view controls is the same as for list view controls. Therefore, for more information on this subject, please refer to the section Label Editing in Tree View and List View Controls.

Note that even if the `SORTED` attribute is set, the items are not automatically re-sequenced after a label editing operation has been completed. If this is required, this can be done as shown in the example below. Firstly, we define some variables for later use:

    01 #CONTROL HANDLE OF GUI 01 #ITEM HANDLE OF TREEVIEWITEM
    01 #SORTITEM HANDLE OF TREEVIEWITEM

In addition, it is assumed that the tree view control is named `#TV-1`.

In the tree view control's `AFTER-EDIT` event, we cannot do the re-sequencing asynchronously, as this would interfere with the (as yet incomplete) editing process. Instead, we simply set the `#SORTITEM` variable to indicate that the re-sequencing should occur at a later time, after the editing process has been completed. This only needs to be done if the tree view items are sorted:

    #CONTROL := *CONTROL #ITEM := #CONTROL.ITEM IF #CONTROL.SORTED OR #ITEM.SORTED
    #SORTITEM := #ITEM ELSE #SORTITEM := NULL-HANDLE END-IF

Note that this examples assumes the use of the default (ascending alphabetical) sorting provided by the tree view control itself.

The actual work of re-sequencing the items is done asynchronously in the dialog's `IDLE` event:

    IF #SORTITEM <> NULL-HANDLE PROCESS GUI ACTION SORT-ITEMS WITH #SORTITEM.PARENT
    GIVING *ERROR RESET #SORTITEM END-IF

## Multiple Context Menus

If you wish to support just a single context menu for the control, you can simply set the control's `CONTEXT-MENU` attribute to the handle of the context menu you wish to display, and leave it set to this value. However, it is often required to be able to display more than one context menu for list view controls, whereby this approach is too inflexible.

To address the above problem, the `CONTEXT-MENU` event was introduced (not to be confused with the attribute of the same name as mentioned above!). This event (if not suppressed) is raised for the target control immediately before its `CONTEXT-MENU` attribute is evaluated, allowing the application to dynamically set this attribute to the handle of the appropriate context menu first.

As an example, assume that we have defined two context menus in the dialog editor: one containing item-related commands, `#CTXMENU-ITEMS`, and one containing generic commands (e.g., for switching the view mode), `#CTXMENU-DEFAULT`. In this case, the following `CONTEXT-MENU` event could be used:

    #CONTROL := *CONTROL IF #CONTROL.SELECTED-SUCCESSOR
    <> NULL-HANDLE #CONTROL.CONTEXT-MENU := #CTXMENU-ITEMS ELSE #CONTROL.CONTEXT-MENU
    := #CTXMENU-DEFAULT END-IF

where the following local data definition is assumed:

    01 #CONTROL HANDLE OF GUI

In this example, the context menu `#CTXMENU-ITEMS` will be displayed if the user right clicks at a position occupied by an item, or `#CTXMENU-DEFAULT` otherwise.

Of course, this technique can be refined further to display context menus specific to the type(s) of the selected item(s).

## Dynamic Item Creation

When a tree view item is expanded or collapsed, an `EXPAND` event or `COLLAPSE` event (respectively) is raised for the control, if the event is not suppressed. Amongst other things, these events allow tree-view items to be dynamically created and deleted on demand.

For example, the following code demonstrates the dynamic creation of three items in response to the `EXPAND` event. It assumes that a dummy placeholder item with an empty `STRING` attribute value is already in place at the position where the items should be inserted. The placeholder can either have been statically defined in the dialog editor, or dynamically-defined during the initial tree view item creation. The purpose of the placeholder is to ensure that a "+" button (if button display enabled via the "+/- buttons (b)" `STYLE`) appears next to the parent node.

The following `EXPAND` event code assumes that the variable `#TGT-ITEM` contains the handle of the tree view item under which the dynamic tree view items are to be created:

    #CONTROL := *CONTROL
    #ITEM := #CONTROL.ITEM
    IF #ITEM = #TGT-ITEM
      #TVITEM-DYN := #ITEM.FIRST-CHILD
      IF #TVITEM-DYN <> NULL-HANDLE AND
         #TVITEM-DYN.STRING = ' '
        PROCESS GUI ACTION DELETE WITH #TVITEM-DYN GIVING *ERROR
        FOR #I 1 3
          COMPRESS 'Dynamic Item' #I INTO #A
          PROCESS GUI ACTION ADD WITH PARAMETERS
            HANDLE-VARIABLE = #TVITEM-DYN
            TYPE = TREEVIEWITEM
            PARENT = #ITEM
            STRING = #A
          END-PARAMETERS GIVING *ERROR
        END-FOR
      END-IF
    END-IF

where the following local data definitions are assumed:

    01 #CONTROL HANDLE OF GUI
    01 #ITEM HANDLE OF TREEVIEWITEM
    01 #TVITEM-DYN HANDLE OF TREEVIEWITEM
    01 #TGT-ITEM HANDLE OF TREEVIEWITEM
    01 #I (I4)
    01 #A (A) DYNAMIC

The above code first looks to see whether the item being expanded is the target item. If so, it queries the `STRING` attribute of the first child, to find out whether a placeholder is present. If this is the case, the placeholder is deleted, and three dynamic tree view items are created. The `STRING` attribute value for the inserted items is specified on creation, rather than modified afterwards, to ensure that the code also works correctly for `SORTED` tree views.

To save resources, the dynamically-created items could optionally be deleted again in the `COLLAPSE` event handler, being replaced by a placeholder item:

    #CONTROL := *CONTROL
    #ITEM := #CONTROL.ITEM
    IF #ITEM = #TGT-ITEM
      PROCESS GUI ACTION DELETE-CHILDREN WITH #ITEM GIVING *ERROR
      PROCESS GUI ACTION ADD WITH PARAMETERS /* placeholder
        TYPE = TREEVIEWITEM
        PARENT = #ITEM
      END-PARAMETERS GIVING *ERROR
    END-IF

## Drag and Drop

The basic technique for providing drag and drop support is described in the section Using the Clipboard and Drag and Drop.

Note that it is the responsibility of the programmer to (if required) highlight the item (if any) under the mouse cursor by setting its `SELECTED` attribute, and to restore the original selection (if any) afterwards.

The following example provides some code for demonstrating a tree view control acting as a drop target, in order to support dragging and dropping text from another application (e.g. WordPad) onto a tree view item in order to change its label.

The first step is to ensure that the drop mode is set correctly for the tree view control. In the Tree View Control Attributes window in the dialog editor, set the Drop mode selection box to Copy+Move. This causes the control's `DROP-MODE` attribute to be set to `DM-COPYMOVE` in the generated source code for the dialog.

Next, the required local variables that are going to be used below must be defined:

    01 #CONTROL HANDLE OF GUI
    01 #DROP-ITEM HANDLE OF GUI
    01 #ITEM HANDLE OF GUI
    01 #SELECTED HANDLE OF GUI
    01 #AVAIL (L)
    01 #X (I4)
    01 #Y (I4)

Having done this, we can write the necessary event handlers. The logical place to start is with the `DRAG-ENTER` event:

    #CONTROL := *CONTROL
    #CONTROL.CLIENT-HANDLE := #CONTROL.SELECTED-SUCCESSOR
    PROCESS GUI ACTION INQ-FORMAT-AVAILABLE WITH CF-TEXT #AVAIL GIVING *ERROR
    IF #AVAIL
      #CONTROL.SUPPRESS-DRAG-DROP-EVENT := NOT-SUPPRESSED
    ELSE
      #CONTROL.SUPPRESS-DRAG-DROP-EVENT := SUPPRESSED
    END-IF

The above code first saves the handle of the currently selected item in the control's `CLIENT-HANDLE` attribute for the purposes of restoring the selection to this item later. The event handler then checks whether text data is available on the drag-drop clipboard. If it is, the `DRAG-DROP` event is unsuppressed in order to allow a drop. Otherwise, we suppress this event in order to prohibit a drop such that the "no drop" drag cursor appears.

To provide drop emphasis during the dragging of external data across the tree view control, a `DRAG-OVER` event handler is supplied:

    #CONTROL := *CONTROL
    IF #CONTROL.SUPPRESS-DRAG-DROP-EVENT = NOT-SUPPRESSED
      PROCESS GUI ACTION INQ-DRAG-DROP WITH
        3X #X #Y GIVING *ERROR
      PROCESS GUI ACTION INQ-ITEM-BY-POSITION WITH
        #CONTROL #X #Y #ITEM GIVING *ERROR
      IF #ITEM <> #DROP-ITEM
        #DROP-ITEM := #ITEM
        IF #DROP-ITEM <> NULL-HANDLE
          #DROP-ITEM.SELECTED := TRUE
        END-IF
      END-IF
    END-IF

The above code performs the following:

1. If a drop was disallowed in the `DRAG-ENTER` event, the event is ignored, as no drag emphasis is required in this case.

2. Otherwise, the `INQ-DRAG-DROP` action is called to determine the current drop position.

3. The `INQ-ITEM-BY-POSITION` action is used to find the tree view item (if any) at the current drop position. This item is tracked in `#DROP-ITEM`.

4. The tree view item (if any) under the cursor is selected to provide the drop emphasis by setting its `SELECTED` attribute to `TRUE`.

To perform the actual drop, a `DRAG-DROP` event handler is supplied:

    IF #DROP-ITEM <> NULL-HANDLE
      PROCESS GUI ACTION GET-CLIPBOARD-DATA WITH CF-TEXT #DROP-ITEM.STRING
        GIVING *ERROR
    END-IF
    #CONTROL := CONTROL  /* "parameter" for subroutine below
    PERFORM RESET-SELECTION

If there is a tree view item representing the drop target, the above code retrieves the text from the drag-drop clipboard directly into the item's caption. Afterwards, the original selection state of the tree view control is restored. The `RESET-SELECTION` subroutine used for this purpose can be written as follows:

    #ITEM := #CONTROL.CLIENT-HANDLE
    IF #ITEM <> NULL-HANDLE
      /* Restore original selection:
      #ITEM.SELECTED := TRUE
    ELSE
      /* Nothing was originally selected,
      /* so clear any existing selection:
      #ITEM := #CONTROL.SELECTED-SUCCESSOR
      #ITEM.SELECTED := FALSE
    END-IF
    RESET #DROP-ITEM

To satisfy the logic provided for the other event handlers, `#DROP-ITEM` is also reset, indicating the absence of a drop item.

Lastly, in the case that the user cancels the drag operation, or exits the bounds of the tree view control without having performed a drop, a `DRAG-LEAVE` event handler is supplied:

    #CONTROL := CONTROL  /* "parameter" for subroutine below
    PERFORM RESET-SELECTION

The above code simply invokes the inline subroutine listed above in order to clear the drop emphasis (if any), and to restore the original selection state.

### On this page

- About Tree View Controls
- Setting Item Images
- Item Selection in Tree View Controls
- Item Activation
- Item Data
- Sorting
- Label Editing
- Multiple Context Menus
- Dynamic Item Creation
- Drag and Drop