Recent

Author Topic: [SOLVED] Event TComboBox.OnEditingDone fires to often (Focus problem?)  (Read 8909 times)

Hartmut

  • Hero Member
  • *****
  • Posts: 742
I have 2 problems, that event TComboBox.OnEditingDone is fired to often:

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4. {$apptype console} {neccessary for writeln}
  5.  
  6. interface
  7.  
  8. uses
  9.  Classes, SysUtils, Forms, Controls, Dialogs, StdCtrls, ComCtrls;
  10.  
  11. type
  12.  TForm1 = class(TForm)
  13.   Button1: TButton;
  14.   Button2: TButton;
  15.   ComboBox1: TComboBox;
  16.   ListBox1: TListBox;
  17.   PageControl1: TPageControl;
  18.   TabSheet1: TTabSheet;
  19.   TabSheet2: TTabSheet;
  20.   procedure ComboBox1EditingDone(Sender: TObject);
  21.   procedure FormActivate(Sender: TObject);
  22.  private
  23.  public
  24.  end;
  25.  
  26. var Form1: TForm1;
  27.  
  28. implementation
  29.  
  30. {$R *.lfm}
  31.  
  32. procedure ComboBox_add_History(var X: TComboBox; max: integer);
  33.    {adds the current value of the ComboBox to the history}
  34.    var s: ansistring;
  35.        i: integer;
  36.    begin
  37.    s:=X.Text;             {get aktuellen Wert der ComboBox}
  38.    if s='' then exit;
  39.  
  40.    i:=X.Items.IndexOf(s); {sucht 's' in der History, not found => -1}
  41.    if i = 0 then exit;    {wenn schon als jüngstes vorhanden}
  42.  
  43.    if i > 0 then          {wenn schon vorhanden, aber nicht als jüngstes: }
  44.       begin
  45.       X.Items.Delete(i);  {delete current item}
  46.       X.Text:=s;          {muß nach Delete() wiederholt werden}
  47.       end;
  48.  
  49.    while X.Items.Count >= max do {lösche ggf. ältere Einträge: }
  50.       begin
  51.       i:=X.Items.Count-1; {oldest item}
  52.       X.Items.Delete(i);  {delete oldest item}
  53.       end;
  54.  
  55.    X.Items.Insert(0,s);   {neues item am Anfang einfügen}
  56.    end;
  57.  
  58. procedure TForm1.ComboBox1EditingDone(Sender: TObject);
  59.    const nr: integer = 1;
  60.    begin
  61.    writeln('OnEditingDone', nr);
  62.    inc(nr);
  63.    ComboBox_add_History(ComboBox1,12);
  64.    ListBox1.SetFocus; // get the focus away from ComboBox1 (does not help)
  65.    end;
  66.  
  67. procedure TForm1.FormActivate(Sender: TObject);
  68.    begin
  69.    ListBox1.SetFocus; // get the focus away from ComboBox1 (does not help)
  70.    end;
  71.  
  72. end.

Problem #1:
a) compile and start the attached project
b) click on TabSheet2
c) click on Button 2
d) click on TabSheet1 (result: ComboBox1 becomes blue)
e) click on Button 1
Result: event TComboBox.OnEditingDone is fired, although this component was NOT "edited".
Looks for me like a focus problem, that the ComboBox became blue in d) and was "quit" in e)
Is that correct?
To avoid this I used ListBox1 to place the focus there (lines 64 and 69), but this did not help and evoked problem #2.

Problem #2:
 - compile and start the attached project
 - enter something via keybord in ComboBox1 and press ENTER
Result: event TComboBox.OnEditingDone is fired twice, although this component was "edited" only once.
Why does this happen?

I would like to understand this behavior, if possible.
What I want is ONE event in case of a) the ComboBox has got another content or b) the same content as last time is selected a 2nd time via Mouse or ENTER. How can this be done?

I use Lazarus 1.8.4 on Windows 7 32 bit. Thanks a lot in advance.

[EDIT]
There is a summary about this Topic in reply #24.
« Last Edit: July 15, 2018, 03:39:09 pm by Hartmut »

Handoko

  • Hero Member
  • *****
  • Posts: 5131
  • My goal: build my own game engine using Lazarus
