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

Delphi 10.3/7可视化窗体继承异常:多实例显示与标题失效问题

问题原因与解决方案

核心错误:全局变量误用导致实例数据共享

你的代码中所有子窗体"共享"数据、标题不生效的根本原因,是把实例专属的状态存在了单元级全局变量里,而非类的成员变量中。每次创建新窗体时,这些全局变量会被新值覆盖,导致所有实例都使用最后一次赋值的内容,看起来像是共享了同一个基窗体实例。

具体问题点

  1. 基窗体中的PanelName、PanelTitle、BaseTimerEnabled是全局变量,不是类的成员,所有子窗体实例都会读写同一个值。
  2. WeatherPanel中的URL也是全局变量,创建多个WeatherPanel时,后续的URL会覆盖前面的实例数据。
  3. 单元自动生成的全局窗体变量(如BasePanelForm、WeatherPanelForm)容易被误用,混淆实例管理逻辑。

修复步骤

1. 改造基窗体:将全局变量改为类成员

把基窗体的全局变量移到类内部,变为每个实例独立拥有的字段:

unit BasePanel;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  OvcBase, StdCtrls, ExtCtrls, MySBox, AdvGroupBox, AdvOfficeImage, EnJpgGr,
  BorderPanel, AdvPanel, Grids, AdvObj, BaseGrid, AdvGrid, AdvUtil;

type
  TBasePanelForm = class(TForm)
    MainPanel: TAdvPanel;
    BaseTimer: TTimer;
    procedure FormCreate(Sender: TObject);
  private
    // 改为类私有成员变量,每个实例独立存储
    FPanelName: String;
    FPanelTitle: String;
    FBaseTimerEnabled: Boolean;
  public
    procedure SavePosition;
    procedure SetTimerInterval(Interval: Cardinal);
    constructor CreatePanel(Name: String; Title: String; TimerEnabled: Boolean);
  end;

// 移除原全局变量:PanelName、PanelTitle、BaseTimerEnabled
var
  BasePanelForm: TBasePanelForm; // 保留但建议手动管理实例,不要依赖它

implementation

{$R *.dfm}

constructor TBasePanelForm.CreatePanel(Name: String; Title: String; TimerEnabled: Boolean);
begin
  inherited Create(Application);
  FPanelName := Name;
  FPanelTitle := IfThen(Title = '', Name, Title);
  FBaseTimerEnabled := TimerEnabled;
end;

procedure TBasePanelForm.SetTimerInterval(Interval: Cardinal);
begin
  BaseTimer.Interval := Interval;
end;

procedure TBasePanelForm.FormCreate(Sender: TObject);
begin
  // 使用实例专属的成员变量
  MainPanel.Text := '<img src="idx:1"> ' + FPanelTitle;
  BaseTimer.Enabled := FBaseTimerEnabled;
  MainPanel.OptimizePaint := True;
  MainPanel.DoubleBuffered := True;
end;

procedure TBasePanelForm.SavePosition;
begin
  //Save the positions to ini
  BaseTimer.Interval := 1;
end;
end.

2. 改造子窗体:将全局变量改为类成员

以WeatherPanel为例,把全局的URL改为实例字段:

unit WeatherPanel;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes,
  Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, BasePanel, Vcl.ExtCtrls,
  AdvPanel, AdvCustomControl, AdvWebBrowser;

type
  TWeatherPanelForm = class(TBasePanelForm)
    WebBrowser: TAdvWebBrowser;
    constructor CreatePanel(url_string: String);
    procedure BaseTimerTimer(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    // 实例专属的URL字段
    FURL: String;
  public
  end;

var
  WeatherPanelForm: TWeatherPanelForm; // 建议手动管理实例,避免依赖全局变量

implementation

{$R *.dfm}

constructor TWeatherPanelForm.CreatePanel(url_string: String);
begin
  inherited CreatePanel('weather','Weather', True);
  FURL := url_string;
end;

procedure TWeatherPanelForm.FormCreate(Sender: TObject);
begin
  inherited;
  WebBrowser.Navigate(FURL);
end;

end.

3. 正确管理窗体实例

创建窗体时,确保每个实例都有独立的变量存储,避免依赖单元全局变量:

var
  TextPanel: TTextPanelForm;
  WeatherPanel1: TWeatherPanelForm;
begin
  TextPanel := TTextPanelForm.CreatePanel;
  TextPanel.Parent := MainForm;
  TextPanel.Top := 0;
  TextPanel.Left := 0;
  TextPanel.Visible := True;
  TextPanel.Show;

  WeatherPanel1 := TWeatherPanelForm.CreatePanel('url-here');
  WeatherPanel1.Parent := MainForm;
  WeatherPanel1.Top := 300;
  WeatherPanel1.Left := 500;
  WeatherPanel1.Visible := True;
  WeatherPanel1.Show;
end;

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 00:32:51