I'm about to use frames to create something similar to an MDI application.
Frames are shown in panels on the main form.
There could be different frame types, like TMemoFrame or TImageFrame so I have created a TBaseFrame. Some work behind the scene goes on and the baseframe saves the need to duplicate that code in descendant frames.
When you click another frame, it should show that it is selected. Different frames look different so the ancestor and descendant share the responsibility to switch to selected.
To select/switch frame, click it. Then a dispatch method of the frame calls an eventhandler in the main form via an method pointer in the BaseFrame. The event property of each frame is assigned right after the frame is created from the main form unit.
Is this a good approach or would you do it differently?
Simple project attached with only two panels and two frames.
unit uMemoFrame;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, ExtCtrls, StdCtrls, uBaseFrame;
type
{ TfrMemoFrame }
TfrMemoFrame = class(TfrBaseFrame)
Memo: TMemo;
procedure FrameClick(Sender: TObject);
private
{ private declarations }
public
constructor Create(APanel: TPanel); override;
procedure Select; override;
procedure UnSelect; override;
end;
implementation
{$R *.lfm}
uses
Graphics;
{ TfrMemoFrame }
procedure TfrMemoFrame.FrameClick(Sender: TObject);
begin
{Executes the callback in mainform}
{using the methodpointer in TfrBaseFrame}
DoSelect;
end;
constructor TfrMemoFrame.Create(APanel: TPanel);
begin
inherited Create(APanel);
Memo.Color:=clSilver;
end;
procedure TfrMemoFrame.Select;
begin
{Called from mainform via callback}
Inherited; //Sets Selected to True
{Unique settings for a MemoFrame}
Memo.Color:=clDefault;
Memo.Font.Size:=15;
Memo.Lines.Add('Selected');
end;
procedure TfrMemoFrame.UnSelect;
begin
{Called from mainform via callback}
Inherited; //Sets Selected to False
{Unique settings for a MemoFrame}
Memo.Color:=clSilver;
Memo.Lines.Clear;
end;
end.
unit uBaseFrame;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, ExtCtrls;
type
TfrBaseFrame = class;
TSelectEvent = procedure(AFrame: TfrBaseFrame) of object;
{ TfrBaseFrame }
TfrBaseFrame = class(TFrame)
private
FOnSelect: TSelectEvent; //Method Pointer
FSelected: Boolean;
protected
procedure DoSelect; //Dispatch Method
public
constructor Create(APanel: TPanel); overload; virtual;
procedure Select; virtual;
procedure UnSelect; virtual;
property Selected: Boolean read FSelected write FSelected;
property OnSelect: TSelectEvent read FOnSelect write FOnSelect;
end;
implementation
{$R *.lfm}
{ TfrBaseFrame }
procedure TfrBaseFrame.DoSelect;
begin
if not Selected then
if Assigned(FOnSelect) then
FOnSelect(Self);
end;
constructor TfrBaseFrame.Create(APanel: TPanel);
begin
inherited Create(nil);
Parent:= APanel;
Name:='';
end;
procedure TfrBaseFrame.Select;
begin
FSelected:= True;
end;
procedure TfrBaseFrame.UnSelect;
begin
FSelected:= False;
end;
end.
unit uMain;
{$mode objfpc}{$H+}
interface
uses
Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, ComCtrls,
Menus, ExtCtrls, uBaseFrame, uMemoFrame;
type
{ TfrmMain }
TfrmMain = class(TForm)
ImageList1: TImageList;
MenuItem1: TMenuItem;
pnlLeft: TPanel;
pmFrames: TPopupMenu;
pnlRight: TPanel;
Splitter1: TSplitter;
StatusBar: TStatusBar;
ToolBar1: TToolBar;
tbAddMemoFrame: TToolButton;
tbFrames: TToolButton;
procedure FormCreate(Sender: TObject);
procedure FormResize(Sender: TObject);
private
{ private declarations }
public
CurrentFrame: TfrBaseFrame;
procedure SetActiveFrame(AFrame: TfrBaseFrame);
end;
var
frmMain: TfrmMain;
implementation
{$R *.lfm}
{ TfrmMain }
procedure TfrmMain.FormCreate(Sender: TObject);
var
aFrame: TfrBaseFrame;
begin
{Create first MemoFrame, assign eventhandler}
with TfrMemoFrame.Create(pnlLeft) do
OnSelect:=@SetActiveFrame;
{Create second MemoFrame, assign eventhandler, and select frame}
aFrame:= TfrMemoFrame.Create(pnlRight);
aFrame.OnSelect:=@SetActiveFrame;
SetActiveFrame(aFrame);
end;
procedure TfrmMain.FormResize(Sender: TObject);
var
panelwidth: integer;
begin
{Resize panels when form is resized}
panelwidth:= round(Width / 2);
pnlLeft.Width:=panelwidth;
end;
procedure TfrmMain.SetActiveFrame(AFrame: TfrBaseFrame);
begin
{If assigned, unselect old frame, select last clicked frame}
if Assigned(CurrentFrame) then
begin
if CurrentFrame <> AFrame then
begin
CurrentFrame.UnSelect; //Deselect old frame
CurrentFrame:= AFrame;
CurrentFrame.Select; //Then select chosen frame
StatusBar.SimpleText:= TPanel(CurrentFrame.Parent).Name;
end;
end
else
begin
{CurrentFrame not assigned so just assign and select}
CurrentFrame:= AFrame;
CurrentFrame.Select;
StatusBar.SimpleText:= TPanel(CurrentFrame.Parent).Name;
end;
end;
end.