Re: Event TComboBox.OnEditingDone fires to often (Focus problem?)
« Reply #1 on: July 14, 2018, 01:03:27 pm »
For problem #1:

I cannot reproduce what you said. Or maybe I didn't fully understand what you meant.

For problem #2:

Yes, I get ComboBox1EditingDone fired twice. But your code has problem. Simply remove ListBox1.SetFocus; solve the issue. In the code below, the event only fired once because I use Exit to skip the LisBox1.SetFocus:

Code: Pascal  [Select][+][-]
  1. procedure TForm1.ComboBox1EditingDone(Sender: TObject);
  2.    const nr: integer = 1;
  3.    begin
  4.    writeln('OnEditingDone', nr);
  5.    inc(nr);
  6.    ComboBox_add_History(ComboBox1,12);
  7.    Exit;
  8.    ListBox1.SetFocus; // get the focus away from ComboBox1 (does not help)
  9.    end;

So, I will say it is your fault that cause OnEditingDone to fired 2x. You should not 'steal' focus inside the OnEditingDone event.

Tested on Lazarus 1.8.4 Ubuntu 18.04.

Hartmut

  • Hero Member
  • *****
  • Posts: 742
Re: Event TComboBox.OnEditingDone fires to often (Focus problem?)
« Reply #2 on: July 14, 2018, 01:42:12 pm »
Thank you Handoko for your help.
For problem #1:
I cannot reproduce what you said. Or maybe I didn't fully understand what you meant.
At which point you cannot follow / what is difficult to understand? Can you click from b) to e) ? Do you get ComboBox1 marked blue in d) ? Is the event fired after e) ?
Is it possible that this problem occurs only under Windows?

For problem #2:

I learned that I may not 'steal' the focus inside an OnEditingDone event. But how can I move the focus away from the ComboBox? When I press ENTER in the ComboBox, the focus "stays" there and comes back in d)
After gettinng a (new) value from the ComboBox, I want to show something in the ListBox and want to place the focus there, so that you e.g. can scroll in the ListBox with the arrow keys. How can this be done?

Handoko

  • Hero Member
  • *****
  • Posts: 5131
  • My goal: build my own game engine using Lazarus
Re: Event TComboBox.OnEditingDone fires to often (Focus problem?)
« Reply #3 on: July 14, 2018, 02:02:37 pm »
At which point you cannot follow / what is difficult to understand? Can you click from b) to e) ? Do you get ComboBox1 marked blue in d) ? Is the event fired after e) ?
Is it possible that this problem occurs only under Windows?

I don't know. I use Linux and I tested your code on Gtk2 widgetset. As you can see on the image below, nothing is blue. :D
« Last Edit: July 14, 2018, 02:13:10 pm by Handoko »

Handoko

  • Hero Member
  • *****
  • Posts: 5131
  • My goal: build my own game engine using Lazarus
Re: Event TComboBox.OnEditingDone fires to often (Focus problem?)
« Reply #4 on: July 14, 2018, 02:12:05 pm »
... But how can I move the focus away from the ComboBox? When I press ENTER in the ComboBox, the focus "stays" there and comes back in d)
After gettinng a (new) value from the ComboBox, I want to show something in the ListBox and want to place the focus there ...

