Treeview control type

This control displays a set of elements in a tree similar to the ones commonly used in filesystems and allows the user to select elements from the tree.

The control can be used in collection variables. If that's the case, the user can select many items from the tree.

TreeviewSample
Treeview sample

The UI for the levels and items can use the same components (image, detail, trailing text) defined in the Dynamic Combo Box control type.

This control is useful when:

  1. The items can be grouped in in a hierarchical structure that makes sense to the user.
    Examples:
    Continent -> Country -> City
    Company -> Area -> Employee
  2. The size of the set is medium or large.

If the set size is small and/or the hierarchy is not as relevant, the Dynamic Combo Box control type may be a better option.

If the set size is large, consider using the lazy loading feature described in the following sections.

Datasource

The treeview is largely defined by the items it contains. To load these items, the developer must specify a Procedure or Data provider in the "Treeview Data Source" property in the ControlInfo node.

The object referenced in this property must return a value of the "K2BTools.Controls.Treeview.Structure" SDT.

TreeviewStructureSDT
SDT structure

Note that the structure contains a recursive element called "items", where the children of a level must be loaded. This SDT can contain the full treeview structure or just a part of it, leaving the rest to be lazy loaded. The pattern considers a datasource to contain a lazy load if it contains a parameter called "K2BT_TreeviewPath".

The fields in this SDT must be used as follows:

  1. value: must contain a value that is unique. In some cases, this may imply that a "fully qualified" value must be used. For example, if showing contents of a file system where file names can be repeated in different folders, the value should be the complete file path.
  2. description, imageUrl, detail, trailingText: these fields define how the level / item is shown. See Dynamic Combo Box control type to learn how each value is used.
  3. items: when the item is not a level, it should be left empty. When loading a level, this should contain the level's children, if any. Note that if using lazy loading, this collection may be left empty even when loading a level, as the contents will be loaded when necessary.
  4. isLevel: this boolean value should be used to define if the item should be treated as a level or not.
  5. isContentLoaded: this value is only used if the "isLevel" field is set to true. In that case, this field indicates if the "items" field is already loaded and should be treated as such. That mainly implies that if the collection is empty an empty state should be shown.

When loading items lazily, a procedure must be specified in the "Path Resolver" property. This procedure must receive a value and return the path where that value is located as a collection of character values.

This is necessary because when the control is shown for the first time and values are selected in the control, we must have an efficient way of determining which levels should be loaded / open in the control initially. Using the path resolver, K2BTools can generate code to fetch the contents for all the levels involved in the path so that the treeview behaves correctly.

Loading items eagerly

The following dataprovider sample loads a treeview eagerly.

Structure
{
    Item
    {
        value = "Country:"+CountryId.ToString()
        description = CountryName
        isLevel = true
        isContentLoaded = true
        
        items
        {
            value = "City:"+CityId.ToString()
            description = CityName
            isLevel = true
            isContentLoaded = true
            
            items
            {
                Value = CustomerId.ToString()
                description = CustomerName
                imageUrl = CustomerPhoto.ImageURI
                detail = "detail"
                trailingText = "TT"
            }
        }
    }
}

Note that:

  1. The ids for countries and cities is prefixed to avoid confusion with CustomerIds. The latter is left as-is so that the values selected in the control map directly to a variable of that type.
  2. The isLevel field is set to true for countries and cities.
  3. The isContentLoaded field is set to true for countries and cities, as children are loaded always.
  4. This sample receives no parameters. It could receive parameters to add filters in any of the levels, but the control does not require nay parameters to work properly in this case.

You can download a sample data provider with static data here.

Loading items lazily

The following sample procedure loads items lazily.

parm(in:&K2BT_TreeviewPath, out: &K2BT_ExtendedControlValues);

------------

&K2BT_ExtendedControlValues = new()

Do Case
    Case &K2BT_TreeviewPath.Count = 0
        For Each Country
            &K2BT_ExtendedControlValuesItem = new()
            &K2BT_ExtendedControlValuesItem.value = "Country:"+CountryId.ToString().Trim()
            &K2BT_ExtendedControlValuesItem.description = CountryName
            &K2BT_ExtendedControlValuesItem.imageUrl = CountryFlag.ImageURI
            &K2BT_ExtendedControlValuesItem.isLevel = true
            &K2BT_ExtendedControlValuesItem.isContentLoaded = false
            &K2BT_ExtendedControlValues.Add(&K2BT_ExtendedControlValuesItem)
        EndFor
        
    Case &K2BT_TreeviewPath.Count = 1
        For Each City
            Where "Country:"+CountryId.ToString().Trim() = &K2BT_TreeviewPath.Item(1).Trim()
            &K2BT_ExtendedControlValuesItem = new()
            &K2BT_ExtendedControlValuesItem.value = "City:"+CityId.ToString().Trim()
            &K2BT_ExtendedControlValuesItem.description = CityName
            &K2BT_ExtendedControlValuesItem.isLevel = true
            &K2BT_ExtendedControlValuesItem.isContentLoaded = false
            &K2BT_ExtendedControlValues.Add(&K2BT_ExtendedControlValuesItem)
        EndFor
        
    Case &K2BT_TreeviewPath.Count = 2
        For Each Customer
            Where "Country:"+CountryId.ToString().Trim() = &K2BT_TreeviewPath.Item(1).Trim()
            Where "City:"+CityId.ToString().Trim() = &K2BT_TreeviewPath.Item(2).Trim()
            &K2BT_ExtendedControlValuesItem = new()
            &K2BT_ExtendedControlValuesItem.value = CustomerId.ToString().Trim()
            &K2BT_ExtendedControlValuesItem.description = CustomerName
            &K2BT_ExtendedControlValues.Add(&K2BT_ExtendedControlValuesItem)
        EndFor
