Link to home
Start Free TrialLog in
Avatar of prefix
prefix

asked on

Is this inherited? how?

Hi.
    I asked someone for a question, and he showed me some code, and I read and understood, but I found one thing here:
    Form1.OnClose := MyOnClose;
    This will make the Form1.OnClose event call MyOnClose procedure instead of its OnClose procedure. But this will occur another issue, some forms need to execute something in its OnClose procedure, so I cannot ignore it, but I need every form call MyOnClose when it close, how? Is this inherited? I cannot modify so many forms - too many forms, so, how to inherit the form1(and other forms in this case) its own OnClose and then call MyOnClose?
    Thank you!!
Avatar of LukA_YJK
LukA_YJK
Flag of Azerbaijan image

As far as I understand now you wrote in all special froms FormM1...FormMk this code OnClose := MyOnClose (maybe in OnCreate)
ASKER CERTIFIED SOLUTION
Avatar of BAlexandrov
BAlexandrov

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Avatar of steve_hsk
steve_hsk

Hi Prefix ...

Unfortunately you cannot inherit event handlers, but only methods, functions and procedures.

Secondly, there exists no hidden or default functionality that you need to inherit from the onClose event occuring.

So if you wanted to use a generic MyOnClose handler, in each form's constructor you would need to setup the handler :

Constructor TForm1.Create (...);
Begin
   OnClose := OnMyClose;
End;

Constructor TForm2.Create (...);
Begin
   OnClose := OnMyClose;
End;

PROCEDRUE MyOnClose (Sender : TObject);
BEGIN  
   // Handle your specific requirements here
   If Sender = Form1 THEN
   BEGIN
      // Specific stuff ...
      // This is the main form, so close the app
      Application.Terminate ;
   END
   ELSE If Sender = Form2 THEN
   BEGIN
      // This is not the main form
      Form2.CLose ;
      // etc etc ...
   END;  
END;

There is always an alternative, and you could capture the close message for each form, and then use a generic handler too !

PROCEDURE WCCLOSE (VAR Msg : Tmessage); MESSAGE WM_CLOSE;

PROCEDURE TForm1.WCCLOSE (VAR Msg : Tmessage);
BEGIN
     // 1.0 Ask the user whether vthey want to quit the app
     IF MessageDlg(MSGTITLE +#13+#10+
                   'Request for application closure. Proceed with termination ?'
                   , mtConfirmation,
                   [mbYes, mbNo],
                   0) = mrYes then
     BEGIN
          // 1.1 Finally End Application's Instance
          Application.Terminate ;
     END;
END;

Hope this helps,
STeve
Hi,

You can inherit it:

// Unit1.TForm1: ancestor of all your other forms:
type
  TForm1 = class(TForm)
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

implementation

{$R *.DFM}

// this is your MyOnClose procedure
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  ShowMessage('Form1.Close');
end;

// A descendant form
uses Unit1; // where TForm1 is declared

type
  TForm2 = class(TForm1) // TForm1 instead of TForm
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form2: TForm2;

implementation

{$R *.DFM}

procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
  ShowMessage('Form2.Close');
  inherited; // calls Form1.OnClose
end;

You may exclude Form1 from autocreation list of the project (Form1 is not the main form). Form1 will never be created and used as a form. Its purpose is to add some functionality to other forms that inherit from TForm1.

Regards, Geo
if this is from q about TabControl then you must change a bit class design. in mdichild form add a property TabControl and operate on it in child form. use this form as base form for all mdichild forms (inherit from it). in inherited forms you can safely use onClose etc. events.

type
  TForm2 = class(TForm)
    Label1: TLabel;
    procedure FormActivate(Sender: TObject);
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
  private
    FTab: TTabControl;
  public
    property Tab: TTabControl read FTab write FTab;
  end;

var
  Form2: TForm2;

implementation

{$R *.DFM}

procedure TForm2.FormActivate(Sender: TObject);
var
  idx: Integer;
begin
  Caption := TimeToStr(now);
  if assigned(Tab) then begin
    idx := Tab.Tabs.IndexOfObject(Sender);
    if idx > -1 then
      Tab.TabIndex := idx;
  end;
end;

procedure TForm2.FormClose(Sender: TObject; var Action: TCloseAction);
var
  idx: Integer;