Maybe this is what you want:

Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4. {$apptype console} {neccessary for writeln}
  5.  
  6. interface
  7.  
  8. uses
  9.  Classes, SysUtils, Forms, Controls, Dialogs, StdCtrls, ComCtrls, ExtCtrls;
  10.  
  11. type
  12.  
  13.  { TForm1 }
  14.  
  15.  TForm1 = class(TForm)
  16.   Button1: TButton;
  17.   Button2: TButton;
  18.   ComboBox1: TComboBox;
  19.   ListBox1: TListBox;
  20.   PageControl1: TPageControl;
  21.   TabSheet1: TTabSheet;
  22.   TabSheet2: TTabSheet;
  23.   Timer1: TTimer;
  24.   procedure ComboBox1EditingDone(Sender: TObject);
  25.   procedure FormActivate(Sender: TObject);
  26.   procedure Timer1Timer(Sender: TObject);
  27.  private
  28.  public
  29.  end;
  30.  
  31. var Form1: TForm1;
  32.  
  33. implementation
  34.  
  35. {$R *.lfm}
  36.  
  37. procedure ComboBox_add_History(var X: TComboBox; max: integer);
  38.    {adds the current value of the ComboBox to the history}
  39.    var s: ansistring;
  40.        i: integer;
  41.    begin
  42.    s:=X.Text;             {get aktuellen Wert der ComboBox}
  43.    if s='' then exit;
  44.  
  45.    i:=X.Items.IndexOf(s); {sucht 's' in der History, not found => -1}
  46.    if i = 0 then exit;    {wenn schon als jüngstes vorhanden}
  47.  
  48.    if i > 0 then          {wenn schon vorhanden, aber nicht als jüngstes: }
  49.       begin
  50.       X.Items.Delete(i);  {delete current item}
  51.       X.Text:=s;          {muß nach Delete() wiederholt werden}
  52.       end;
  53.  
  54.    while X.Items.Count >= max do {lösche ggf. ältere Einträge: }
  55.       begin
  56.       i:=X.Items.Count-1; {oldest item}
  57.       X.Items.Delete(i);  {delete oldest item}
  58.       end;
  59.  
  60.    X.Items.Insert(0,s);   {neues item am Anfang einfügen}
  61.    end;
  62.  
  63. procedure TForm1.ComboBox1EditingDone(Sender: TObject);
  64.    const nr: integer = 1;
  65.    begin
  66.    writeln('OnEditingDone', nr);
  67.    inc(nr);
  68.    ComboBox_add_History(ComboBox1,12);
  69.    Timer1.Enabled := True;
  70.    Exit;
  71.    ListBox1.SetFocus; // get the focus away from ComboBox1 (does not help)
  72.    end;
  73.  
  74. procedure TForm1.FormActivate(Sender: TObject);
  75.    begin
  76.    Timer1.Interval := 100;
  77.    Timer1.Enabled := False;
  78.    ListBox1.SetFocus; // get the focus away from ComboBox1 (does not help)
  79.    end;
  80.  
  81. procedure TForm1.Timer1Timer(Sender: TObject);
  82. begin
  83.   ListBox1.SetFocus;
  84.   Timer1.Enabled := False;
  85. end;
  86.  
  87. end.

If I want to 'force' something to always on focus I usually use a Timer. Maybe it is not the best solution but it works. See line #69, #76, #77, #83, #84.

Hartmut

  • Hero Member
  • *****
  • Posts: 742
Re: Event TComboBox.OnEditingDone fires to often (Focus problem?)
« Reply #5 on: July 14, 2018, 02:55:40 pm »
I use Linux and I tested your code on Gtk2 widgetset. As you can see on the image below, nothing is blue. :D
Seems that in your screenshot the Combobox is "selected" and green (this could be the equivalent to 'blue' on Windows). I guess that if you press a letter key in this situation, it is inserted in the Combobox. If this screenshot is from d) I don't understand, why Combobox is "selected" after clicking on 'TabSheet1'.
Don't you get event TComboBox.OnEditingDone fired when you then click on Button 1? This would be a difference between Windows and Linux and maybe could be a bug?

Hartmut

  • Hero Member
  • *****
  • Posts: 742
Re: Event TComboBox.OnEditingDone fires to often (Focus problem?)
« Reply #6 on: July 14, 2018, 02:56:44 pm »
If I want to 'force' something to always on focus I usually use a Timer.
Thank you very much for that suggestion. It tried it and it solves problem #2 in this demo, but in real live I show data from an sql DB in ListBox1. To read this data can need 10 ms or a couple of seconds. So I would not know how long to set the timer...

wp

  • Hero Member
  • *****
  • Posts: 11858
Re: Event TComboBox.OnEditingDone fires to often (Focus problem?)
« Reply #7 on: July 14, 2018, 03:54:06 pm »
Problem #1:
a) compile and start the attached project
b) click on TabSheet2
c) click on Button 2
d) click on TabSheet1 (result: ComboBox1 becomes blue)
e) click on Button 1
Result: event TComboBox.OnEditingDone is fired, although this component was NOT "edited".
After step d) the Combobox1 is blue, this means that it is in edit mode. When you click on Button1 in step e) the focus moves away from the Combobox, and this terminates the edit mode. The behavior is absolutely correct because edit mode is done - even if nothing was changed.

