Moving a VCL application to FireMonkey (FMX) - to run it on macOS, a phone or just with a
modern look - is mostly a long list of small, mechanical changes. Caption
becomes Text, Left becomes Position.X,
Vcl.StdCtrls becomes FMX.StdCtrls, hundreds of times over. That
part suits an AI coding assistant such as Claude Code very well.
The rest is not mechanical: code that draws on a canvas, calls the Windows API, or relies on data-aware controls has to be redesigned, not translated. An assistant will translate it anyway, and the result usually compiles. This article is about splitting the work so the assistant does the first part and you keep control of the second.
Give the assistant the .dfm and .pas files and it will write the
.fmx and the new unit. These changes it gets right nearly every time - all the
FMX names below were checked by compiling them with Delphi 13:
| VCL | FMX |
|---|---|
Vcl.StdCtrls, Vcl.Forms ... | FMX.StdCtrls, FMX.Forms ... |
{$R *.dfm} | {$R *.fmx} |
Caption on labels, buttons, check boxes | Text |
Checked on check boxes and radio buttons | IsChecked |
Left, Top (Integer) | Position.X, Position.Y (Single) |
Align := alClient | Align := TAlignLayout.Client |
TPageControl / TTabSheet | TTabControl / TTabItem |
TComboBox with typing allowed | TComboEdit |
ComboBox.Text of a drop-down list | ComboBox.Selected.Text |
Image.Picture | Image.Bitmap |
Position := poScreenCenter | Position := TFormPosition.ScreenCenter |
Put this table, and any others you build up, in the project's CLAUDE.md, so
each form is converted the same way.
In FMX the style decides a control's font unless you say otherwise. Setting
TextSettings.Font.Size has no visible effect while Size is still in
StyledSettings:
Title_Label.StyledSettings := Title_Label.StyledSettings - [TStyledSetting.Size, TStyledSetting.Style];
Title_Label.TextSettings.Font.Size := 14;
Title_Label.TextSettings.Font.Style := [TFontStyle.fsBold];
A converted form whose bold headings all come out plain has usually missed this. The same applies to font colour and family.
A VCL TColor is stored as $00BBGGRR; an FMX
TAlphaColor is $AARRGGBB. clRed is
$000000FF and TAlphaColors.Red is $FFFF0000. Copying a
numeric colour across unchanged swaps red and blue, and with an alpha of zero the colour is
fully transparent, so it disappears. Ask for every colour constant to be converted
explicitly. The article on
converting web colours to Delphi shows the
byte order.
Owner-drawn grids, custom charts and anything in an OnPaint handler need more
than renaming. FMX drawing happens between BeginScene and
EndScene, uses Fill and Stroke instead of
Brush and Pen, takes Single coordinates in a
TRectF, and draws text with FillText instead of
TextOut:
if Canvas.BeginScene then
try
Canvas.Fill.Color := TAlphaColors.Black;
Canvas.FillText(RectF(4, 2, 200, 22), 'Total', False, 1, [], TTextAlign.Leading);
finally
Canvas.EndScene;
end;
An assistant can make that translation, but whether the result looks right - line widths, half-pixel offsets, text that is clipped - is only visible when you run it. Expect to adjust every owner-drawn control by eye.
Message handlers (procedure WMSize(var Msg: TWMSize); message WM_SIZE;),
PostMessage, Handle, the registry and other Windows API calls either
do not exist in FMX or only work on Windows. If the goal is other platforms, each one needs a
decision, not a translation: TThread.Queue or TMessageManager in
place of posted messages, an ini or JSON file in place of the registry, and so on. Ask the
assistant to list all of these before converting anything, then decide each one
yourself.
FMX has no TDBGrid, TDBEdit or TDBNavigator. Controls
are connected to datasets with LiveBindings instead, which is a different design, not a
different spelling. An assistant asked to convert a data-aware form will produce
LiveBindings, but check each binding in the LiveBindings Designer - a binding that points at
the wrong field shows nothing, without an error.
ShowModal and MessageDlg wait for an answer on desktop platforms,
but not on Android, where code after the call keeps running. Code meant to run on a phone
needs the versions that take an anonymous method, such as
TDialogService.MessageDialog, with the follow-up code moved inside it.
Components without an FMX version cannot be converted at all, and an assistant may substitute something plausible without saying so. Have it list every component class on each form first, and mark which ones have an FMX equivalent.
For reading a form file outside the IDE - to check what an assistant changed, or to see a form's structure at a glance - our DFM Viewer shows a .dfm or .fmx file as a component tree with its properties.
StyledSettings), colours (byte order and
alpha), canvas drawing