You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Delphi 10.2 Firemonkey非模态控制窗体创建及访问违例解决

解决Firemonkey中非模态控制窗体的创建问题

问题核心原因

Firemonkey框架里,普通TForm窗体不能设置Parent属性——FMX的TForm是设计为独立窗口的组件,强行设置Parent会破坏其内部窗口管理逻辑,要么导致窗体无法显示,要么触发访问违例。你之前的代码错误地给控制窗体指定了Parent,这是问题根源。

正确实现方案

  1. 创建窗体时不设置Parent
    直接以Application作为所有者创建控制窗体,保留其独立窗口的特性。
  2. 通过交互控件控制显示/隐藏
    因为控制窗体无需始终显示,建议在主窗体添加菜单、按钮等控件,通过点击事件来切换控制窗体的显示状态,避免在FormCreate中直接Show(此时主窗体可能未完成初始化)。
  3. 建立参数实时同步机制
    在控制窗体的调整控件(如滑块、输入框)的变更事件中,通过自定义事件回调的方式通知主窗体更新TImage的位图参数,实现实时生效。

完整代码示例

主窗体代码

unit MainForm;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls,
  FMX.Controls.Presentation, FMX.Objects, ControlForm;

type
  TMainForm = class(TForm)
    Image1: TImage;
    btnToggleControl: TButton;
    procedure FormCreate(Sender: TObject);
    procedure btnToggleControlClick(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    FControlForm: TControlForm;
    procedure UpdateImageEffect(Brightness: Integer; Contrast: Integer);
  public
  end;

var
  MainForm: TMainForm;

implementation

{$R *.fmx}

procedure TMainForm.FormCreate(Sender: TObject);
begin
  BorderStyle := TFmxFormBorderStyle.Sizeable;
  WindowState := TWindowState.wsMaximized;
  // 创建控制窗体实例,不设置Parent
  FControlForm := TControlForm.Create(Application);
  // 绑定参数变更事件,实现实时同步
  FControlForm.OnParamsChanged := UpdateImageEffect;
end;

procedure TMainForm.btnToggleControlClick(Sender: TObject);
begin
  // 切换控制窗体的显示/隐藏状态
  if FControlForm.Visible then
    FControlForm.Hide
  else
    FControlForm.Show;
end;

procedure TMainForm.UpdateImageEffect(Brightness: Integer; Contrast: Integer);
begin
  // 这里实现位图参数调整逻辑,示例仅做演示框架
  if Assigned(Image1.Bitmap) then
  begin
    // 可调用FMX图像API调整亮度、对比度,比如使用TColorEffect
    // 示例代码:
    // with TColorEffect.Create(Image1) do
    // begin
    //   Parent := Image1;
    //   Brightness := Brightness / 100;
    //   Contrast := Contrast / 100;
    // end;
    Image1.Repaint;
  end;
end;

procedure TMainForm.FormDestroy(Sender: TObject);
begin
  // 销毁控制窗体
  FControlForm.Free;
end;

end.

控制窗体代码

unit ControlForm;

interface

uses
  System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.StdCtrls,
  FMX.Controls.Presentation, FMX.SpinBox;

type
  // 自定义参数变更事件类型
  TParamsChangedEvent = procedure(Brightness, Contrast: Integer) of object;

  TControlForm = class(TForm)
    spnBrightness: TSpinBox;
    spnContrast: TSpinBox;
    lblBrightness: TLabel;
    lblContrast: TLabel;
    procedure spnBrightnessChange(Sender: TObject);
    procedure spnContrastChange(Sender: TObject);
  private
    FOnParamsChanged: TParamsChangedEvent;
    procedure NotifyParamsChanged;
  public
    property OnParamsChanged: TParamsChangedEvent read FOnParamsChanged write FOnParamsChanged;
  end;

var
  ControlForm: TControlForm;

implementation

{$R *.fmx}

procedure TControlForm.NotifyParamsChanged;
begin
  if Assigned(FOnParamsChanged) then
    FOnParamsChanged(Round(spnBrightness.Value), Round(spnContrast.Value));
end;

procedure TControlForm.spnBrightnessChange(Sender: TObject);
begin
  NotifyParamsChanged;
end;

procedure TControlForm.spnContrastChange(Sender: TObject);
begin
  NotifyParamsChanged;
end;

end.

关键注意事项

  • 禁止给FMX窗体设置Parent:只有TPanel、TLayout这类容器控件才能作为其他控件的父容器,TForm作为独立窗口,设置Parent会引发内部逻辑错误。
  • 延迟显示窗体:不要在主窗体FormCreate事件中直接Show控制窗体,此时主窗体可能未完成初始化流程,容易触发异常。
  • 事件回调更安全:通过自定义事件实现两个窗体间的通信,比全局变量、直接访问控件的方式更符合模块化设计,也更稳定。

内容的提问来源于stack exchange,提问作者user3384674

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 00:37:37