Lazarus

Programming => General => Topic started by: jojo86 on January 29, 2019, 02:02:57 pm

Title: Affect many buttons to class with events ?
Post by: jojo86 on January 29, 2019, 02:02:57 pm
Hi,
I just want to know if it's possible to affect many buttons to a class.
For exemple, I would want to create a class module with many procedure like OnEnter, OnExit, OnClick...

Today If I want to do that, I can use a specific unit with my specifics procedures (OnEnter, click...) and I still have to add on all my buttons :
Code: Pascal  [Select][+][-]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. begin
  3.     MyUnit.ClickTheButton(Sender);
  4. end;      
  5.  
  6. procedure TForm1.Button1MouseUp(Sender: TObject; Button: TMouseButton;
  7.   Shift: TShiftState; X, Y: Integer);
  8. begin
  9.   MyUnit.MouseUp(Sender, Button, Shift, X, Y);
  10. end;  
  11.  

It's really long...
How can I do with a class module to say "Button1 = MySPecificClass" and MySpecificClass could store all registered buttons to activate events as necessary ??

I hope that you could understand my need.

Thanks,
Title: Re: Affect many buttons to class with events ?
Post by: balazsszekely on January 29, 2019, 02:24:50 pm
I'm not sure if I understood you correctly, but if I did you need something like this:
Code: Pascal  [Select][+][-]
  1. procedure TfMain.FormCreate(Sender: TObject);
  2. begin
  3.   Button1.OnClick := @MyUnit.ClickTheButton;
  4.   Button1.OnMouseUp := @MyUnit.MouseUp;
  5.  
  6.   Button2.OnClick := @MyUnit.ClickTheButton;
  7.   Button2.OnMouseUp := @MyUnit.MouseUp;
  8. end;
Title: Re: Affect many buttons to class with events ?
Post by: jojo86 on January 29, 2019, 02:37:11 pm
Thank you, but I don't want to specify for each events from each buttons... I just want to register my buttons to a class with procedures...

The result is the same but it's easier to use a class module...

I can do that with VBA with a class module and a collection... But I can't find the solution to do that in lazarus...
Title: Re: Affect many buttons to class with events ?
Post by: balazsszekely on January 29, 2019, 02:41:29 pm
You have a though request  :). How about this:
Code: Pascal  [Select][+][-]
  1. procedure SetButtons(const AControl: TControl);
  2. var
  3.   I: Integer;
  4. begin
  5.   if (AControl = nil) then
  6.     Exit;
  7.   if AControl is TWinControl then
  8.   begin
  9.     if (TWinControl(AControl) is TButton) and ((TWinControl(AControl) as TButton).Tag = 0) then
  10.     begin
  11.      (TWinControl(AControl) as TButton).OnClick := @MyUnit.ClickTheButton;
  12.      (TWinControl(AControl) as TButton).OnMouseUp := @MyUnit.MouseUp;
  13.     end;
  14.     for I := 0 to TWinControl(AControl).ControlCount - 1 do
  15.       SetButtons(TWinControl(AControl).Controls[I]);
  16.   end;
  17. end;
  18.  

and you have to call it once for the form:
Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormCreate(Sender: TObject);
  2. begin
  3.   {It will register the events for each buttons on the form with "Tag" property zero.
  4.   Since tag is zero by default, every button will be registered by default. If you wish to exclude a button,
  5.   just change its Tag to a custom value.}
  6.   SetButtons(Form1);
  7. end;
Title: Re: Affect many buttons to class with events ?
Post by: jojo86 on January 29, 2019, 04:14:55 pm
Ok, that's a good proposal, but with your code, we have to loop on each components form...
And I can't store datas to a class module...
Title: Re: Affect many buttons to class with events ?
Post by: balazsszekely on January 29, 2019, 05:44:43 pm
Quote
Ok, that's a good proposal, but with your code, we have to loop on each components form...
I'm afraid there is no other way. What's wrong with the loop?

