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.
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.
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.
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:
Set peGrouping of the cWebList to grpCustom and set peDbGridType to gtManual.
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
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:
Integer iGroupIndex: Set it to 0 for the first grouping level, 1 for the second grouping level, etc.
Integer iParentHeaderIndex: Set it to -1 for the first grouping level, 0 for the second grouping level, etc.
String sItem: Set it to the group value
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.
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
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 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
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.