Lazarus

Programming => LCL => Topic started by: egsuh on October 05, 2020, 08:49:16 am

Title: [SOLVED] Any way to Exit a control when form is clicked?
Post by: egsuh on October 05, 2020, 08:49:16 am
Let's assume that I have a TMEMO in a form.  When I click the TMemo, TMemo.OnEnter is called. But OnExit is not called when I click on the vacant area of the form which contains the TMemo. OnExit is called when I click on another control in the same form.

Is there anyway that I can make the OnExit event handler is called when I click on the form?
Title: Re: Any way to Exit a control when form is clicked?
Post by: Handoko on October 05, 2020, 09:11:43 am
OnExit is only triggered when it's losing focus. Clicking the form does not make the Memo to lose focus. Clicking other control (on the same form) will make that control to receive focus and that will send a signal to the Memo to trigger the OnExit.

Can you explain more detail why you need to trigger the event when user click the form?

-edit-
You can make form click to trigger Memo.OnExit. Try this code:

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, StdCtrls;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.  
  14.   TForm1 = class(TForm)
  15.     Button1: TButton;
  16.     Memo1: TMemo;
  17.     procedure FormClick(Sender: TObject);
  18.     procedure Memo1Exit(Sender: TObject);
  19.   end;
  20.  
  21. var
  22.   Form1: TForm1;
  23.  
  24. implementation
  25.  
  26. {$R *.lfm}
  27.  
  28. { TForm1 }
  29.  
  30. procedure TForm1.Memo1Exit(Sender: TObject);
  31. begin
  32.   Caption := Random(100).ToString;
  33. end;
  34.  
  35. procedure TForm1.FormClick(Sender: TObject);
  36. begin
  37.   Memo1Exit(Self);
  38. end;
  39.  
  40. end.
Title: Re: Any way to Exit a control when form is clicked?
Post by: lucamar on October 05, 2020, 09:23:48 am
Add a handler for the form's OnClick event which does this:

Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormClick(Sender: TObject);
  2. begin
  3.   if Memo.Focused then Memo.PerformTab(True); { Or False to go backwards }
  4. end;

It will move the focus away from the memo and trigger its OnExit event. Note that for it to work there must be some other focusable control in the form, say, a button or alike; otherwise PerformTab will do nothing.
Title: Re: Any way to Exit a control when form is clicked?
Post by: egsuh on October 05, 2020, 09:57:58 am
Quote
Can you explain more detail why you need to trigger the event when user click the form?
What I need is to store the content of controls whenever it is changed.  I'm looking for the most secure way. Practically exiting control may happen in many cases .. by directly entering into another control, clicking on a form, closing the form by clickicking on the small x button, or closing form by pressing Alt-F4, etc. Just want to know any event that will be called everytime.
Title: Re: Any way to Exit a control when form is clicked?
Post by: howardpc on October 05, 2020, 10:22:56 am
You could maybe harness your controls' OnEditingDone event.
Title: Re: Any way to Exit a control when form is clicked?
Post by: egsuh on October 05, 2020, 11:44:13 am
Quote
You could maybe harness your controls' OnEditingDone event.

Nothing is 100% reliable. I'll search for other methods.
Title: Re: Any way to Exit a control when form is clicked?
Post by: Sieben on October 05, 2020, 12:14:20 pm
Very often you will have to watch more than a single event to cover all situations. But in most cases you need to write just one handler since most of them are simple TNotifyEvents. Stop searching for THE EVENT...
Title: Re: Any way to Exit a control when form is clicked?
Post by: BrunoK on October 05, 2020, 01:58:09 pm
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.  
  14.   TForm1 = class(TForm)
  15.     Memo1: TMemo;
  16.     procedure FormClick(Sender: TObject);
  17.     procedure FormShow(Sender: TObject);
  18.     procedure Memo1Enter(Sender: TObject);
  19.     procedure Memo1Exit(Sender: TObject);
  20.   private
  21.     FDummyWinC: TWinControl;
  22.     procedure DummyWinCEnter(Sender: TObject);
  23.   public
  24.  
  25.   end;
  26.  
  27. var
  28.   Form1: TForm1;
  29.  
  30. implementation
  31.  
  32. {$R *.lfm}
  33.  
  34. { TForm1 }
  35.  
  36. procedure TForm1.FormShow(Sender: TObject);
  37. begin
  38.   if not Assigned(FDummyWinC) then begin
  39.     FDummyWinC := TWinControl.Create(Self);
  40.     FDummyWinC.Parent := Self;
  41.     FDummyWinC.SetBounds(0,0,0,0);
  42.     FDummyWinC.OnEnter:=@DummyWinCEnter;
  43.  
  44.     { If it has to respond to TAB, include next 2 lines }
  45.     FDummyWinC.TabStop := true;
  46.     FDummyWinC.TabOrder:=Memo1.TabOrder+1;
  47.   end;
  48. end;
  49.  
  50. procedure TForm1.FormClick(Sender: TObject);
  51. begin
  52.   FDummyWinC.SetFocus;
  53. end;
  54.  
  55. procedure TForm1.Memo1Enter(Sender: TObject);
  56. begin
  57.   Memo1.Lines.Add('Entered:'+FormatDateTime('hh:nn:ss',now));
  58. end;
  59.  
  60. procedure TForm1.Memo1Exit(Sender: TObject);
  61. begin
  62.   Memo1.Lines.Add('Exited:'+FormatDateTime('hh:nn:ss',now));
  63. end;
  64.  
  65. procedure TForm1.DummyWinCEnter(Sender: TObject);
  66. begin
  67.   FDummyWinC.PerformTab(True); // Tab to next control
  68. end;
  69.  
  70. end.
