Automating UI Upgrades with a Delphi Form Converter

Delphi Form Converter: From VCL to FMX — Step-by-StepConverting Delphi forms from the Visual Component Library (VCL) to the FireMonkey (FMX) framework is a common task when modernizing legacy Windows-only applications for cross-platform deployment (Windows, macOS, iOS, Android, and Linux). This guide walks through the process step-by-step, explains key differences between VCL and FMX, identifies common pitfalls, and offers practical examples and tools to speed migration while keeping behavior and appearance as close as possible.


Why convert VCL forms to FMX?

  • Cross-platform reach: FMX supports native deployment across multiple operating systems; VCL is Windows-only.
  • Modern UI capabilities: FMX provides GPU-accelerated rendering, flexible styling, and resolution-independent layouts.
  • Future-proofing: Newer Delphi features and third-party components increasingly target FMX.

High-level migration strategy

  1. Inventory and prioritize forms and units to migrate.
  2. Separate platform-agnostic logic (business, data access, algorithms) from UI code.
  3. Convert UI forms and components, replicating layout and behavior with FMX equivalents.
  4. Replace or wrap VCL-only components with FMX versions or third-party substitutes.
  5. Rework event handling, painting, and custom drawing for FMX paradigms.
  6. Test incrementally on each target platform and adjust for platform-specific behavior.

Key differences between VCL and FMX

  • VCL is a thin wrapper around the Windows API; FMX is a cross-platform, GPU-accelerated framework.
  • FMX uses a scene graph for rendering; VCL relies on GDI/GDI+.
  • FMX components are styled via TStyleBook and stylesheets rather than Windows controls and themes.
  • Coordinate and layout systems differ: FMX uses anchors, alignments, and layouts (TLayout, TGridPanelLayout), and supports vector/scaled drawing for high-DPI.
  • Event model is similar but certain events and properties differ (e.g., OnPaint vs. OnPaint/OnRender differences).
  • FMX components often use different property names and types (e.g., TColor management, TBitmap classes).

Step 1 — Preparation and planning

  • Create a backup of the project and source control branch for migration work.
  • Build a list of forms sorted by complexity and business priority. Start with simple forms to build patterns.
  • Identify VCL-only third-party components and plan replacements. Note which units reference VCL-specific units (Vcl.*).
  • Separate core logic into Delphi units without UI references if not already done. This reduces duplication during UI rewrite.

Step 2 — Project and unit setup

  • Create a new FMX project (multi-device application) or convert the existing project by adding FMX platform support.
  • Add FMX units to uses clauses where appropriate. Typical FMX units: FMX.Forms, FMX.Types, FMX.Controls, FMX.StdCtrls, FMX.Layouts, FMX.Objects, FMX.Dialogs, FMX.Graphics.
  • Remove or guard VCL-only units with conditional compilation if preserving cross-framework shared code:
{$IFDEF MSWINDOWS} uses   Vcl.Forms, Vcl.Controls; {$ELSE} uses   FMX.Forms, FMX.Controls; {$ENDIF} 
  • Prefer isolating UI code in form units so core modules remain framework-agnostic.

Step 3 — Translating forms (.dfm to .fmx)

There’s no guaranteed automatic converter that perfectly maps all VCL forms to FMX because of conceptual and component differences. However, strategies and tools can help:

  • Use a form converter tool (if available) as a starting point, then manually refine.
  • Recreate forms in the FMX form designer: create a new FMX form and add FMX components to match layout and behavior.
  • For simple forms, you can translate the .dfm properties into .fmx manually by mapping component names and basic properties (Size, Position, Align, Anchors, Caption, Hint).