Quote
And I can't store datas to a class module...
You have to create a class in MyUnit.
Title: Re: Affect many buttons to class with events ?
Post by: jojo86 on January 29, 2019, 06:07:11 pm
Quote
Ok, that's a good proposal, but with your code, we have to loop on each components form...
I'm afraid there is no other way. What's wrong with the loop?

Quote
And I can't store datas to a class module...
You have to create a class in MyUnit.

Yes sorry, you are right, but I have another problem with your method.
I've got an error :
Error: Incompatible types: got "<address of procedure(TObject);Register>" expected "<procedure variable type of procedure(TObject) of object;Register>"

I put this code in a specific unit :
Code: Pascal  [Select][+][-]
  1. Unit Functions;
  2. {$mode objfpc}{$H+}
  3.  
  4. interface
  5.  
  6. uses
  7.   Classes, SysUtils, IBDatabase, IBQuery, Forms, Filtrer, Buttons, Dialogs,
  8.   types, LCLIntf,
  9.   FileUtil, Controls, Graphics, StdCtrls, ExtCtrls, IniFiles, ShellApi, Windows, ComCtrls, Menus,
  10.   strutils, Printers, fpstypes, fpspreadsheet, fpsallformats, fpsexport, FGL; //, fpstypes,  fpsallformats, fpsexport;
  11.  
  12.  
  13. Procedure SetImgZoom(TrackBar: TTrackBar);
  14. Procedure TrackBarChange(Sender:TOBject);
  15.  
  16. type
  17.     TZoomCls = class
  18.     Form: TComponent;
  19.     TrackBar: TTrackBar;
  20.   end;                    
  21.    
  22. Var MyZoom: TZoomCls; //No list yet, just a test
  23.  
  24. procedure SetImgZoom(TrackBar: TTrackBar);
  25. begin
  26.   MyZoom:= TZoomCls.Create;
  27.   MyZoom.Form:=TrackBar.Owner;
  28.   MyZoom.TrackBar:= TrackBar;
  29.  
  30. [color=red][b]  TrackBar.OnChange := @TrackBarChange;[/b] //Error with this line[/color]
  31. end;
  32.  
  33. procedure TrackBarChange(Sender: TOBject);
  34. begin
  35. Showmessage('You Changed Me !!!');
  36. end;    
  37.  
  38.  

Thanks for helping me.
Title: Re: Affect many buttons to class with events ?
Post by: balazsszekely on January 29, 2019, 06:38:04 pm
As I mentioned in my previous post,  procedure ClickTheButton and MouseUp must be part of a class, like this:
Code: Pascal  [Select][+][-]
  1. //your custom unit
  2. type
  3.   TMyClass = class
  4.   private
  5.     procedure ClickTheButton(Sender: TObject);
  6.     procedure MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  7.   public
  8.     constructor Create;
  9.     destructor Destroy; override;
  10.   end;
  11.  
  12. var
  13.   MyClass: TMyClass;
  14.  
  15. procedure TMyClass.ClickTheButton(Sender: TObject);
  16. begin
  17.  
  18. end;
  19.  
  20. procedure TMyClass.MouseUp(Sender: TObject; Button: TMouseButton;
  21.   Shift: TShiftState; X, Y: Integer);
  22. begin
  23.  
  24. end;
  25.  
  26. constructor TMyClass.Create;
  27. begin
  28.   //do some initialization if needed, create objects, etc...
  29. end;
  30.  
  31. destructor TMyClass.Destroy;
  32. begin
  33.   //free object here
  34.   inherited Destroy;
  35. end;
  36.  
  37. //form's unit
  38. procedure SetButtons(const AControl: TControl);
  39. var
  40.   I: Integer;
  41. begin
  42.   if (AControl = nil) then
  43.     Exit;
  44.   if AControl is TWinControl then
  45.   begin
  46.     if (TWinControl(AControl) is TButton) and ((TWinControl(AControl) as TButton).Tag = 0) then
  47.     begin
  48.      (TWinControl(AControl) as TButton).OnClick := @Myclass.ClickTheButton;
  49.      (TWinControl(AControl) as TButton).OnMouseUp := @MyClass.MouseUp;
  50.     end;
  51.     for I := 0 to TWinControl(AControl).ControlCount - 1 do
  52.       SetButtons(TWinControl(AControl).Controls[I]);
  53.   end;
  54. end;
  55.  
  56. procedure TForm1.FormCreate(Sender: TObject);
  57. begin
  58.   MyClass := TMyClass.Create;
  59.   SetButtons(Form1);
  60. end;
  61.  
  62. procedure TForm1.FormDestroy(Sender: TObject);
  63. begin
  64.   MyClass.Free;
  65. end;
