views:

265

answers:

3

Hopefully this is a quick one, and "Easy if you know how"...

I'm writing some kind of Serialization/Scripting class to generate forms on the fly, I tried setting a TColor the other day and got an error clBtnFace is not a valid integer value or something like that and found that the constants used in properties are registered so that they can be converted to integer, and so I added code to fetch the converter and use it.

Now today I have a similar issue with the ModalResult property but I can't work out how the DFM deserializer handles this property? Any ideas how it converts mrOK into an integer?

Edit

There isn't much of an example to give:

PropInfo := GetPropInfo(Instance, PropertyName);
SetPropValue(Instance, PropInfo, PropertyValue);

Where in this case Instance is a TButton, PropertyName is 'ModalResult' and PropertyValue is 'mrOK'

+5  A: 

It doesn't need to:

const
  { Dialog Box Command IDs }
  {$EXTERNALSYM IDOK}
  IDOK = 1;          ID_OK = IDOK;

const
  mrNone     = 0;
  mrOk       = idOk;

type
  TModalResult = Low(Integer)..High(Integer);

TModalResult is in someway a subrange of Integer and mrOK is just an Integer constant.

Uwe Raabe
So's a TColor? yet that needs a conversion?
JamesB
Admittedly you do not need PropertyInfo or GetEnumValue, but I think the dfm stores mrOK, and you would therefore still need some kind of conversion to get from mrOK to 1?
Marjan Venema
I thought wrong... As VilleK checked and reported: the dfm stores integer values.
Marjan Venema
+4  A: 

Don't really want to answer my own question but since no one else has....

There is no converter for ModalResults, Delphi stores the Integer representation in the DFM as VilleK says in the comment to the question. As a solution I've registered a new converter

const
  ModalResults: array[0..10] of TIdentMapEntry = (
    (Value: mrNone; Name: 'mrNone'),           
    (Value: mrOk; Name: 'mrOk'),               
    (Value: mrCancel; Name: 'mrCancel'),       
    (Value: mrAbort; Name: 'mrAbort'),         
    (Value: mrRetry; Name: 'mrRetry'),         
    (Value: mrIgnore; Name: 'mrIgnore'),       
    (Value: mrYes; Name: 'mrYes'),             
    (Value: mrNo; Name: 'mrNo'),               
    (Value: mrAll; Name: 'mrAll'),             
    (Value: mrNoToAll; Name: 'mrNoToAll'),     
    (Value: mrYesToAll; Name: 'mrYesToAll'));



function ModalResultToIdent(ModalResult: Longint; var Ident: string): Boolean;
begin
    Result := IntToIdent(ModalResult, Ident, ModalResults);
end;

function IdentToModalResult(const Ident: string; var ModalResult: Longint): Boolean;
begin
    Result := IdentToInt(Ident, ModalResult, ModalResults);
end;
initialization
    RegisterIntegerConsts(TypeInfo(TModalResult), IdentToModalResult, ModalResultToIdent);
JamesB
Nah, I like your answer. You get some credit for it. And nothing wrong with accepting your own answer either. (After all you need to think of your accept rate as well).
Marjan Venema
I don't understand the purpose of this code. I thought you already established that Delphi doesn't need to convert the string "mrOK" into an integer because it never stores that value in the DFM in the first place. Therefore, Uwe's answer is correct. It seems you're adding code to *make* Delphi store the text name of the ModalResult value, just so that the premise of your question is valid. You're not answering the question you asked.
Rob Kennedy
@Rob - I was using the DFM Serializer as an example as I thought (wrongly) that it converted ModalResults to integers, it doesn't it stores them as integers. I needed a conversion for my own serialization class as I would much rather read mrOk than 1
JamesB
"How does Delphi do X" and "How can I do X" are not the same question. Uwe answered the former; you, the latter. It's a good answer, just not to the question at hand. You should ask another question and then post this as an answer; I'd vote for both.
Rob Kennedy
@Rob I agree they are different questions, I've failed to ask what I actually wanted to know. I've posted it as new quesion
JamesB
+1  A: 

Both of your examples you have given are sub ranges of numeric values. As such RTTI only really knows of the underlying Integer. Other examples include TCursor, TFontCharset and TTabOrder.

If you have a type like this:

  TEnum = (exOne,exTwo,exThree);

You can use RTTI to get and set 'exOne', 'exTwo' and 'exThree' as Strings.

This can be done through these methods in TypInfo.pas

function GetEnumName(TypeInfo: PTypeInfo; Value: Integer): string;
function GetEnumValue(TypeInfo: PTypeInfo; const Name: string): Integer;

If you want to use the constants that are defined for colors or ModalResults you must build your won dictionary of constant name to value, that you could then implement into your own serialization routines.

TColor implements a static dictionary called Colors, which could be used if you only use the 52 colors that it supports.

  Colors: array[0..51] of TIdentMapEntry = (
    (Value: clBlack; Name: 'clBlack'),
    ...
    (Value: clWindowText; Name: 'clWindowText'));

You can then do the following to get the color name.

var
  ColorName : String;
begin
  // Color Value must be between 0 and 51 otherwise index out of bounds
  ColorName := Colors[ColorValue];  
end;

You then could loop through the items in the Colors Array to determine the value for a given name.

Robert Love
This was the source of the confusion, as ModalResult doesn't store the data in this way. If you look in the DFM you can see the color stored as text, but ModalResult is an integer. Hence TColor has the inbuilt IdentToInt converter and ModalResult doesn't
JamesB