Title: Re: Any way to Exit a control when form is clicked?
Post by: lucamar on October 05, 2020, 04:42:35 pm
Code: Pascal  [Select][+][-]
  1. procedure TForm1.DummyWinCEnter(Sender: TObject);
  2. begin
  3.   FDummyWinC.PerformTab(True); // Tab to next control
  4. end;

If you're going to do that you might just as well do it as I wrote before, making the memo do PerformTab(). You simply introduced a "phantom" control in the "focus" chain and doing it this way you might accidentally find yourself "tabing" back to the memo, depending on the controls's tab order. And it also complicates the program unneccesarily.
Title: Re: Any way to Exit a control when form is clicked?
Post by: BrunoK on October 05, 2020, 06:08:21 pm
If you're going to do that you might just as well do it as I wrote before, making the memo do PerformTab(). You simply introduced a "phantom" control in the "focus" chain and doing it this way you might accidentally find yourself "tabing" back to the memo, depending on the controls's tab order. And it also complicates the program unneccesarily.
Tested on Win10 :
If the only control on the form is the Memo then MemoExit is not called.
Title: Re: Any way to Exit a control when form is clicked?
Post by: wp on October 05, 2020, 06:32:47 pm
I think it is not a very user-friendly concept to change the focus when a click on the form occurs, this is against all conventions and the users will become very frustrated.
Title: Re: Any way to Exit a control when form is clicked?
Post by: lucamar on October 05, 2020, 07:13:45 pm
Tested on Win10 :
If the only control on the form is the Memo then MemoExit is not called.

Yes, I said that too but, frankly, if the memo is the only control and all that is needed is to trigger its OnExit event one might as well call the handler directly from the form's OnClick one.

In fact, from the OP's  description:
What I need is to store the content of controls whenever it is changed.  I'm looking for the most secure way. Practically exiting control may happen in many cases .. by directly entering into another control, clicking on a form, closing the form by clickicking on the small x button, or closing form by pressing Alt-F4, etc. Just want to know any event that will be called everytime.
what I would do is to add a "SaveMemo" method to the form and call from whatever event(s) it should be called or, anyway, from the form's OnCloseQuery handler, when you know the contents are finally fixed.

