Now we have Enums, but how do we ensure that apps we create with enums are actually extendable? Just because the Enum can be extended does not ensure that the usage of them us truly extendable.
In this video, I discuss the problem when offering users a choice with STRMENU or something similar, a choice that cannot be extended. And then build a solution with RecordRef/FieldRef to make a generic Enum selector page:

In this video, Erik tackles a common challenge in Business Central development: how to let users select a value from an Enum dynamically, without hardcoding the options into a string menu. The key insight is that if someone later adds an Enum Extension, a hardcoded menu would break — but a dynamic approach using RecordRef and FieldRef handles extensions gracefully.
The Problem with Hardcoded Enum Selection
Erik describes a classic scenario: you have a field that used to be an Option field (now an Enum), and you need to present the user with a selection dialog. The traditional approach would be to build a string menu listing all the known values. But what happens if someone else comes along and adds an Enum Extension? Your hardcoded menu would break — it wouldn’t include the new values.
This got Erik thinking: why not build a generic “Select an Enum” page that dynamically reads all available enum values at runtime?
Understanding the Building Blocks
The Integer Virtual Table
Business Central has several types of tables:
- Normal tables — stored in the database (GL Entries, Customers, Items, etc.)
- Temporary tables — in-memory copies of a table structure
- Virtual tables — tables with data that doesn’t sit in the database
The Integer table is a virtual table containing all records from roughly minus 2 billion to plus 2 billion. It’s perfect when you need to show a specific number of lines and keep track of index positions — exactly what we need for enumerating enum values.
FieldRef Enum Functions
The FieldRef type in AL includes several functions for working with enums dynamically:
GetEnumValueNameFromOrdinalValue()— returns the display name for a given ordinal valueGetEnumValueOrdinal()— returns the ordinal value for a given indexEnumValueCount()— returns the total number of enum values
These functions are the key to building a truly dynamic enum selector.
Building the Select Enum Page
The core of the solution is a generic list page backed by the Integer virtual table. Here’s the complete implementation:
page 50131 "Select an Enum"
{
PageType = List;
Caption = 'Select...';
UsageCategory = None;
SourceTable = Integer;
layout
{
area(Content)
{
repeater(rep)
{
field(value; EnumValue(Rec.Number))
{
Caption = 'Value';
ApplicationArea = all;
}
}
}
}
local procedure EnumValue(Number: Integer): Text
begin
exit(F.GetEnumValueNameFromOrdinalValue(F.GetEnumValueOrdinal(Number)));
end;
procedure GetSelectedEnum(): Integer
begin
exit(F.GetEnumValueOrdinal(Rec.Number));
end;
procedure SetupPage(TableNo: Integer; FieldNo: Integer)
var
SelectLbl: Label 'Select ';
begin
Ref.OPEN(TableNo);
F := Ref.Field(FieldNo);
Setrange(Number, 1, F.EnumValueCount());
CurrPage.Caption(SelectLbl + F.Caption());
end;
var
Ref: RecordRef;
F: FieldRef;
}
Let’s walk through the key parts:
The SetupPage Procedure
This is the entry point for configuring the page. It accepts a table number and field number, then:
- Opens a
RecordRefto the specified table - Gets a
FieldRefto the specified field (which must be an Enum field) - Sets a range on the Integer table from 1 to the total enum value count — this limits the repeater to show exactly the right number of rows
- Updates the page caption dynamically to read “Select ” followed by the field’s caption
An important note: the index for enum values when using these functions starts at 1, not 0. This is why the range begins at 1.
The EnumValue Function
This function translates the Integer table’s Number field (which serves as our index) into the display name of the corresponding enum value. It uses a two-step process: first getting the ordinal value from the index, then getting the human-readable name from that ordinal value.
The GetSelectedEnum Function
When the user makes a selection, this function returns the actual ordinal value of the selected enum — not the index. This distinction matters because enum ordinal values can have gaps (e.g., 0, 1, 2, … 53, then jumping to 84).
Testing the Solution
Erik created a simple test page to exercise the enum selector:
page 50130 "Test Enum Select"
{
trigger OnOpenPage()
var
p: Page "Select an Enum";
N: Integer;
begin
repeat
clear(p);
p.SetupPage(77, 1);
p.LookupMode(true);
if p.RunModal() = Action::LookupOK then begin
N := p.GetSelectedEnum();
Message('You selected %1', N);
end;
until 4 = 5;
end;
}
This test page opens the enum selector for table 77 (Report Selections), field 1 (Usage) — an enum with values that include gaps in their ordinal numbering (values go from 0 through 53, then jump to 84 and beyond). The repeat...until 4 = 5 loop keeps the dialog running so you can test multiple selections without restarting.
One important detail Erik discovered during testing: the clear(p) call at the top of the loop is essential. Without it, the global variables (RecordRef and FieldRef) from the previous run persist, causing errors when trying to reopen the page.
Test Results
The testing confirmed the solution works correctly:
- The page caption dynamically displayed “Select Usage”
- Selecting “Sales Order” returned ordinal value 1
- Selecting a value with a higher ordinal (like one of the entries in the gap) returned 87
- Another selection returned 10
This proves the page correctly handles enums with non-contiguous ordinal values.
Why This Approach Matters
The beauty of this solution is its resilience to change:
- If a colleague adds new values to the enum, the selection page automatically includes them
- If another extension with a dependency on yours adds an Enum Extension, those new values will also appear in the dialog
- You never need to update hardcoded string menus when enum values change
Summary
By combining the Integer virtual table with RecordRef and FieldRef enum functions, you can build a fully dynamic enum selection page in Business Central. This approach is future-proof against Enum Extensions, requires no maintenance when enum values change, and can be reused across your entire application for any enum field. It’s a clean example of how the reflection capabilities in AL (through RecordRef and FieldRef) can solve real-world extensibility challenges.