Title: Re: Affect many buttons to class with events ?
Post by: jojo86 on January 29, 2019, 06:49:49 pm
Ohhhh!!!
Thank you.
I can’t test actually because I left my job, but I’ll try this tomorrow but I’m sure that’s going to work!!!

Thanks!!!!
Title: Re: Affect many buttons to class with events ?
Post by: jojo86 on January 31, 2019, 10:09:56 am
As I mentioned in my previous post,  procedure ClickTheButton and MouseUp must be part of a class, like this:
Code: Pascal  [Select][+][-]
  1. //your custom unit
  2. type
  3.   TMyClass = class
  4.   private
  5.     procedure ClickTheButton(Sender: TObject);
  6.     procedure MouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Integer);
  7.   public
  8.     constructor Create;
  9.     destructor Destroy; override;
  10.   end;
  11.  
  12. var
  13.   MyClass: TMyClass;
  14.  
  15. procedure TMyClass.ClickTheButton(Sender: TObject);
  16. begin
  17.  
  18. end;
  19.  
  20. procedure TMyClass.MouseUp(Sender: TObject; Button: TMouseButton;
  21.   Shift: TShiftState; X, Y: Integer);
  22. begin
  23.  
  24. end;
  25.  
  26. constructor TMyClass.Create;
  27. begin
  28.   //do some initialization if needed, create objects, etc...
  29. end;
  30.  
  31. destructor TMyClass.Destroy;
  32. begin
  33.   //free object here
  34.   inherited Destroy;
  35. end;
  36.  
  37. //form's unit
  38. procedure SetButtons(const AControl: TControl);
  39. var
  40.   I: Integer;
  41. begin
  42.   if (AControl = nil) then
  43.     Exit;
  44.   if AControl is TWinControl then
  45.   begin
  46.     if (TWinControl(AControl) is TButton) and ((TWinControl(AControl) as TButton).Tag = 0) then
  47.     begin
  48.      (TWinControl(AControl) as TButton).OnClick := @Myclass.ClickTheButton;
  49.      (TWinControl(AControl) as TButton).OnMouseUp := @MyClass.MouseUp;
  50.     end;
  51.     for I := 0 to TWinControl(AControl).ControlCount - 1 do
  52.       SetButtons(TWinControl(AControl).Controls[I]);
  53.   end;
  54. end;
  55.  
  56. procedure TForm1.FormCreate(Sender: TObject);
  57. begin
  58.   MyClass := TMyClass.Create;
  59.   SetButtons(Form1);
  60. end;
  61.  
  62. procedure TForm1.FormDestroy(Sender: TObject);
  63. begin
  64.   MyClass.Free;
  65. end;

Hi It’s working!
But how can I destroy all my custom class with variables?

I tried by adding « myclassvariable.free » inside my destructor code and I added MyClass.Destroy  but it doesn’t works...

Thank you,

