Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to break up TPageControl tabs into seperate files in Delphi?

I have a VCL form with a five tab TPageControl on it. Each TTabSheet's content is fairly unrelated and has a lot of individual controls and processing logic...so I want to break it up so it's easier to deal with the code for "just that tab". Having a line like

//-------------------------- begin rules tab methods -------------------

just isn't cutting it anymore. Really, I think I'd like each tab in a separate file somehow.

Currently I'm contemplating creating a VCL Frame for each TTabPage. If I did so I'd either need to load all the frames into the TPageControl in the constructor, or when the tab is shown.

Is this a good approach? Would it be better to make a whole TForm for each tab? Should I continue to wrap the tabs in a TPageControl, or should that be changed to a TTabControl if the content is loaded dynamically? If this is a good approach, is it better to load all the tabs on startup, or each time the tab is shown? (perhaps pros/cons if it's not totally obvious which is better in most/all cases)

like image 526
Jessica Brown Avatar asked May 29 '14 23:05

Jessica Brown


2 Answers

You can use either Frames or Forms.

  • With Frames you have to add a TabControl as the parent for each Frame.
  • With Forms you have to dock each Form to the PageControl (the Form caption will automatic be the TabControl Caption).

procedure TMyForm.AddPage( AFormClass : TFormClass );
var
  LForm : TForm;
begin
  LForm := AFormClass.Create( Self );
  LForm.ManualDock( PageControl1, nil, alClient );
  LForm.Show;
end;

Example

Create a base Settings Form

unit UI_Form_SettingBase;

interface

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

type
  TUISettingBase_Form = class( TForm )
  private

  protected
    procedure DoSaveData; virtual;
  public
    function CanSaveData : Boolean; virtual;
    procedure SaveData;
  end;

  TUISettingBase_FormClass = class of TUISettingBase_Form;

var
  UISettingBase_Form : TUISettingBase_Form;

implementation

{$R *.dfm}
{ TUISettingBase_Form }

function TUISettingBase_Form.CanSaveData : Boolean;
begin
  Result := True;
end;

procedure TUISettingBase_Form.DoSaveData;
begin

end;

procedure TUISettingBase_Form.SaveData;
begin
  if CanSaveData
  then
    DoSaveData;
end;

end.

Derive all Settings Forms from that Form and override the DoSaveData and optionally the CanSaveData methods

Common Settings (with a simple CheckBox)

unit UI_Form_SettingCommon;

interface

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

type
  TUISettingCommon_Form = class(TUISettingBase_Form)
    CheckBox1: TCheckBox;
  private

  protected
    procedure DoSaveData; override;
  public

  end;

var
  UISettingCommon_Form: TUISettingCommon_Form;

implementation

{$R *.dfm}

procedure TUISettingCommon_Form.DoSaveData;
begin
  inherited;
  // code to save the data
end;

end.

Connection Settings (with a simple Edit control)

unit UI_Form_SettingConnection;

interface

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

type
  TUISettingConnection_Form = class( TUISettingBase_Form )
    Edit1 : TEdit;
  private
  protected
    procedure DoSaveData; override;
  public
  end;

var
  UISettingConnection_Form : TUISettingConnection_Form;

implementation

{$R *.dfm}
{ TUISettingConnection_Form }

procedure TUISettingConnection_Form.DoSaveData;
begin
  inherited;
  // code to save the data
end;

end.

Putting the pieces together: The real Settings Form

The main Setting Form is also derived from the SettingBase

unit UI_Form_Settings;

interface

uses
  System.Generics.Collections,
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, UI_Form_SettingBase, Vcl.StdCtrls,
  Vcl.ExtCtrls, Vcl.ComCtrls;

type
  TUISettings_Form = class( TUISettingBase_Form )
    PageControl1 : TPageControl;
    Panel1 : TPanel;
    Save_Button : TButton;
  private
    FForms : TList<TUISettingBase_Form>;
    procedure AddSettingPage( ASettingFormClass : TUISettingBase_FormClass );
  protected
    procedure DoSaveData; override;
  public
    function CanSaveData : Boolean; override;
    procedure AfterConstruction; override;
    procedure BeforeDestruction; override;
  end;

var
  UISettings_Form : TUISettings_Form;

implementation

{$R *.dfm}

uses
  UI_Form_SettingCommon, UI_Form_SettingConnection;
{ TUISettings_Form }

procedure TUISettings_Form.AddSettingPage( ASettingFormClass : TUISettingBase_FormClass );
var
  LForm : TUISettingBase_Form;
begin
  LForm := ASettingFormClass.Create( Self );
  try
    LForm.ManualDock( PageControl1, nil, alClient );
    LForm.Show;
    FForms.Add( LForm );
    LForm := nil;
  finally
    LForm.Free;
  end;
end;

procedure TUISettings_Form.AfterConstruction;
begin
  inherited;
  FForms := TList<TUISettingBase_Form>.Create;
  // add all the setting forms
  AddSettingPage( TUISettingCommon_Form );
  AddSettingPage( TUISettingConnection_Form );
end;

procedure TUISettings_Form.BeforeDestruction;
begin
  inherited;
  FForms.Free;
end;

function TUISettings_Form.CanSaveData : Boolean;
var
  LForm : TUISettingBase_Form;
begin
  // iterate all setting forms if they can save the data
  Result := True;
  for LForm in FForms do
    Result := Result and LForm.CanSaveData;
end;

procedure TUISettings_Form.DoSaveData;
var
  LForm : TUISettingBase_Form;
begin
  inherited;
  // iterate all setting forms and save the data
  for LForm in FForms do
    LForm.SaveData;
end;

end.
like image 68
Sir Rufo Avatar answered Nov 03 '22 19:11

Sir Rufo


One thing in particular that I'd like to do (the focus of this question) is to somehow break up a TPageControl containing five TTabSheets. Each tab sheet is a pretty cohesive section of the UI (and corresponding processing logic), so it would be nice to break each tab into separate files.

You can use TFrame for that. Give each page its own Frame, which you can implement and design in their own individual files with their own DFMs.

For each broken up tab, would I want to start with adding new VCL Forms? or VCL Frames?

Frames. Don't use embedded Forms, they don't work very well. Frames were designed for that purpose.

Should I leave the TPageControl or change it to a TTabControl if each tab's content is being loaded from a panel anyhow?

After you create a new Frame and design it as needed, you can then either:

  1. put the Frame on the Component Palette (right-click on the Frame in the Designer and choose 'Add to Palette'), and then you can drop it onto the desired TTabSheet at design-time. Set the Frame's Align to alClient if you want it to fill the entire TTabSheet.

  2. instantiate the Frame object in your code at run-time and then set its Parent property to be the desired TTabSheet.

like image 23
Remy Lebeau Avatar answered Nov 03 '22 21:11

Remy Lebeau