EndCase

Note that, in this case:

  1. The procedure receives a parameter called K2BT_TreeviewPath. This is how the pattern defines if a lazy load should be performed or not. This parameter indicates the level that shouldbe loaded. The parameter K2BT_TreeViewPath must be defined as a Character Collection.
  2. The procedure's actions depends on the path received. If the path is empty, it loads the root level. If the path contains elements, the items corresponding to that path are loaded.
  3. The isLevel field is set to true in countries and cities, to indicate that they should be treated as levels.
  4. The isContentLoaded field is set to false in all cases, to indicate that the control must request the level's contents when necessary.

As mentioned in previous sections, when using lazy loading the developer must also specify a path resolver procedure that recives the value and return the path (Character collection) . A sensible path resolver for this case would be this:

parm(in: &CustomerId, out: &Path);

------------

&Path = new()
For Each Customer
    Where CustomerId = &CustomerId
    
    &Path.Add("Country:"+CountryId.ToString().Trim())
    &Path.Add("City:"+CityId.ToString().Trim())
    
EndFor

You can find a sample procedure with lazy loading using static data here.

Advanced lazy loading

The lazy loading example shown above could be thought of as "fully lazy", as all level contents are loaded lazily when required. This is not mandatory: the developer can choose to load some levels lazily and others eagerly. To do so, the procedure must set the "isContentLoaded" to true when loading eagerly, and also load the level contents in the "items" field.

The path resolver does not change, as it must always return the levels where the item is located regardless of how the level is loaded.

The sample above could be updated like this to load the cities for each country eagerly, and the customers lazily:

parm(in:&K2BT_TreeviewPath, out: &K2BT_ExtendedControlValues);

----------

&K2BT_ExtendedControlValues = new()

Do Case
    Case &K2BT_TreeviewPath.Count = 0
        For Each Country
            &K2BT_ExtendedControlValuesItem = new()
            &K2BT_ExtendedControlValuesItem.value = "Country:"+CountryId.ToString().Trim()
            &K2BT_ExtendedControlValuesItem.description = CountryName
            &K2BT_ExtendedControlValuesItem.imageUrl = CountryFlag.ImageURI
            &K2BT_ExtendedControlValuesItem.isLevel = true
            &K2BT_ExtendedControlValuesItem.isContentLoaded = false
            &K2BT_ExtendedControlValues.Add(&K2BT_ExtendedControlValuesItem)

            For Each City
                &K2BT_ExtendedControlValuesItem = new()
                &K2BT_ExtendedControlValuesItem.value = "City:"+CityId.ToString().Trim()
                &K2BT_ExtendedControlValuesItem.description = CityName
                &K2BT_ExtendedControlValuesItem.isLevel = true
                &K2BT_ExtendedControlValuesItem.isContentLoaded = false
                &K2BT_ExtendedControlValues.Add(&K2BT_ExtendedControlValuesItem)
            EndFor
     EndFor
        
    Case &K2BT_TreeviewPath.Count = 2
        For Each Customer
            Where "Country:"+CountryId.ToString().Trim() = &K2BT_TreeviewPath.Item(1).Trim()
            Where "City:"+CityId.ToString().Trim() = &K2BT_TreeviewPath.Item(2).Trim()
            &K2BT_ExtendedControlValuesItem = new()
            &K2BT_ExtendedControlValuesItem.value = CustomerId.ToString().Trim()
            &K2BT_ExtendedControlValuesItem.description = CustomerName
            &K2BT_ExtendedControlValues.Add(&K2BT_ExtendedControlValuesItem)
        EndFor
EndCase

Data source parameters

The "Data source parameters" property can be used to select the values passed to the data source. Its value is calculated by default based on the data source's parm rule.

Some variable names in this property have special meanings, as defined below:

Variable Name Inferred when Replaced by
&K2BT_TreeviewPath The data source contains a parameter that contains the string "Path" in its name. The path that is being loaded (if this parameter is present, the data source is assumed to implement lazy loading).
&K2BT_ReturnValue The output parameter (in data providers) or out parameters with the "K2BTools.Controls.Treeview.Structure" (in procedures). The variable that receives the result of executing the data source.

Availability