Default layout fallback¶
When a user opens the drilldown for the first time, there is no saved layout to render. Without a fallback, the resulting view would be empty — no visible columns, no grouping, no useful information. The drilldown detects this case and applies a sensible default layout.
How the empty state is detected¶
The check uses the existing GetVisibleOrGroupedCount() helper on CLayoutInterface:
If PrimaryLayoutInterface.GetVisibleOrGroupedCount() = 0 Then
ApplyDefaultDrilldownLayout(PrimaryLayoutInterface)
End If
A return value of zero means the saved layout has no entries with m_nVisibleIndex >= 0 and no entries with m_nGroupIndex >= 0 — that is, nothing the renderer would surface to the user. This is the right test for "no usable layout" because:
- It treats a layout file that exists but has been emptied as still empty.
- It does not produce false positives for layouts that have visible columns and a deliberate grouping.
When the check happens¶
The check runs before DisplayDrillDown is called. This is intentional. DisplayDrillDown triggers LayoutInterface.Execute(), which in turn calls RequestElements against the optimized table to populate the cache for everything that the layout needs. By configuring the layout first, the optimized table fetches the correct columns on its first pass and the gridview renders the layout without further intervention.
sequenceDiagram
participant U as User
participant D as OnDrillDown
participant L as PrimaryLayoutInterface
participant F as FDrilldown
participant G as GridView
U->>D: double-click row
D->>L: build criteria
D->>L: GetVisibleOrGroupedCount()
alt Count = 0
D->>L: ApplyDefaultDrilldownLayout
end
D->>F: DisplayDrillDown(L)
F->>L: AttachToLayoutInterface
F->>L: Execute()
L->>L: RequestElements (cache columns)
L->>G: bind data + apply layout
D->>G: AddHandler CustomDrawCell
D->>G: AutoExpandAllGroups = True
D->>G: RefreshData()
What the default layout looks like¶
The default groups by item_no and shows seventeen columns covering the standard MRP detail set. The grouping column is not also added as a regular column — it appears only in the group header rows ("Item #: BCABASSY", "Item #: BIKEMP", and so on).
The visible columns, in left-to-right order, are:
| # | Field name | Caption |
|---|---|---|
| 1 | imloc_byr_plnr | Byr Plnr |
| 2 | item_desc_1 | Item Desc 1 |
| 3 | item_loc | Loc |
| 4 | pur_or_mfg | Item Pur or MFG |
| 5 | mrp_ord_type | MRP - Ord Type |
| 6 | mrp_trx_dt | MRP - Demand/Replenish Date |
| 7 | qty_on_hand_il | O/H Qty (Loc) |
| 8 | mrp_trx_qty | MRP - Qty Demand/Replenish |
| 9 | mrp_new_qty_il | MRP - New Qty On-Hand (Loc) |
| 10 | mrp_ord_no | MRP Ord # |
| 11 | safety_stk | Safety Stk |
| 12 | po_lead_tm | PO Lead Tm |
| 13 | cus_no | Cust # |
| 14 | oe_ord_no | OE Ord # |
| 15 | par_item_no | Par Item # |
| 16 | org_ord_no | Org Ord # |
| 17 | po_ord_no | PO Ord # |
The grouped column (not shown in the data area) is item_no.
How the layout is applied¶
The implementation works directly on the layout model, mutating Layout.m_LayoutElements. It does not touch the gridview directly — the gridview is rendered from the layout, so configuring the layout is sufficient.
Private Sub ApplyDefaultDrilldownLayout(ByVal layoutInterface As PulseDevTools.DynamicQuery.CLayoutInterface)
If layoutInterface Is Nothing OrElse layoutInterface.Layout Is Nothing Then Exit Sub
'Group by item_no (group header only - item_no is not also added as a visible column).
AddLayoutElement(layoutInterface, "item_no", -1, 0)
Dim szVisibleOrder As String() = New String() {
"imloc_byr_plnr", "item_desc_1", "item_loc", "pur_or_mfg",
"mrp_ord_type", "mrp_trx_dt", "qty_on_hand_il", "mrp_trx_qty",
"mrp_new_qty_il", "mrp_ord_no", "safety_stk", "po_lead_tm",
"cus_no", "oe_ord_no", "par_item_no", "org_ord_no", "po_ord_no"
}
For i As Integer = 0 To szVisibleOrder.GetUpperBound(0)
AddLayoutElement(layoutInterface, szVisibleOrder(i), i, -1)
Next
End Sub
AddLayoutElement is a small helper that resolves a field name to a matrix element ID and either updates an existing layout entry for that ID or appends a new one.
The duplicate-entry pitfall¶
Layout.m_LayoutElements may already contain a layout entry for a field even when nothing is visible or grouped — typically with Visible = -1, Group = -1. If the helper blindly appends a second entry for the same element ID, two distinct lookup paths inside CLayoutInterface see different entries:
ElementLayoutByID(used byCachedElementsandAllElements) returns the first matching entry it finds.GroupedElementsandVisibleElementswalk the fullm_LayoutElementslist and pick up every entry that has the relevant index set.
If those two paths disagree about which CElementLayout is "the" entry for a given element, the rendering can break in subtle ways. The most visible symptom is grouping not showing up, even though the layout list has a m_nGroupIndex >= 0 entry.
The helper avoids this by checking for an existing entry and updating it in place:
Private Sub AddLayoutElement(ByVal layoutInterface As PulseDevTools.DynamicQuery.CLayoutInterface,
ByVal fieldName As String,
ByVal visibleIndex As Integer,
ByVal groupIndex As Integer)
Dim core As DynamicQuery.CQueryMatrix.CSD_2.CElementCore = layoutInterface.Layout.GetElementCoreByFieldName(fieldName)
If core Is Nothing Then Exit Sub
Dim szID As String = core.ID
Dim existing As PulseDevTools.DynamicQuery.CQueryView.CSD_1.CLayoutSet.CElementLayout = Nothing
For Each cur As PulseDevTools.DynamicQuery.CQueryView.CSD_1.CLayoutSet.CElementLayout In layoutInterface.Layout.m_LayoutElements
If cur.m_szID = szID Then
existing = cur
Exit For
End If
Next
If existing Is Nothing Then
existing = New PulseDevTools.DynamicQuery.CQueryView.CSD_1.CLayoutSet.CElementLayout
existing.m_szID = szID
layoutInterface.Layout.m_LayoutElements.Add(existing)
End If
existing.m_nVisibleIndex = visibleIndex
existing.m_nGroupIndex = groupIndex
If groupIndex >= 0 Then
existing.m_nSortOrder = 1
End If
End Sub
Reusable pattern
AddLayoutElement is generic — it does not depend on anything specific to MRP Orders Causing Shortages. It would be a good candidate to lift onto a shared base class if a second module ever needs a similar programmatic default layout.
Group expansion behavior¶
After the drilldown is displayed, one runtime behavior is set on the gridview:
This is a DevExpress gridview behavior, not part of the saved layout. With it on, any group rows that materialize when data changes (initial load, sort, filter, refresh) auto-expand. The user can still collapse a group manually, but a refresh will not collapse groups that were already expanded.
Why not put it in the layout?
The serializable CLayoutSet does not have a member for "auto-expand groups." It is a runtime-only gridview option. Setting it on tempgv after DisplayDrillDown is the right place for it.