I have got a program, kind of a "notes to self/things to remember" pad, which needs to do something similar and one of the options I looked into for an "auto-save" feature was to intercept the <Enter> key (in the memo's OnKeyUp event) and save the contents then, but I finally realized it was a futile exercise and what I did was to use a TXMLPropStorage to store some session properties and to save/load the memo contents using its events. After a few false starts (first time I ever used that component) all was a piece of cake compared to, say, dealing with the shortcomings of TMemo ;D
Title: Re: Any way to Exit a control when form is clicked?
Post by: Handoko on October 05, 2020, 07:33:29 pm
If the OP's intention is to save the user provided values to disk, I think it should not be performed if user simply clicks on the form.

When providing value on a textbox or memo I might clicking the form several times but that does not mean I already finish with that item. For example, after typing some text, I forget and need to do some copy/paste something from other applications. That could be a web browser, help file or my personal-saved text files. And I always have multiple applications running and showing on my screen. So I may need to click on the form to bring the application to the front, move the form a better location on the screen, the go back and continue providing the value for the textbox.
Title: Re: Any way to Exit a control when form is clicked?
Post by: egsuh on October 09, 2020, 04:10:39 pm
I found this issue was discussed in StackOverflow, and found that simplest way is to write

    Self.ActiveControl := nil;

which will call the OnExit procedure of focused control.  I tested this, and reliable --- this calls OnExit event of focused control when I press other tab, or close the Windows by clicking F4 or samll x of the window (on Windows 10).

But I cannot do this in Frame. There are no ActiveControl in Frame. Is there any work-around?
Title: Re: Any way to Exit a control when form is clicked?
Post by: howardpc on October 09, 2020, 05:31:36 pm
ActiveControl is a TCustomForm property.
So you could try
Code: Pascal  [Select][+][-]
  1. ParentForm.ActiveControl := Nil;
Title: Re: Any way to Exit a control when form is clicked?
Post by: Sieben on October 09, 2020, 07:01:02 pm
But again - why? OnExit is triggered when the control is left for whatever reason, and for closing form there is this wonderful event OnCloseQuery where you can even stop the whole thing if something goes wrong, ask the user if she/he really means to leave, if he/she wants to save or not and so on... why not do it the way lucamar suggested...?
Title: Re: Any way to Exit a control when form is clicked?
Post by: marcio2003 on October 09, 2020, 08:01:23 pm
Code: Pascal  [Select][+][-]
  1. procedure TForm1.FormMouseDown(Sender: TObject; Button: TMouseButton;
  2.   Shift: TShiftState; X, Y: Integer);
  3. begin
  4.   Form1.FocusControl( nil );
  5. end;

Windows 10 64bit - Lazarus v2.0.6
Title: Re: Any way to Exit a control when form is clicked?
Post by: egsuh on October 10, 2020, 12:45:09 am
I'm not sure addressing this issue again is valuable enough, but I'd like to share my idea and experiements with others.

First of all, I appreciate all the comments and commenters.

My baseline purpose is the content in the controls are saved to a field in object, like

Code: Pascal  [Select][+][-]
  1. type
  2.      TMyObject = class
  3.      published
  4.           Name,
  5.           Address : string;
  6.           OtherInfo: TStringList;
  7.  
  8.           // methods
  9.      end;
  10.  
  11.      TMyFrame = class (TFrame)
  12.            edit1 : TTIEdit;   // name
  13.            edit2 : TTIEdit;   // address
  14.            OtherInfoEdit : TValueListEditor;
  15.  
  16.            // methods
  17.       private
  18.            FMyObject : TMyObject;
  19.            procedure setMyObject(AValue: TMyObject);
  20.       public
  21.             property MyObject : TMyObject read FMyObject write setMyObject;
  22.    
  23.       end;
  24.  
  25. implementation
  26.  
  27. procedure TMyFrame.setMyObject(AValue: TMyObject);
  28. begin
  29.     if AValue <> nil then begin  
  30.         FMyObject := AValue;
  31.         OtherInfoEdit.Strings.Text := FMyObject.OtherInfo.Text;    // initialize the content of valuelist editor
  32.     end;
  33. end;  
  34.  
  35.  

I do not assign any saving procedure myself for RTTI controls. But I have to write them for general controls, TValueListEditor here. So,

Code: Pascal  [Select][+][-]
  1. procedure TMyFrame.OtherInfoEditEditingDone(sender: TObject);   // or
  2. procedure TMyFrame.OtherInfoExit(sender: TObject);   // either one is the same
  3. begin
  4.      MyObject.OtherInfo.Text := OtherInfoEdit.Strings.Text;
  5. end;
  6.  
     
             
And I want the (changed) contents in the controls are automatically saved in the MyObject variable. The contents are saved when focus moves from one control to another, but not (neither RTTI controls nor general controls, neither OnEditingDone nor OnExit) when I leave the form like by running other Windows application, by closing the form pressing Alt-F4 or small x, or on other cases I do not remember exactly. So I was looking for ways that I can save my changes reliably, i.e. will call OnExit or OnEditingDone reliably. 

I thought over solutions suggested here in comments. But I think it better not to introduce any futher things like TWinControl or TNotifyEvent or other event handlers or new variable like ParentForm.

And I found putting ActiveContol to nil in the form.

Code: Pascal  [Select][+][-]
  1. procedure TMyForm.FormClose(Sender: TObject; var CloseAction: TCloseAction);
  2. begin
  3.      // It is possible to ask whether to save the changes here, but decided to put it later setp, between Object and permanent storage (e.g. database)
  4.      Self.ActiveControl := nil;
  5. end;
  6.  

This will call OnExit or OnEditingDone procedures defined only in the frames. 

Hope that I did not mislead you by the title ".. when form is clicked?".  At first, I tried to save the content by clicking on (title bar or vacant part of) the form but I found that there are other situations like pressing Alt-F4.

Thank all of you again for your kind advices.
TinyPortal © 2005-2018