If you want to prevent firing OnEditingDone in this case you must store the value of the combobox before editing starts and exit the OnEditingDone event if the current value is not changed:
Code: Pascal  [Select][+][-]
  1.  TForm1 = class(TForm)
  2.   ...
  3.   private
  4.     FOldComboValue: String;
  5.   end;
  6.  
  7. procedure TForm1.ComboBox1EditingDone(Sender: TObject);
  8.    const nr: integer = 1;
  9. begin
  10.    if FOldComboValue = Combobox1.Text then exit; // <---- NEW
  11.    writeln('OnEditingDone', nr);
  12.    inc(nr);
  13.    ComboBox_add_History(ComboBox1,12);
  14.    ListBox1.SetFocus; // get the focus away from ComboBox1 (does not help)
  15. end;
  16.  
  17. procedure TForm1.ComboBox1Enter(Sender: TObject);   // <--- NEW
  18. begin
  19.   FOldComboValue := Combobox1.Text;
  20. end;
  21.  


Handoko

  • Hero Member
  • *****
  • Posts: 5131
  • My goal: build my own game engine using Lazarus
Re: Event TComboBox.OnEditingDone fires to often (Focus problem?)
« Reply #8 on: July 14, 2018, 04:04:30 pm »
@wp:

On Linux Gtk2, it behaves differently. Now matter how often I switched tabs and clicked the buttons, the ComboBox will never automatically get focus nor OnEditingDone event fired. Tested on Linux Gtk2 and WinXP.

@Hartmut:

I've just found myself a Windows computer to test what you meant in problem 1. I finally understand what you meant now, I can reproduce it. I believe it is a bug because it does not behave like that on Linux Gtk2. Maybe you should submit a bug report on the bugtracker forum:
https://bugs.freepascal.org

I now testing the code to see if I can find any workaround for it.

Quote from: Hartmut link=topic=41882.msg291589#msg291589
It tried it and it solves problem #2 in this demo, but in real live I show data from an sql DB in ListBox1. To read this data can need 10 ms or a couple of seconds. So I would not know how long to set the timer...

I use that trick often and so far never have any problem. Although I never use it on database program, but I believe the Interval := 100 should work without any issue too.

Hartmut

  • Hero Member
  • *****
  • Posts: 742
Re: Event TComboBox.OnEditingDone fires to often (Focus problem?)
« Reply #9 on: July 14, 2018, 04:23:48 pm »
After step d) the Combobox1 is blue, this means that it is in edit mode. When you click on Button1 in step e) the focus moves away from the Combobox, and this terminates the edit mode. The behavior is absolutely correct because edit mode is done - even if nothing was changed.
Thank you very much for that explanation. Now I understand the 2nd half of problem #1. What I still not understand is, why Combobox1 gets into edit mode by clicking on TabSheet1 (instead of clicking in the ComboBox). Can this be avoided?

Quote
If you want to prevent firing OnEditingDone in this case you must store the value of the combobox before editing starts and exit the OnEditingDone event if the current value is not changed:
Thank you too for this suggestion. But it only works, if the value in the ComboBox is changed. I need that event too, if the same content as last time is selected a 2nd time via Mouse or ENTER. Can this be done?

Hartmut

  • Hero Member
  • *****
  • Posts: 742
Re: Event TComboBox.OnEditingDone fires to often (Focus problem?)
« Reply #10 on: July 14, 2018, 04:35:39 pm »
On Linux Gtk2, it behaves differently. Now matter how often I switched tabs and clicked the buttons, the ComboBox will never automatically get focus nor OnEditingDone event fired. Tested on Linux Gtk2 and WinXP.
Thanks a lot for testing and reporting this.

Quote
I believe it is a bug because it does not behave like that on Linux Gtk2. Maybe you should submit a bug report on the bugtracker forum
I will wait if wp disagrees and if not I will submit a bug report.

Quote
I now testing the code to see if I can find any workaround for it.
This is very friendly of you!

Quote
I use that trick often and so far never have any problem. Although I never use it on database program, but I believe the Interval := 100 should work without any issue too.
I will try it in my real program.

wp

  • Hero Member
  • *****
  • Posts: 11858
