WebList Grouping

The cWebList (and cWebGrid) support displaying data in groups. It can do this completely automatically in automatic mode and it supports a manual mode. The peGrouping property enables the grouping logic and determines the mode.

You may also want to watch the DataFlex Learning Center course about WebList Grouping.

Automatic Grouping

In automatic mode, the control completely handles the grouping logic and the developer or the end user only determine on which column(s) the data is grouped. Set peGrouping to grpAutomatic to enable this mode. It does require all data to be loaded into the WebList at once, which can be done by setting peDbGridType to gtAllData or by manually loading the list.

The context menu that is shown when right clicking in the header of the WebList will now show the group selection utility that allows the user to configure / view the columns by which the data is grouped. The cWebMenuGroupingSelector class can be used to add the selector to a custom context menu.

In the above screenshot, the data will be grouped by the state column and then each city will be grouped per state. The groups can be rearranged using drag and drop and the column can be changed by using the combo. The ordering of the groups is configured using the arrows next to the combo and the trash can button will remove a group. Use the ‘+ Add sub group’ button to add a new group.

The screenshot above shows the data being grouped (on State and City columns). Note that these columns have been hidden in this screenshot, which does not happen automatically.

Group Configuration

By default, the grouping configuration for automatic grouping is stored in the local storage of the browser. This means the data will be shown grouped again when the user reopens the view or the application in the same browser. So even reloading the page will maintain the group configuration. Set pbStoreGroupConfig to False to disable this behavior.

To customize how groups are stored, the OnGroupConfigChanged event can be implemented, which will receive a struct defining the current group config after each change to the grouping by the end user. This same configuration can be passed to ApplyGroupConfig to (re)apply a group configuration. This procedure can also be used to apply a custom group configuration. Use ClearGroupConfig to clear the grouping.

Manual Grouping

To enable manual grouping mode, set the peGrouping property to grpCustom. In this mode, the developer is responsible for determining the groups by filling structs in OnManualLoadData.

 

Here is the procedure definition of OnManualLoadData:

Procedure OnManualLoadData tWebRow[] ByRef aTheRows String ByRef sCurrentRowID tWebGroupConfig[] ByRef aTheGroups tWebGroupHeader[] ByRef aTheGroupHeaders

 

Then, inside OnManualLoadData, the developer needs to fill two additional arrays of structs with information on the groups. Together, these two will set the text in the group headers like this:

"<grouplabel>: groupvalue".

 

Finally, fill the rows and place them under a groupheader index.

 

Take the following steps to implement manual grouping:

STEP 1

Set peGrouping of the cWebList to grpCustom and set peDbGridType to gtManual.

 

STEP 2

Define the groupings by manually filling the tWebGroupConfig struct array. This array defines the grouping in the weblist and is also (implicitely) used in automatic grouping. For manual grouping only the member sLabel needs to be set. This is the label that will be shown at each group header. The iColumnId and bReverse members can be ignored (these are only relevant for automatic grouping).

If you are grouping the data on two levels, you’ll need to provide two labels in the aTheGroups arrays. This means that you will have to define two items in the array, like this:

Move "Brand"            to aTheGroups[0].sLabel

Move "Pricing category" to aTheGroups[1].sLabel

 

STEP 3

The second array that should be filled is the tWebGroupHeader struct array. This defines the values that will be shown in the group headers. The following struct members need to be set:

When there should be multiple group headers above a line, then iParentHeaderIndex of tWebGroupHeader should be set.

With these two additional struct arrays the grouping is defined, but there is no data yet. This will be done in step 4.

 

STEP 4

The first parameter of the OnManualLoadData procedure is a tWebRow struct, which is where the weblist data should go.

The data can be manually filled like this:

Move iRow to aTheRows[0].sRowId // iRow can start at 0 and increment for each row

Move "value_col1" to aTheRows[0].aCells[0].sValue

Move "value_col2" to aTheRows[0].aCells[1].sValue

Then, each FIRST row that should be grouped under a particular grouping header must be bound to an item from the just defined tWebGroupHeader struct array:

Move 0 to aTheRows[0].iGroupHeaderIndex // Bind to group header

 

The other rows that follow under the same grouping header must have iGroupHeaderIndex set to -1, like this:

Increment iRow

Move iRow to aTheRows[1].sRowId // iRow can start at 0 and increment for each row

Move "nextrowvalue_col1" to aTheRows[1].aCells[0].sValue

Move "nextrowvalue_col2" to aTheRows[1].aCells[1].sValue

Move -1 to aTheRows[1].iGroupHeaderIndex // Bind to group header

 

Example

The following code example shows how to implement simple manual grouping in a weblist. It will render a list with 5 rows divided into 2 groups, like this:

