2

I have a Windows Vcl Application which have several forms inside. I wanted to standardize the layout for all of these forms. So I wanted to declare some constants which I can apply across all of my .dfm layout file.

For example, this main from is the automatically generated form in the IDE:

object frm_MainForm: Tfrm_MainForm
  Left = 0
  Top = 0
  Caption = 'Main Form'
  ClientHeight = 300
  ClientWidth = 400    
  Color = clBtnFace
end

What I wanted to do is to declare something like:

AllFormHeight = 300
AllFormWidth = 400

So that I can apply to all form like the following:

object frm_MainForm: Tfrm_MainForm
  Left = 0
  Top = 0
  Caption = 'Main Form'
  ClientHeight = AllFormHeight <--- Like this
  ClientWidth = AllFormWidth <--- And this
  Color = clBtnFace
end

I tried doing something almost similar to the color constant in Vcl.Graphics.hpp but it doesn't work. I'm using Embarcadero RAD studio C++ Builder 10.3. I used C++ for the programming and dfm file as the UI file.

VLL
  • 9,634
  • 1
  • 29
  • 54

1 Answers1

3

Custom string identifiers CAN be used in DFMs for integer/enum properties. To do that, you need to call RegisterIntegerConsts() in <System.Classes.hpp> to register your own custom functions that convert between string identifiers and their ordinal values. In your case, converting "AllFormHeight" and "AllFormWidth" strings to specific integer values, and vice versa.

For instance, this is exactly how the DFM example you have shown allows the clBtnFace identifier to be used for the Color property.

Try this:

#include <System.Classes.hpp>
#include <System.TypInfo.hpp>
#include <sysopen.h>

const int AllFormHeight = 300;
const int AllFormWidth = 400;

const TIdentMapEntry MyFormIdents[] = {
    {AllFormHeight, "AllFormHeight"},
    {AllFormWidth, "AllFormWidth"}
};

bool __fastcall MyFormIdentToInt(const String Ident, int &Int)
{
    return IdentToInt(Ident, Int, EXISTINGARRAY(MyFormIdents));
}

bool __fastcall MyIntToFormIdent(int Int, String &Ident)
{
    return IntToIdent(Int, Ident, EXISTINGARRAY(MyFormIdents));
}

// See http://bcbjournal.org/articles/vol3/9908/Registering_AnsiString_property_editors.htm
// for why this function is needed...
TTypeInfo* IntTypeInfo()
{
    TTypeInfo* typeInfo = new TTypeInfo;
    typeInfo->Name = "int";
    typeInfo->Kind = tkInteger;
    return typeInfo;

    /* alternatively:
    TPropInfo* PropInfo = GetPropInfo(__typeinfo(TForm), "ClientHeight");
    return *PropInfo->PropType;
    */
}

RegisterIntegerConsts(IntTypeInfo(), &MyFormIdentToInt, &MyIntToFormIdent);

However, the downside to this approach is that because the ClientHeight/ClientWidth properties are using int as their data type, your custom identifiers will then be applied to ANY int property in ANY streamable class. RegisterIntegerConsts() is typically only used for more unique data types instead, like TColor, TFontCharset, etc.

You can't change the ClientHeight/ClientWidth properties themselves to use a different data type so you have something unique to map your identifiers to. But, you can define your own properties that use your own data type that you can then map. Or, you can try having your Form override the DefineProperties() method to create "fake" properties just for DFM streaming. Either way, you could then optionally redeclare the ClientHeight/ClientWidth properties in your Form class to include the stored=false attribute so they are not streamed in the DFM at all. Have your custom properties read/set the ClientHeight/ClientWidth properties internally.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770