Delphi 10.2 Firemonkey非模态控制窗体创建及访问违例解决
解决Firemonkey中非模态控制窗体的创建问题
问题核心原因
Firemonkey框架里,普通TForm窗体不能设置Parent属性——FMX的TForm是设计为独立窗口的组件,强行设置Parent会破坏其内部窗口管理逻辑,要么导致窗体无法显示,要么触发访问违例。你之前的代码错误地给控制窗体指定了Parent,这是问题根源。
正确实现方案
- 创建窗体时不设置Parent
直接以Application作为所有者创建控制窗体,保留其独立窗口的特性。 - 通过交互控件控制显示/隐藏
因为控制窗体无需始终显示,建议在主窗体添加菜单、按钮等控件,通过点击事件来切换控制窗体的显示状态,避免在FormCreate中直接Show(此时主窗体可能未完成初始化)。 - 建立参数实时同步机制
在控制窗体的调整控件(如滑块、输入框)的变更事件中,通过自定义事件回调的方式通知主窗体更新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
相关产品推荐
相关产品推荐