Key mapping examples:

  • TButton (VCL) -> TButton (FMX) (similar, but styling and default behavior differ)
  • TLabel -> TLabel (FMX) (careful with word wrap and autosize behavior)
  • TEdit -> TEdit (FMX) (focus and keyboard handling varies on mobile)
  • TMemo -> TMemo (FMX) or TRichEdit equivalent (FMX has TRichEdit in some versions)
  • TImage -> TImage (FMX) but FMX.TBitmap has different pixel formats and loading behavior
  • TPanel -> TLayout or TPanel (FMX has both; TLayout is non-visual container)
  • TGrid/TStringGrid -> TGrid (FMX) — API differs; consider third-party grids for parity
  • Menu components -> TPopupMenu and platform-appropriate menus; main menu paradigms differ on mobile

Step 4 — Layout, scaling, and styling

  • Replace pixel-based positioning with FMX layouts (TLayout, TGridPanelLayout, TFlowLayout, TStackLayout) for automatic resizing and multi-resolution support.
  • Use Align and Anchors, but prefer responsive layouts for different screen sizes.
  • Use TStyleBook to define application styles and component appearances. FMX styles are XML-based and control the visual tree.
  • Fonts and DPI: FMX is device-independent; test font sizes on target devices and adjust style resources.

Practical layout example: replace nested TPanels used for positioning with TGridPanelLayout to keep proportional sizing across different aspect ratios.


Step 5 — Events, input, and navigation

  • Review event handlers: OnClick, OnChange, OnKeyDown/Up exist, but mobile input and virtual keyboard behavior require special handling.
  • Modal dialogs: TForm.ShowModal works in FMX but may have platform-specific behavior (e.g., mobile platforms often discourage modal dialogs).
  • Focus and tab order: FMX handles focus differently; use TabOrder and TabStop, and test keyboard navigation on desktop.
  • Touch gestures: Add FMX.Gestures units and handle OnGesture or use TTapGestureRecognizer, TPanGestureRecognizer, etc., for mobile interactions.

Step 6 — Graphics, custom drawing, and animations

  • FMX uses TCanvas with vector capabilities and supports GPU-accelerated effects.
  • Replace VCL OnPaint custom drawing with FMX.OnPaint or use TPath, TShape, and TPaintBox for drawing operations.
  • Images: use FMX.Graphics.TBitmap and be mindful of pixel formats. For high-DPI, provide multiple image resolutions or use scalable vector graphics where possible.
  • Animations: FMX has built-in animation classes (TFloatAnimation, TColorAnimation) and transitions; use them instead of custom timers for smoother effects.

Example: converting a custom owner-drawn list:

  • VCL: Custom draw in OnDrawItem.
  • FMX: Use TListBox with custom item appearance (TListBoxItem and custom content) or TListView with appearance classes for virtualized performance.

Step 7 — Data-aware components and bindings

  • LiveBindings in FMX provide a modern binding system (BindSourceDB, TBindingsList) that differs from VCL’s DB-aware controls.
  • Replace data-aware controls (TDBGrid, TDBEdit) with FMX equivalents using LiveBindings or third-party components.
  • Consider decoupling data layer and using ViewModel patterns to simplify UI binding across frameworks.

Step 8 — Third-party components and platform APIs

  • Identify VCL-only third-party components and find FMX counterparts (or wrap them via platform services if necessary).
  • For platform-specific features (registry access, shell integration), use platform services or conditional compilation to call Windows APIs on Windows and appropriate APIs on other OSes.
  • Some features (OLE, COM) are Windows-specific and may require rethinking for cross-platform goals.

Step 9 — Testing and platform-specific adjustments

  • Test on every target platform early and often. Small UI assumptions (font metrics, scroll behavior, modal semantics) can differ.
  • Mobile: watch memory usage, navigation patterns (use multi-form navigation or mobile-styled navigation), and input methods.
  • macOS/Linux: verify file dialogs, menus, and default control behavior.
  • Windows: confirm backwards compatibility for behavior that users expect from the original VCL app.

Common pitfalls and how to handle them

  • Expect visual differences: exact pixel-perfect replication is often impossible; aim for functional parity and consistent UX.
  • Performance issues with complex FMX controls: prefer virtualized lists (TListView) and reduce overdraw; profile GPU vs CPU.
  • Missing third-party controls: budget time to find replacements or rewrite UI parts.
  • Threading and synchronization: FMX’s rendering may require UI updates on the main thread; use TThread.Synchronize or TThread.Queue appropriately.
  • Differences in file formats: .dfm (VCL) and .fmx (FMX) are not directly interchangeable; manual conversion or recreation is common.

