How do I iterate the Enum captions in Business Central 365

3.2k views Asked by At

Enums are/will be the Option replacements for Business Central 365. Recently I had an occasion to use a few and get my feet wet, so to speak. As seems to be the case far too often, about 80% of the functionality you need is readily available, but the remaining 20% takes more work than it should.

In the case of Enums, you get a Text list of Names and an Integer list of associated Ordinal values, but you do NOT get a list of Captions. In the partial example Enum FourStates below, Default, OH and TX are the Names, 0, 1 and 2 are the Ordinals and space, Ohio and Texas are the captions. Note that Ordinals are the defined numeric values, NOT the indexes. Perfectly valid Ordinals in the example below could be 1, 5 and 7.

value(0; Default) { Caption = ' '; }
value(1; OH) { Caption = 'Ohio'; }
value(2; TX) { Caption = 'Texas'; }

If you define a Table or Page field as an Enum, then the captions are displayed in the dropdowns. To get the Caption you can use Format(EnumType::Name) but we needed to iterate all of the Captions for a given Enum.

2

There are 2 answers

0
Yuri M. On

I have implemented a better workaround for BC14. That should work on newer versions also, but I have tested on BC14 only.

var
  RecRef: RecordRef;
  FRef: FieldRef;
  OptionNames: List of [Text];
  OptionCaptions: List of [Text];
  i: Integer;

RecRef.GetTable(Rec); // Some record
FRef := RecRef.Field(20); // Some field of type Enum
OptionNames := FRef.OptionMembers().Split(',');
OptionCaptions := FRef.OptionCaption().Split(',');
for i := 1 to OptionNames.Count() do begin
  Evaluate(FRef, OptionNames.Get(i));
  Message('Ordinal = %1\Name = %2\Caption = %3',
    format(FRef, 0, 9),
    OptionNames.Get(i),
    format(FRef, 0, 1)); // or OptionCaptions.Get(i)
end;
0
j2associates On

After digging around in some blogs and the documentation, here is a summary of what I found.

  1. First, there is a major limitation because Captions can ONLY be processed within the context of an Enum type and, at least as of now, the Business Central 365 SaaS solution has no support for generic Enums (e.g. EnumRef like RecordRef and FieldRef). This means a Text list of Captions must be created on an Enum by Enum basis.

  2. However, once the Captions list has been created, then using the combination of Name, Ordinal and Caption Lists of [Text], you can then write general purpose code that will work for any Enum.

  3. A Gotcha, when you use the TEnum code snippet in VS Code, it defaults the first element to 0. We have learned to either make that the Default as the partial code above does, or change it to 1. The reason for this is because if there is any chance that you will ever use an Enum with the StrMenu command, StrMenu defaults 0 as the Cancel return value. In that case, you could never select the 0 Ordinal Enum because ti would be treated as Canceled.

  4. To create an instance of an Enum to use within your code, create a Var variable similar to MyChoices: Enum "Defined Choices";. If you also define a MyChoice: Enum: "Defined Choices", you can compare them with something like if MyChoice = MyChoices::FirstChoice then, etc.

  5. Below is some sample code with a few different Enum styles along with a method that allows you to create a List of [Text] for the Captions. Again, that has to be coded for each specific Enum. a. Within VS Code, use AL Go to create a new HelloWorld app b. Change the app.json file Name and Publisher, I named mine EnumHelper NOTE: We always define a Publisher because you cannot filter for Default Publisher within SaaS c. Replace all of the code inside the HelloWorld.al file with the code below NOTE: To simplify things, everything below is all in the same file d. The code is a PageExtension for the Chart of Accounts Page and runs off the OnOpenPage trigger This allows the code to be easily called without requiring a bunch of setup code.

  6. Here is the code that allows you to create a Captions list. The variable myOrdinal is an Integer and Flagged is a defined Enum. Note the Enum::EnumName, similar to Page::PageName or Database::TableName.

     foreach myOrdinal in Flagged.Ordinals do begin
         // Enum definition, NOT an enum instance.
         captions.Add(Format(Enum::Flagged.FromInteger(myOrdinal)));
     end;
    
  7. All of the code (sorry, it didn't format exactly correctly)

enum 50200 FourStates { Extensible = true;

value(0; Default) { Caption = ' '; }
value(1; OH) { Caption = 'Ohio'; }
value(2; TX) { Caption = 'Texas'; }
value(3; NC) { Caption = 'North Carolina'; }
value(4; IA) { Caption = 'Iowa'; }
value(5; MO) { Caption = 'Missouri'; }

}

enum 50201 Flagged { Extensible = true;

value(0; Default) { Caption = ' '; }
value(1; Bold) { Caption = 'Bold'; }
value(2; ITalic) { Caption = 'Italid '; }
value(4; Underline) { Caption = 'Underline'; } 
value(8; BoldItalic) { Caption = 'Bold & Italic'; }
value(16; BoldUnderline) { Caption = 'Bold & Underline '; }
value(32; ItalicUnderline) { Caption = 'Italic & Underline'; }
value(64; All3Options) { Caption = 'All 3 Options'; }

}

enum 50202 Randomized { Extensible = true;

value(0; Default) { Caption = ' '; }
value(7; Good) { Caption = 'The Good'; }
value(5; Bad) { Caption = 'The Bad'; }
value(11; Ugly) { Caption = 'The Ugly'; }

}

enum 50203 ProcessFlowOptions { Extensible = true;

value(0; Default) { Caption = ' '; }
value(1; Flagged) { Caption = 'Flagged'; }
value(2; Randomized) { Caption = 'Randomized'; }
value(4; FourStates) { Caption = 'FourStates'; }

}

pageextension 50200 "Chart of Accounts EH" extends "Chart of Accounts" { var // Enum instance variables. myFlagged: Enum Flagged; myFourStates: Enum FourStates; myRandomized: Enum Randomized;

trigger OnOpenPage();
begin
    case UserID.ToLower() of
        'larry':
            Message('Hello Larry, this is an extension for October testing.');
        'vicki':
            Message('Good morning Vicki, this is an extension for October testing.');
        else
            if Confirm('Hello %1 from EnumHelper.\\Click Yes to process or no to cancel.', true, UserID) then begin
                ProcessEnumerations();
            end;
    end;
end;

local procedure ProcessEnumerations()
var
    allLines: TextBuilder;
    randomCaptions: List of [Text];
    flaggedCaptions: List of [Text];
    fourStatesCaptions: List of [Text];
begin
    GetEnumCaptions(randomCaptions, flaggedCaptions, fourStatesCaptions);
    IterateEnumNamesOrdinalsAndCaptions(allLines, randomCaptions, flaggedCaptions, fourStatesCaptions);
    Message(allLines.ToText());
end;

local procedure GetEnumCaptions(randomCaptions: List of [Text]; flaggedCaptions: List of [Text]; fourStatesCaptions: List of [Text])
begin
    GetCaptions(randomCaptions, ProcessFlowOptions::Randomized);
    GetCaptions(flaggedCaptions, ProcessFlowOptions::Flagged);
    GetCaptions(fourStatesCaptions, ProcessFlowOptions::FourStates);
end;

local procedure IterateEnumNamesOrdinalsAndCaptions(allLines: TextBuilder; randomCaptions: List of [Text]; flaggedCaptions: List of [Text]; fourStatesCaptions: List of [Text])
begin
    IterateEnumNamesOrdinalsAndCaptions('Flagged Enum', allLines, myFlagged.Names, myFlagged.Ordinals, flaggedCaptions);
    IterateEnumNamesOrdinalsAndCaptions('Randomized Enum', allLines, myRandomized.Names, myRandomized.Ordinals, randomCaptions);
    IterateEnumNamesOrdinalsAndCaptions('FourStates Enum', allLines, myFourStates.Names, myFourStates.Ordinals, fourStatesCaptions);
end;

local procedure IterateEnumNamesOrdinalsAndCaptions(title: Text; allLines: TextBuilder; enumNames: List of [Text]; enumOrdinals: List of [Integer]; enumCaptions: List of [Text])
var
    i: Integer;
    enumLine: TextBuilder;
    enumLines: TextBuilder;
begin
    allLines.AppendLine(title);
    allLines.appendLine();
    for i := 1 to enumNames.Count do begin
        Clear(enumLine);
        enumLine.AppendLine('EnumName: ''' + enumNames.Get(i) + ''',');
        enumLine.AppendLine('EnumOrdinal: ' + Format(enumOrdinals.Get(i)) + ',');
        enumLine.AppendLine('EnumCaption: ''' + enumCaptions.Get(i) + '''.');
        //enumLine.AppendLine('EnumName: ''' + enumNames.Get(i) + ''', EnumOrdinal: ' + ordinal + ', EnumCaption: ''' + enumCaptions.Get(i) + '''');
        enumLines.AppendLine(enumLine.ToText());
    end;
    allLines.AppendLine(enumLines.ToText());
end;

local procedure GetCaptions(captions: List of [Text]; processFlowOption: Enum ProcessFlowOptions)
var
    myOrdinal: Integer;
    myProcessFlowOptions: Enum ProcessFlowOptions;
begin
    // Load captions by iterating specific Enums.
    case processFlowOption of
        myProcessFlowOptions::Flagged:
            begin
                foreach myOrdinal in Flagged.Ordinals do begin
                    // Enum definition, NOT an enum instance.
                    captions.Add(Format(Enum::Flagged.FromInteger(myOrdinal)));
                end;
            end;
        myProcessFlowOptions::Randomized:
            begin
                foreach myOrdinal in Randomized.Ordinals do begin
                    // Enum definition, NOT an enum instance.
                    captions.Add(Format(Enum::Randomized.FromInteger(myOrdinal)));
                end;
            end;
        myProcessFlowOptions::FourStates:
            begin
                foreach myOrdinal in FourStates.Ordinals do begin
                    // Enum definition, NOT an enum instance.
                    captions.Add(Format(Enum::FourStates.FromInteger(myOrdinal)));
                end;
            end;
    end;
end;

}

Enjoy