How do I correctly use Visual Form Inheritance in Delphi 10.3/7?

83 Views Asked by At

I have a base form that I am trying to use for multiple forms on screen at once. I thought I set up inheritance correctly but when I create 2 forms, only the last one created shows up on the screen. I also can't get the title string to actually change when passed in with the constructor.

Here is my ancestor form:

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
    { Private declarations }
  public
    { Public declarations }
    procedure SavePosition;
    procedure SetTimerInterval(Interval: Cardinal);
    constructor CreatePanel(Name:String; Title:String; TimerEnabled: Boolean);
  end;

var
  BasePanelForm: TBasePanelForm;
  PanelName : String;
  BaseTimerEnabled: Boolean;
  PanelTitle:String;

implementation

{$R *.dfm}

constructor TBasePanelForm.CreatePanel(Name: String; Title:String; TimerEnabled: Boolean);
begin
  inherited Create(application);
  PanelName := Name;
  if Title = '' then
    Title := PanelName;
  PanelTitle := Title;
  BaseTimerEnabled := TimerEnabled;
end;

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

procedure TBasePanelForm.FormCreate(Sender: TObject);
begin
  self.MainPanel.Text := '<img src="idx:1"> ' + PanelTitle;
  BaseTimer.Enabled := BaseTimerEnabled;
  MainPanel.OptimizePaint := True;
  MainPanel.DoubleBuffered := True;
end;

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

Here is one of the forms that inherit it:

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
    { Private declarations }
  public
  end;

var
  WeatherPanelForm: TWeatherPanelForm;
  URL:String;
implementation

{$R *.dfm}
constructor TWeatherPanelForm.CreatePanel(url_string:String);
begin
  inherited CreatePanel('weather','Weather', True);
  URL := url_string;
end;

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

end.

Here is how I create them:

TextPanelForm := TTextPanelForm.CreatePanel;
TextPanelForm.Parent        := MainForm;
TextPanelForm.Top           := 0;
TextPanelForm.Left          := 0;
TextPanelForm.Visible       := true;
TextPanelForm.Show;

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

I tried setting up form inheritance. File->New->Other->Inherited. TextPanelForm and WeatherPanelForm both inherit the basepanelform

I was expecting to be able to instantiate separate instances of the base form. Like setting the title of the panel when I create each form.

Only the weather panel is shown, and the title label I set does not change from what's in the BasePanel by default.

It seems like it is acting like it uses only 1 single instance of the BasePanel and if I change a property in the BasePanel instance at runtime, it doesn't carry out what is shown on screen.

What am I doing wrong?

1

There are 1 best solutions below

0
mait On

I copied your code pretty much as you presented it and it worked fine. I ended up with two forms contained in the main form. You can either create an inherited form or simply use [File | New | VCL Form] to create the form and manually change the class statement from TForm to TBaseForm (and include BasePanel in the uses clause). Either way is fine.

Moving the global variables into the class will avoid any conflict when creating multiple forms. This also includes the "URL" variable in your Weather form.

As for instantiation, as you are creating these forms manually, you will need to go into [Project | Options] and select "Forms" from the TreeView. Move the Weather and Text forms from "Auto-create Forms" to "Available forms". Otherwise you'll end up with two of each. (Only one will be visible because the auto-created form does not call the CreatePanel constructor.)

Base Panel

unit BasePanel;

interface

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

type
  TBasePanelForm = class(TForm)
    MainPanel: TPanel;
    BaseTimer: TTimer;
    procedure FormCreate(Sender: TObject);
  private
  PanelName: String;
  BaseTimerEnabled: Boolean;
  PanelTitle: String;
  public
    procedure SavePosition;
    procedure SetTimerInterval(Interval: Cardinal);
    constructor CreatePanel(Name: String; Title: String; TimerEnabled: Boolean);
  end;

var
  BasePanelForm: TBasePanelForm;

implementation

{$R *.dfm}

constructor TBasePanelForm.CreatePanel(Name: String; Title: String; TimerEnabled: Boolean);
  begin
  inherited Create(application);
  PanelName := Name;
  if Title = '' then
    Title := PanelName;
  PanelTitle := Title;
  BaseTimerEnabled := TimerEnabled;
  end;

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

procedure TBasePanelForm.FormCreate(Sender: TObject);
  begin
  MainPanel.Caption := '<img src="idx:1"> ' + PanelTitle;
  BaseTimer.Enabled := BaseTimerEnabled;
  MainPanel.DoubleBuffered := True;
  end;

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

end.

Weather Panel

unit WeatherPanel;
    
    interface
    
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, BasePanel;
    
    type
      TWeatherPanelForm = class(TBasePanelForm)
        procedure FormCreate(Sender: TObject);
      private
        URL: string;
      public
        constructor CreatePanel(url_string: String);
      end;
    
    var
      WeatherPanelForm: TWeatherPanelForm;
    
    implementation
    
    {$R *.dfm}
    
    constructor TWeatherPanelForm.CreatePanel(url_string: String);
      begin
      inherited CreatePanel('weather', 'Weather', True);
      URL := url_string;
      end;
    
    procedure TWeatherPanelForm.FormCreate(Sender: TObject);
      begin
      inherited;
      MainPanel.Caption := 'Weather Station';
      end;
    
    end.

Main Form

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, BasePanel, WeatherPanel;

type
  TMainForm = class(TForm)
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  MainForm: TMainForm;
  TextPanelForm: TBasePanelForm;

implementation

{$R *.dfm}

procedure TMainForm.FormCreate(Sender: TObject);
  begin
  TextPanelForm := TBasePanelForm.CreatePanel('TextPanel', 'TextPanel Caption', false);
  TextPanelForm.Parent := MainForm;
  TextPanelForm.Top := 0;
  TextPanelForm.Left := 0;
  TextPanelForm.Visible := true;
  TextPanelForm.Show;

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

end.

Note that I have programatically created TextPanelForm and created WeatherPanelForm in the IDE. Both methods are valid depending on your requirements.

enter image description here