Re: Event TComboBox.OnEditingDone fires to often (Focus problem?)
« Reply #11 on: July 14, 2018, 06:25:29 pm »
Quote
If you want to prevent firing OnEditingDone in this case you must store the value of the combobox before editing starts and exit the OnEditingDone event if the current value is not changed:
Thank you too for this suggestion. But it only works, if the value in the ComboBox is changed. I need that event too, if the same content as last time is selected a 2nd time via Mouse or ENTER. Can this be done?
Now you lost me. Do you want the second OnEditingDone event or not? Or do you want to distinguish between the two cases: (1) EditingDone because of focusing another control, or (2) EditingDone because ENTER was pressed? If your code really depends so much on multiple occurance of events you should evaluate whether you are going the correct way. What exactly do you want to achieve?

Handoko

  • Hero Member
  • *****
  • Posts: 5131
  • My goal: build my own game engine using Lazarus
Re: Event TComboBox.OnEditingDone fires to often (Focus problem?)
« Reply #12 on: July 14, 2018, 07:06:15 pm »
After some testings on both Linux and WinXP, I found TComboBox behaves very differently on those widgetsets. The ComboBox will automatically receive focus when switching tab. It won't happen if you just switch the tab. But the ComboBox will be receive focus after you clicked the button inside the tab and then switch to the other tab. Sound very weird, isn't it? Such behavior does not happen on Linux.

To prevent this 'weird' behavior on Windows, this is my workaround:
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4. {$apptype console} {neccessary for writeln}
  5.  
  6. interface
  7.  
  8. uses
  9.  Classes, SysUtils, Forms, Controls, Dialogs, StdCtrls, ComCtrls;
  10.  
  11. type
  12.  
  13.  { TForm1 }
  14.  
  15.  TForm1 = class(TForm)
  16.   Button1: TButton;
  17.   Button2: TButton;
  18.   ComboBox1: TComboBox;
  19.   ListBox1: TListBox;
  20.   PageControl1: TPageControl;
  21.   TabSheet1: TTabSheet;
  22.   TabSheet2: TTabSheet;
  23.   procedure ComboBox1EditingDone(Sender: TObject);
  24.   procedure ComboBox1KeyPress(Sender: TObject; var Key: char);
  25.   procedure FormActivate(Sender: TObject);
  26.   procedure PageControl1Change(Sender: TObject);
  27.  private
  28.  public
  29.  end;
  30.  
  31. var Form1: TForm1;
  32.     SkipEvent: Boolean = False;
  33.  
  34. implementation
  35.  
  36. {$R *.lfm}
  37.  
  38. procedure ComboBox_add_History(var X: TComboBox; max: integer);
  39.    {adds the current value of the ComboBox to the history}
  40.    var s: ansistring;
  41.        i: integer;
  42.    begin
  43.    s:=X.Text;             {get aktuellen Wert der ComboBox}
  44.    if s='' then exit;
  45.  
  46.    i:=X.Items.IndexOf(s); {sucht 's' in der History, not found => -1}
  47.    if i = 0 then exit;    {wenn schon als jüngstes vorhanden}
  48.  
  49.    if i > 0 then          {wenn schon vorhanden, aber nicht als jüngstes: }
  50.       begin
  51.       X.Items.Delete(i);  {delete current item}
  52.       X.Text:=s;          {muß nach Delete() wiederholt werden}
  53.       end;
  54.  
  55.    while X.Items.Count >= max do {lösche ggf. ältere Einträge: }
  56.       begin
  57.       i:=X.Items.Count-1; {oldest item}
  58.       X.Items.Delete(i);  {delete oldest item}
  59.       end;
  60.  
  61.    X.Items.Insert(0,s);   {neues item am Anfang einfügen}
  62.    end;
  63.  
  64. procedure TForm1.ComboBox1EditingDone(Sender: TObject);
  65.    const nr: integer = 1;
  66.    begin
  67.    if (SkipEvent) then Exit;
  68.    writeln('OnEditingDone', nr);
  69.    inc(nr);
  70.    ComboBox_add_History(ComboBox1,12);
  71.    Exit;
  72.    ListBox1.SetFocus; // get the focus away from ComboBox1 (does not help)
  73.    end;
  74.  
  75. procedure TForm1.ComboBox1KeyPress(Sender: TObject; var Key: char);
  76. begin
  77. end;
  78.  
  79. procedure TForm1.FormActivate(Sender: TObject);
  80.    begin
  81.    ListBox1.SetFocus; // get the focus away from ComboBox1 (does not help)
  82.    end;
  83.  
  84. procedure TForm1.PageControl1Change(Sender: TObject);
  85. begin
  86.   SkipEvent := True;
  87.   PageControl1.ActivePage.SetFocus;
  88.   SkipEvent := False;
  89. end;
  90.  
  91. end.