Title: Re: Affect many buttons to class with events ?
Post by: balazsszekely on January 31, 2019, 10:31:54 am
@jojo86
Quote
But how can I destroy all my custom class with variables?
I tried by adding « myclassvariable.free » inside my destructor code and I added MyClass.Destroy  but it doesn’t works...
Hmm.. I'm not sure what you mean. In the above example, every object belonging to TMyClass(usually created in the constructor), must be freed inside the same class(usually in the destructor). The insance of TMyClass must be created and freed outside the class. In the above example is created and freed in TForm. You should always call MyClass.Free not MyClass.Destroy.
Title: Re: Affect many buttons to class with events ?
Post by: jojo86 on January 31, 2019, 10:53:55 am
I tried to add a TSQLQuery variable in my class.
When I create my class, I link the TSQLQuery Class with another TSQLQuery wich is in a form.

But when  I call
Code: Pascal  [Select][+][-]
  1. MyClass.Free
I can't use my first TSQLQuery (on my form).

Code: Pascal  [Select][+][-]
  1. { TZoomCls }
  2. Type
  3.   TZoomCls = Class
  4.   Form: TComponent;
  5.   TrackBar: TTrackBar;
  6.   DtSource: TDataSource;
  7.   Query: TSQLQuery;
  8.   ChkBox: TCheckBox;
  9.   Img: TImage;
  10.   PContenantImg: TPanel;
  11.  
  12.   private
  13.          procedure TrackBarChange(Sender:TOBject);
  14.          procedure DtSourceChange(Sender: TObject; Field: TField);
  15.   public
  16.     constructor Create;
  17.     destructor Destroy; override;
  18. end;                      
  19.  
  20. var
  21. MyZoom: TZoomCls;    
  22.  
  23. procedure SetImgZoom(TrackBar: TTrackBar; ChkPlRep: TCheckBox;
  24.   DataSrc: TDataSource; Qry: TSQLQuery);
  25. begin
  26.   MyZoom:= TZoomCls.Create;
  27.   MyZoom.Form:=TrackBar.Owner;
  28.   MyZoom.TrackBar:= TrackBar;
  29.   MyZoom.DtSource:=DataSrc;
  30.   MyZoom.ChkBox:= ChkPlRep;
  31.   MyZoom.Query:=Qry;
  32.   MyZoom.Img:=(MyZoom.Form.FindComponent('Img1') as TImage);
  33.   MyZoom.PContenantImg:=(MyZoom.Form.FindComponent('PContenantImg') as TPanel);
  34.   TrackBar.OnChange := @MyZoom.TrackBarChange;
  35.   MyZoom.DtSource.OnDataChange:=@MyZoom.DtSourceChange;
  36.   MyZoom.TrackBarChange(TrackBar);
  37.   AfficherImage;
  38. end;
  39.  

When I call MyZoom.Free, I can't use the originale Qry wich is in another form...
It's as if MyZoom.Query was still linked to My real TSQLQuery...

 :o
Title: Re: Affect many buttons to class with events ?
Post by: balazsszekely on January 31, 2019, 11:32:14 am
TrackBar,  ChkPlRep,  DataSrc and Qry are parts of an external form?
Title: Re: Affect many buttons to class with events ?
Post by: jojo86 on January 31, 2019, 02:06:37 pm
TrackBar,  ChkPlRep,  DataSrc and Qry are parts of an external form?

Yes... I can’t explain why, but I’m sure that you’re going to tell me that’s the problem...
I hope that you have a solution?

Thank!
Title: Re: Affect many buttons to class with events ?
Post by: balazsszekely on January 31, 2019, 02:23:23 pm
Quote
Yes... I can’t explain why, but I’m sure that you’re going to tell me that’s the problem...
It's not a problem, but makes no sense to pass all those components as parameter. Why don't you use them directly by adding the form's unit to the uses clause? Like this:
Code: Pascal  [Select][+][-]
  1. uses unit1;//forms unit
  2.  
  3. procedure SetImgZoom;
  4. begin
  5.   MyZoom:= TZoomCls.Create;
  6.   Form1.TrackBar.OnChange := @MyZoom.TrackBarChange;
  7.   Form1.DtSource.OnDataChange:=@MyZoom.DtSourceChange;
  8.   MyZoom.TrackBarChange(Form1.TrackBar);
  9.   AfficherImage;
  10. end;
  11.  
