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)
You can use either Frames or Forms.
procedure TMyForm.AddPage( AFormClass : TFormClass );
var
LForm : TForm;
begin
LForm := AFormClass.Create( Self );
LForm.ManualDock( PageControl1, nil, alClient );
LForm.Show;
end;
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
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.
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.
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.
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:
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
.
instantiate the Frame object in your code at run-time and then set its Parent
property to be the desired TTabSheet
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With