I use SkipEvent and PageControl.OnChange event to prevent the auto focus issue. See line #32, #67, #84.

I personally think this is a bug. Switching tab in TPageControl should not cause the combobox to receive focus.

What I still not understand is, why Combobox1 gets into edit mode by clicking on TabSheet1 (instead of clicking in the ComboBox).

TComboBox.OnEditingDone event is fired if the combobox lost focus, this behavior is the same when running on Linux.

On Linux:
The TComboBox.OnEditingDone won't be fired when you switch focus from the combobox to a non-active tab. The event only be fired if you switch focus from the combobox to an active tab.

On Windows (XP):
The ComboBox.OnEditingDone always be fired when you switch forus from combobox to either an active or non-active tab.
« Last Edit: July 14, 2018, 07:07:58 pm by Handoko »

Hartmut

  • Hero Member
  • *****
  • Posts: 742
Re: Event TComboBox.OnEditingDone fires to often (Focus problem?)
« Reply #13 on: July 14, 2018, 07:08:05 pm »
Do you want the second OnEditingDone event or not? Or do you want to distinguish between the two cases: (1) EditingDone because of focusing another control, or (2) EditingDone because ENTER was pressed?
No, I don't want do distinguish. Sorry, my English is not so good.

I want to do the same actions if:
A) someone enters a new value in the ComboBox (and presses Enter or leaves the ComboBox via TAB or selects another Control with the Mouse) or
B) if someone selects something from the History of the ComboBox (with the Mouse or by arrow keys and Enter) or
C) if the last content of the ComboBox is selected one more time (e.g. by opening the History via Mouse, but selecting the last value again via Mouse click or via Enter).
In all those cases the same actions must be executed only once.
No actions have to take place, if on a TabSheet is clicked like in d) in my 1st post.
Hope this is more understandable. Thank you for your help.

Hartmut

  • Hero Member
  • *****
  • Posts: 742
Re: Event TComboBox.OnEditingDone fires to often (Focus problem?)
« Reply #14 on: July 14, 2018, 08:27:28 pm »
To prevent this 'weird' behavior on Windows, this is my workaround:
...
I use SkipEvent and PageControl.OnChange event to prevent the auto focus issue. See line #32, #67, #84.
This is a great solution. I implemented this in my real program and it works perfect. Problem #1 is solved. Thanks a lot!

Problem #2 still exists in my real program, although I deleted the "ListBox1.SetFocus" commands. Now I can reproduce it:
Take the attached project from my 1st post. Disable the 2 "ListBox1.SetFocus" commands like:

Code: Pascal  [Select][+][-]
  1. procedure TForm1.ComboBox1EditingDone(Sender: TObject);
  2.    const nr: integer = 1;
  3.    begin
  4.    writeln('OnEditingDone', nr);
  5.    inc(nr);
  6.    ComboBox_add_History(ComboBox1,12);
  7. // ListBox1.SetFocus; // disabled
  8.    end;
  9.  
  10. procedure TForm1.FormActivate(Sender: TObject);
  11.    begin
  12. // ListBox1.SetFocus; // disabled
  13.    end;
           
Then:
1) compile and start the program
2) enter e.g. 3 values into the ComboBox, so that the History contains 3 values
3) open the ComboBox History with the mouse
4) select a value with the arrow keys
5) press ENTER
Result: event TComboBox.OnEditingDone is fired twice.
For me this looks like a bug, what do others say?

I personally think this is a bug. Switching tab in TPageControl should not cause the combobox to receive focus.
@wp: Do you also think that this is a bug?
« Last Edit: July 14, 2018, 10:01:57 pm by Hartmut »

 

TinyPortal © 2005-2018