问题
I'm using TObjectBindSourceAdapter
to use livebindings with an object.
One of the properties of the object i'm using with TObjectBindSourceAdapter
has an enumerated type, but the field in the adapter is never generated when i use an enumerated type in my object
The Only solution i have found for now is to define the enumerated type as an integer in my object and typecast it. This seems to work fine but you have to keep type casting from and back the enumerated type and integers.
Here is some example code to explain what i mean.
First example which uses the enumerated type that i tried initially and does not seem to work:
uses Data.Bind.ObjectScope;
Type
TMyEnumtype = (meOne, meTwo, meThree);
TMyObject = class
public
MyEnumType: TMyEnumtype;
end;
procedure TForm9.But1Click(Sender: TObject);
var
MyObject: TMyObject;
aBindSourceAdapter: TBindSourceAdapter;
begin
MyObject := TMyObject.Create;
MyObject.MyEnumType := meTwo;
aBindSourceAdapter := TObjectBindSourceAdapter<TMyObject>.Create(nil, MyObject, False);
if aBindSourceAdapter.FindField('MyEnumType') <> nil then
ShowMessage('MyEnumType found')
else
showmessage('MyEnumType not found');
FreeAndNil(MyObject);
FreeAndNil(aBindSourceAdapter);
end;
Second example that seems to work by typecasting to integers
uses Data.Bind.ObjectScope;
Type
TMyEnumtype = (meOne, meTwo, meThree);
TMyObject = class
public
MyEnumType: integer;
end;
procedure TForm9.But1Click(Sender: TObject);
var
MyObject: TMyObject;
aBindSourceAdapter: TBindSourceAdapter;
aEnumType : TMyEnumtype;
begin
MyObject := TMyObject.Create;
MyObject.MyEnumType := Integer(meTwo);
aBindSourceAdapter := TObjectBindSourceAdapter<TMyObject>.Create(nil, MyObject, False);
if aBindSourceAdapter.FindField('MyEnumType') <> nil then
ShowMessage('MyEnumType found')
else
showmessage('MyEnumType not found');
aEnumType := TMyEnumtype(aBindSourceAdapter.FindField('MyEnumType').GetTValue.AsInteger);
if aEnumType = meTwo then
showmessage('meTwo');
FreeAndNil(MyObject);
FreeAndNil(aBindSourceAdapter);
end;
I was wondering if someone else had come across this problem and if there is perhaps some other solution to solve this without reverting to integers and keep using the enumerated types. I'm also not sure if my workaround is the common way to do this or not.
回答1:
I believe the best way is to register a converter. It turns out to be very easy, but only after digging through the VCL source code. I didn't find any useful documentation. But here it is.
unit MyConverters;
interface
uses System.Rtti, System.Bindings.Outputs;
type
TMyEnum = (Value1, Value2, Value3);
implementation
procedure RegisterConverters;
begin
TValueRefConverterFactory.RegisterConversion(TypeInfo(TMyEnum), TypeInfo(string),
TConverterDescription.Create(
procedure(const InValue: TValue; var OutValue: TValue)
var
MyEnum: TMyEnum;
S: string;
begin
MyEnum := InValue.AsType<TMyEnum>;
case MyEnum of
Value1: S := 'First Value';
Value2: S := 'Second Value';
Value3: S := 'Third Value';
else S := 'Other';
end;
OutValue := TValue.From<string>(S);
end,
'TMyEnumToString',
'TMyEnumToString',
'', // TODO what is the AUnitName param used for?
True, // TODO what is ADefaultEnabled used for? What does it mean?
'Converts a TMyEnum value to a string',
nil)
);
end;
initialization
RegisterConverters;
end.
In a nutshell, you call TValueRefConverterFactor.RegisterConversion()
and pass in:
- The type that this converter converts FROM
- The type that this converter converts TO
- A TConverterDescription that contains an anonymous procedure to actually perform the conversion along with some other metadata.
In the above code, the initialization
section calls RegisterConverters
, so all that is necessary is to include the unit in your project and the live bindings framework will use the converter whenever it needs to convert a TMyEnum
value to a string
.
回答2:
Casting enums as integers and back is really the appropriate way to accommodate for this. Let's say for example...
type
TMyEnum = (meOne, meTwo, meThree);
As you already demonstrate, these can be casted as integers. When casting as an integer, it uses the index in which each one is listed in the definition. So...
0 = meOne
1 = meTwo
2 = meThree
You would cast TMyEnum
as Integer
like...
Something := Integer(MyEnumValue);
and then cast it back like...
Something := TMyEnum(MyIntegerValue);
This is widely used to solve your exact issue, and I use it all the time myself. I investigated the same scenario long ago and came to the conclusion that it's really the only way to do this - unless you want to do some more sophisticated conversion such as using strings...
function MyEnumToStr(const MyEnum: TMyEnum): String;
begin
case MyEnum of
meOne: Result:= 'meOne';
meTwo: Result:= 'meTwo';
meThree: Result:= 'meThree';
end;
end;
function StrToMyEnum(const Str: String): TMyEnum;
var
S: String;
begin
S:= UpperCase(Str);
if S = 'MEONE' then Result:= meOne
else if S = 'METWO' then Result:= meTwo
else if S = 'METHREE' then Result:= meThree;
end;
(I'm sure there are other ways of using if
statements for StrToMyEnum
)
Using strings in this manner can make things more readable. A more real-world example...
type
TCustomerType = (cmRetail, cmWholesale, cmDesigner);
where...
cmRetail = 'Retail Customer'
cmWholesale = 'Wholesale Customer'
cmDesigner = 'Designer Customer'
来源:https://stackoverflow.com/questions/16943387/how-to-correctly-use-an-enumerated-type-with-livebindings-tobjectbindsourcead