Tools and utilities

  • Delphi IDE form designer (FMX designer) — primary tool for rebuilding forms.
  • Third-party migration tools (where available) — can speed basic property translation but always verify output.
  • LiveBindings Designer — helps wiring data sources to FMX controls.
  • Version control and continuous integration — test builds for each platform target.

Example: Converting a simple login form

VCL login form components:

  • TForm (FormLogin)
  • TLabel (lblUser, lblPassword)
  • TEdit (edtUser)
  • TEdit (edtPassword, PasswordChar := ‘*’)
  • TButton (btnOK, btnCancel)

FMX equivalent steps:

  1. Create new FMX form (TForm).
  2. Add TLabel, TEdit, and TButton from the FMX component palette.
  3. For password input set edtPassword.Password := True.
  4. Use a TGridPanelLayout with two rows and two columns for labels and edits, and a TFlowLayout for buttons to handle different screen sizes.
  5. Wire OnClick handlers to btnOK and btnCancel similarly. Ensure virtual keyboard behavior is tested on mobile.

Sample code snippets

Creating and showing an FMX form from code:

uses   FMX.Forms, FMX.Types, FMX.Controls, FMX.StdCtrls, FMX.Edit, FMX.Layouts; procedure ShowLogin; var   LoginForm: TForm;   Grid: TGridPanelLayout;   edtUser, edtPass: TEdit;   btnOK, btnCancel: TButton; begin   LoginForm := TForm.Create(nil);   try     LoginForm.Width := 360;     LoginForm.Height := 200;     Grid := TGridPanelLayout.Create(LoginForm);     Grid.Parent := LoginForm;     Grid.Align := TAlignLayout.Client;     Grid.RowCount := 3;     Grid.ColumnCount := 2;     edtUser := TEdit.Create(Grid);     edtUser.Parent := Grid;     edtUser.Text := '';     Grid.ControlCollection.Controls[0].Control := edtUser;     edtPass := TEdit.Create(Grid);     edtPass.Parent := Grid;     edtPass.Password := True;     Grid.ControlCollection.Controls[1].Control := edtPass;     btnOK := TButton.Create(LoginForm);     btnOK.Parent := LoginForm;     btnOK.Text := 'OK';     btnOK.Align := TAlignLayout.Bottom;     btnCancel := TButton.Create(LoginForm);     btnCancel.Parent := LoginForm;     btnCancel.Text := 'Cancel';     btnCancel.Align := TAlignLayout.Bottom;     LoginForm.ShowModal;   finally     LoginForm.Free;   end; end; 

Note: the above code is illustrative; when building in the FMX designer you’ll typically place and configure components visually.


Migration checklist

  • [ ] Backup and create migration branch
  • [ ] Inventory forms and components
  • [ ] Decouple business logic from UI
  • [ ] Create FMX project and set target platforms
  • [ ] Recreate forms in FMX designer or use converter tool
  • [ ] Replace or wrap third-party VCL components
  • [ ] Re-implement custom drawing with FMX canvas
  • [ ] Implement LiveBindings or alternative data binding
  • [ ] Test on all target platforms
  • [ ] Optimize UI, images, and performance

When to keep VCL instead of migrating

  • Your application requires deep Windows-only integration (COM/OLE, shell extensions, Windows services).
  • Migration costs outweigh cross-platform benefits.
  • You have large codebase with many VCL-only third-party components with no FMX replacement.

Final notes

Migrating from VCL to FMX is often more a rewrite of UI layers than a direct conversion — treat it as an opportunity to clean up UI code, adopt responsive layouts, and modernize user experience. Start small, invest in tooling and component replacements, and iterate with frequent cross-platform testing to ensure a smooth transition.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *