Search code examples
delphicross-platformenumerationdelphi-xe6

How to populate memtables from enumarations?


I use memtables to wire enumerated type with comboboxes using LiveBinding.

However I have a lot of them and they way I am doing is way too bad (copy/paste)

For example, I have the following enumeration:

 TEnumResourceType        = (trtApp, trtTab, trtSection, trtField, trtCommand, trtOther);

and for that I created a function to give the string equivalent:

function EnumResourceTypeToStr(AEnum: TNaharEnumResourceType): string;
begin
  case AEnum of
    trtApp     : result := 'Aplicação';
    trtTab     : result := 'Pagina (Tab)';
    trtSection : result := 'Secção';
    trtField   : result := 'Campo';
    trtCommand : result := 'Comando';
    trtOther   : result := 'Outro';
  end;
end;

In a datamodule I place my memtable and I need to populate it, I am using the AFTEROPEN event of the table with the following code:

procedure TDMGlobalSystem.vtResourceTypeAfterOpen(DataSet: TDataSet);
var
  enum : TEnumResourceType;
begin
  inherited;

  for enum := Low(TEnumResourceType) to High(TEnumResourceType) do
    DataSet.InsertRecord([EnumResourceTypeToStr(enum), Ord(enum)]);
end;

All that works, however I need to do that for each new enumaration and I have dozens. Eventually I will need to change my current memtable to other and that is an added concern to automate the process. The current memtable sometimes does not work on Android.

I am looking in a way to automate this process, or using generics, or whatever, that in the DataModule I only need something like: PopulateEnum(Table, Enum);

The best solution would be creating a component inherited from this memtable and somehow define what is the enum required and all the magic happens (including the selection of the enumtostr)


Solution

  • Here is a generic wrapper for enums to get an array of integer,string pair representing the ordinal value and the name for the enums.

    A little test

    program so_24955704;
    
    {$APPTYPE CONSOLE}
    {$R *.res}
    
    uses
      System.SysUtils,
      EnumValueStore in 'EnumValueStore.pas';
    
    type
      TEnumResourceType = ( trtApp, trtTab, trtSection, trtField, trtCommand, trtOther );
    
    procedure PrintEnumValueStore( AEnumValueStore : TEnumValueStore );
    var
      LEnumValuePair : TEnumValuePair;
    begin
      for LEnumValuePair in AEnumValueStore.GetKeyValues do
        begin
          Writeln( LEnumValuePair.Key, '-', LEnumValuePair.Value );
        end;
    end;
    
    procedure TestEnum;
    var
      LEnumValueStore : TEnumValueStore<TEnumResourceType>;
    begin
      LEnumValueStore := TEnumValueStore<TEnumResourceType>.Create;
      try
        // print default names
        PrintEnumValueStore( LEnumValueStore );
    
        WriteLn;
    
        // set the custom names
        LEnumValueStore.SetValue( trtApp, 'Aplicação' );
        LEnumValueStore.SetValue( trtTab, 'Pagina (Tab)' );
        LEnumValueStore.SetValue( trtSection, 'Secção' );
        LEnumValueStore.SetValue( trtField, 'Campo' );
        LEnumValueStore.SetValue( trtCommand, 'Comando' );
        LEnumValueStore.SetValue( trtOther, 'Outro' );
    
        // print the default values
        PrintEnumValueStore( LEnumValueStore );
      finally
        LEnumValueStore.Free;
      end;
    end;
    
    begin
      try
        TestEnum;
      except
        on E : Exception do
          Writeln( E.ClassName, ': ', E.Message );
      end;
      ReadLn;
    
    end.
    

    will produce the following output

    0-App
    1-Tab
    2-Section
    3-Field
    4-Command
    5-Other
    
    0-Aplicação
    1-Pagina (Tab)
    2-Secção
    3-Campo
    4-Comando
    5-Outro
    

    and here is the unit that will do the work

    unit EnumValueStore;
    
    interface
    
    uses
      System.Generics.Collections;
    
    type
      TEnumValuePair = TPair<Integer, string>;
    
      TEnumValueStore = class abstract
      public
        function GetKeyValues : TArray<TEnumValuePair>; virtual; abstract;
      end;
    
      TEnumValueStore<TEnumKey> = class( TEnumValueStore )
      private
        FValueDict : TDictionary<TEnumKey, string>;
      public
        constructor Create;
        destructor Destroy; override;
        procedure SetValue( AKey : TEnumKey; const AValue : string );
        function GetKeyValues : TArray<TEnumValuePair>; override;
      end;
    
    implementation
    
    uses
      SimpleGenericEnum;
    
    { TEnumValueStore<TEnumKey> }
    
    constructor TEnumValueStore<TEnumKey>.Create;
    begin
      inherited Create;
      FValueDict := TDictionary<TEnumKey, string>.Create;
    end;
    
    destructor TEnumValueStore<TEnumKey>.Destroy;
    begin
      FValueDict.Free;
      inherited;
    end;
    
    function TEnumValueStore<TEnumKey>.GetKeyValues : TArray<TEnumValuePair>;
    var
      LEnum : TEnum<TEnumKey>;
      LMin, LMax : Integer;
      LCount : Integer;
      LIdx : Integer;
      LStr : string;
    begin
      LMin := LEnum.Ord( LEnum.Low );
      LMax := LEnum.Ord( LEnum.High );
      LCount := LMax - LMin + 1;
      SetLength( Result, LCount );
    
      LCount := 0;
      for LIdx := LMin to LMax do
        begin
          LEnum := LIdx;
          if FValueDict.ContainsKey( LEnum )
          then
            LStr := FValueDict[LEnum]
          else
            LStr := LEnum;
          Result[LCount] := TEnumValuePair.Create( LEnum, LStr );
          Inc( LCount );
        end;
    end;
    
    procedure TEnumValueStore<TEnumKey>.SetValue( AKey : TEnumKey; const AValue : string );
    begin
      FValueDict.AddOrSetValue( AKey, AValue );
    end;
    
    end.
    

    I use the unit SimpleGenericEnum but there is a small bug inside you need to correct

    class function TEnum<T>.High: T;
    begin
      // original code
      // Result := Cast(_TypeData.MaxValue);
      Result := Cast(GetTypeData.MaxValue);
    end;
    
    class function TEnum<T>.Low: T;
    begin
      // original code
      // Result := Cast(_TypeData.MinValue);
      Result := Cast(GetTypeData.MinValue);
    end;