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)
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;