begin
  Action := caFree;
  if assigned(Tab) then begin
    idx := Tab.Tabs.IndexOfObject(Sender);
    if idx > -1 then
      Tab.Tabs.Delete(idx);
  end;
end;

in unit1:

procedure TForm1.StartTheForm(TheForm: TFormClass);
var
  FormName: TForm2;
begin
  FormName := TForm2.Create(Self);
  FormName.Tab := TabControl1;
  FormName.Show;
  TabControl1.Tabs.AddObject(FormName.Caption, FormName);
  TabControl1.TabIndex := pred(TabControl1.Tabs.Count);
end;

wbr, mo.
Avatar of prefix

ASKER

This really works!! Thanks!
Hi prefix,

You asked how to inherit TForm.OnClose and I showed you how this could be done.

Accepted answer works, of course, but:

1. there is no inheritance at all.

2. needs 3 changes in every form:
- add: a declaration of OrigOnClose;
- add: OrigOnClose := Form1.OnClose; in OnCreate
- add: if Assigned(...) then ...; in OnClose.
My example (with inheritance) needs 3 chages also:
- change: TForm to TForm1 (you may give it a name as you wish) in the definition of the forms;
- remove: Form1.OnClose := MyOnClose; from OnCreate;
- add: inherited; in OnClose.

You can achieve the desired result with two lines less and let OOP work for you.

Regards, Geo
... and change the .dfm files of all forms to begin with "inherited"...
BAlexandrov, don't understand me wrong. Your code is correct and works well (I already said that). I'm talking about using OOP features and let them work for us. If the questioner doesn't want to use inheritance there is even easier way: in OnClose event of every form add one extra line at the end:

MyOnClose(Sender,Action);

and remove OnClose := MyOnClose; in OnCreate event.

Regards, Geo
Of course your way is far more clear, but the questioner do not want (or minimise) modifying existing forms. And with your sollution he must update also the dfm files - you have forgot to mention that.

He is a beginner and want fast solltuon - let him choose what him want.

regards,
Bojidar Alexandrov
I can't understand. Why should he have to modify any dfm files manually? Changing
 
FormX = class(TForm)
to
FormX = class(TGenericForm)

is enough.

Regards, Geo
It may work but is not correct.

When you inherit forms you must update also the first line of dfm and change "object" to "inherited"

Do it visually and see yourself the result form.
I've never done so and I've never had problems doing so.
And you never have understand what you do and never have more than one level of inheritance.

You have a dfm file attached at TForm level and if you want the successors to be correct they must inherit this dfm too, else you end up with two classes used same dfm and what if you want the successor to be different?

Make it OOP way!
in MDI interface accepted answer will work until you close last form. remaining forms closing can raise access violations as saved event handler refers to last opened form instance..
you can of course maintain some list of event handlers, but that's so ugly :)
OOP way is to design classes with complete functionality enclosed in it. i.e. you assign to MDIChild TabControl property and MDIChild knows how to operate on it in case of activation and closing (and on Showing, too)

wbr, mo.
That's your opinion. I have as many levels of inheritance as I want, of course. And every form class has its own dfm file.
My previous post was addressed to BAlexandrov.
Avatar of prefix

ASKER

Hi, everyone here:
   First thanks for all of your help. I accepted BAlexandrov's comment as answer because in my case, I have already done some code in this procedure which is in my MDIMainForm:
procedure OnMDIChildClose(Sender: TObject; var Action: TCloseAction)

I used this procedure to set the ChildForm := nil, and remove the associated tab in the tabcontrol.

This procedure is for all of my MDIChildForms, so, it's very easy to add more one line of code in this procedure.

And the biggest problem was, there are more than 20 Childforms in my project, geobul's mothod is an OOP way but it would take much work in this case. - But i used BAlexandrov's method, I only modified my MDIMainForm, add a few code,  that's already done(it works correctly). Even modifying one ChildForm - it's needn't.

So at last, i preferred to BAlexandrov's comment.

geobul and mocarts's comment I have read and I could understand, thank you all the same.
Wow, easy, men, I'm still getting comment notifications ;) Glad to see you all. I think BAlexandrov is right in sence of that all initial settings are stored in .dfm files, even for Event properties. Geobul, your solution is beautiful as always...
By the way, everybody say WELCOME to BAlexandrov! Bojidar = God's Gift ;)