Object oCarsPerBrandList is a cWebList

    Set pbFillHeight to True

    Set peGrouping to grpCustom

    Set pbDataAware to False

   

    Object oCarColumn is a cWebColumn

        Set psCaption to "Car"

        Set piWidth to 50

    End_Object

 

    Procedure OnManualLoadData tWebRow[] ByRef aTheRows String ByRef sCurrentRowID tWebGroupConfig[] ByRef aTheGroups tWebGroupHeader[] ByRef aTheGroupHeaders

        //  Define that we have one level of grouping

        Move "Brand" to aTheGroups[0].sLabel

       

        //  Group Mercedes

        Move "Mercedes" to aTheGroupHeaders[0].sItem

        Move 0 to aTheGroupHeaders[0].iGroupIndex

        Move -1 to aTheGroupHeaders[0].iParentHeaderIndex

       

        //  Rows for group Mercedes

        Move 0 to aTheRows[0].sRowId

        Move "540K" to aTheRows[0].aCells[0].sValue

        Move 0 to aTheRows[0].iGroupHeaderIndex // Bind to group header

       

        Move 1 to aTheRows[1].sRowId

        Move "300d" to aTheRows[1].aCells[0].sValue

        Move -1 to aTheRows[1].iGroupHeaderIndex

       

        Move 2 to aTheRows[2].sRowId

        Move "230SL" to aTheRows[2].aCells[0].sValue

        Move -1 to aTheRows[2].iGroupHeaderIndex

       

        //  Group BMW

        Move "BMW" to aTheGroupHeaders[1].sItem

        Move 0 to aTheGroupHeaders[1].iGroupIndex

        Move -1 to aTheGroupHeaders[1].iParentHeaderIndex

       

        //  Rows for group BMW

        Move 3 to aTheRows[3].sRowId

        Move "Isetta" to aTheRows[3].aCells[0].sValue

        Move 1 to aTheRows[3].iGroupHeaderIndex // Bind to group header

       

        Move 2 to aTheRows[4].sRowId

        Move "M3 E30" to aTheRows[4].aCells[0].sValue

        Move -1 to aTheRows[4].iGroupHeaderIndex

    End_Procedure

   

    Procedure OnLoad

        Send GridRefresh

    End_Procedure

End_Object

This is the result of the code sample above:

 

Manual grouping with automatic filling

Manual grouping does not require that filling of the cWebList to also be done manually. The following code example shows how the rows can be filled from the database while manually creating grouping.

Procedure OnManualLoadData tWebRow[] ByRef aTheRows String ByRef sCurrentRowID tWebGroupConfig[] ByRef aTheGroups tWebGroupHeader[] ByRef aTheGroupHeaders

    Integer iRow iOrder iGroupHead

    

    Move "Order" to aTheGroups[0].sLabel

    Move -1 to iGroupHead

    

    Clear OrderDetail

    Find GT OrderDetail by 1

    While (Found)

        If (OrderDetail.Order_Number <> iOrder) Begin

            Relate OrderDetail

            

            Get LoadGridRow to aTheRows[iRow]

            Move OrderDetail.Order_Number to iOrder

            

            Increment iGroupHead

            

            Move (SFormat("%1 (%2)", OrderHeader.Order_Number, Trim(Customer.Name))) to aTheGroupHeaders[iGroupHead].sItem

            Move (SFormat("%1", FormatCurrency(OrderHeader.Order_Total, 2))) to aTheGroupHeaders[iGroupHead].sTotal

            Move -1 to aTheGroupHeaders[iGroupHead].iParentHeaderIndex

            Move 0 to aTheGroupHeaders[iGroupHead].iGroupIndex

            Move iGroupHead to aTheRows[iRow].iGroupHeaderIndex

        End

        Else Begin

            Get LoadGridRow to aTheRows[iRow]

        End

        

        Increment iRow

        Find GT OrderDetail by 1

    Loop

    

    Clear OrderDetail

    

    // Select the first row

    If (SizeOfArray(aTheRows) > 0) ;

        Move aTheRows[0].sRowId to sCurrentRowID

End_Procedure

 

Collapse / Expand Groups

Groups can be collapsed and expanded. This can be done by clicking the v icon at the left of each group header row. Alternatively, this can be done using the - and + keyboard keys to collapse or expand the group with the current row. The Ctrl + - and Ctrl + + key combination will collapse or expand all the groups. The pbGroupsCollapsible property controls whether groups can be collapsed, setting it to False will make the icon disappear. The pbGroupsCollapseByDefault option will determine if groups are automatically collapsed when using automatic grouping.  With manual grouping, the tWebGroupHeader struct contains a bCollapsed option to collapse groups.