Title: Re: Affect many buttons to class with events ?
Post by: jojo86 on February 12, 2019, 04:34:36 pm
Quote
Yes... I can’t explain why, but I’m sure that you’re going to tell me that’s the problem...
It's not a problem, but makes no sense to pass all those components as parameter. Why don't you use them directly by adding the form's unit to the uses clause? Like this:
Code: Pascal  [Select][+][-]
  1. uses unit1;//forms unit
  2.  
  3. procedure SetImgZoom;
  4. begin
  5.   MyZoom:= TZoomCls.Create;
  6.   Form1.TrackBar.OnChange := @MyZoom.TrackBarChange;
  7.   Form1.DtSource.OnDataChange:=@MyZoom.DtSourceChange;
  8.   MyZoom.TrackBarChange(Form1.TrackBar);
  9.   AfficherImage;
  10. end;
  11.  
Hi,
Sorry for my late reply.
I don't want to do that because my class can be used by 3 differents forms. So I don't want to specifie each form, I want to pass all my form to my class to control my form objects from my class...

So, I still have some problems with that... My code posted on January 31 doesn't works...

Title: Re: Affect many buttons to class with events ?
Post by: lucamar on February 12, 2019, 08:44:40 pm
According to your description, there may be many interactions between objects in the other units and yor class which we can't see. Can you attach the full project so that we can see what exactly are you doing and how?

Anyway, freeing your class shouldn't affect any external object unless you're freeing them:
Note also that after you free/destroy your class the pointers to the event handlers of the external objects now point to nowhere! You should revert the assignations you did in SetImgZoom() in your destructor.
Title: Re: Affect many buttons to class with events ?
Post by: jamie on February 12, 2019, 11:09:26 pm
This looks almost like a job for ACTIONS.

Title: Re: Affect many buttons to class with events ?
Post by: jojo86 on February 13, 2019, 10:55:37 am
You should revert the assignations you did in SetImgZoom() in your destructor.

Hi, thanks,
But how can I revert the assignations?
I Tried :
Code: Pascal  [Select][+][-]
  1. procedure SetImgZoom(TrackBar: TTrackBar; ChkPlRep: TCheckBox;
  2.   DataSrc: TDataSource; Qry: TSQLQuery);
  3. begin
  4.   MyZoom:= TZoomCls.Create;
  5.   MyZoom.Form:=TrackBar.Owner;
  6.   MyZoom.TrackBar:= TrackBar;
  7.   MyZoom.DtSource:=DataSrc;
  8.   MyZoom.ChkBox:= ChkPlRep;
  9.   MyZoom.Query:=Qry;
  10.   MyZoom.Img:=(MyZoom.Form.FindComponent('Img1') as TImage);
  11.   MyZoom.PContenantImg:=(MyZoom.Form.FindComponent('PContenantImg') as TPanel);
  12.   TrackBar.OnChange := @MyZoom.TrackBarChange;
  13.   MyZoom.DtSource.OnDataChange:=@MyZoom.DtSourceChange;
  14.   MyZoom.TrackBarChange(TrackBar);
  15.   AfficherImage;
  16. end;                                        
  17.  
  18.  
  19. destructor TZoomCls.Destroy;
  20. begin
  21.   MyZoom.Form:=nil;
  22.   TrackBar.OnChange := nil;
  23.   MyZoom.TrackBar:= nil;
  24.   MyZoom.DtSource.OnDataChange:=nil;
  25.   MyZoom.DtSource:=nil;
  26.   MyZoom.ChkBox:= nil;
  27.   MyZoom.Query:=nil;
  28.   MyZoom.Img:=nil;
  29.   MyZoom.PContenantImg:=nil;
  30.  
  31.   inherited;
  32.   //Destroy;
  33. end;                                        
  34.  

But after that, Assigned(MyZoom) return allways true so, I presume that something stay linked to my class but I don't know what...
How can I remove all link created with SetImgZoom ?

TinyPortal © 2005-2018