luciano_f 5 Posted Friday at 10:02 AM (edited) Click on Edit1 and press Enter to see Edit2 without the cursor. Some customers complain that the system loses its cursor. I don't know how to replicate this, so I created an application that simulates this incorrectly. See this example. I know how to solve the problem, which would be not displaying the fade-out screen. However, the idea of my example is just to simulate the problem. Suppose I don't know that it's the fade-out screen that's causing the problem. How could I solve it ? Going to another control and back is a terrible solution because the other control might have code in the OnExit example that would trigger it, among other problems. I tried using ProcessMessages or Update on the form, but I didn't find a solution. Can anyone help me ? Lost cursor.7z Edited Friday at 10:12 AM by luciano_f Share this post Link to post
PeterBelow 260 Posted Friday at 10:28 AM (edited) Loosing the caret tends to happen if you programmatical set the focus to a control in code that is itself triggered by a focus transition. An OnExit event is such a code location, as is OnEnter. The common solution is to delay setting the focus until the ongoing focus transition has completed. You can do that either by posting (not sending) a custom message to the form, doing the SetFocus in the handler for that message (you can pass a reference to the target control in the message lparam), or by using a TTimer with a short interval (e.g. 250 msecs). The timer starts disabled, you enable it if you need to change focus and do that in the OnTimer event, after first disabling the timer there. In this case you have to store a reference for the control to focus either in a private field of the form or in the Tag propertry of the timer. Edited Friday at 10:30 AM by PeterBelow Share this post Link to post
Anders Melander 2068 Posted Friday at 10:41 AM 19 minutes ago, luciano_f said: Some customers complain that the system loses its cursor. You mean the caret, right? The cursor is the one you move with the mouse. The caret is the blinking indicator that shows the current position in a text control. The caret should be shown automatically if an edit control has the focus (unless it's been explicitly hidden with HideCaret (it's hasn't in this case)) so my guess is that the sequence of actions you are doing (see Peter's answer) is causing VCL to think that the control has focus while Windows is of another opinion (and of course Windows is always right, in this case). One solution would be to simply not lose the focus by showing your fading form without activating it. See ShowWindow(SW_SHOWNA). Another is the PostMessage trick Peter mentioned. I wouldn't recommend the TTimer workaround - it's a lazy hack. const MSG_DO_STUFF = WM_USER; type TMyForm = class(TForm) private procedure MsgMyStuff(var Msg: TMessage); message MSG_DO_STUFF; end; ... procedure TMyForm.MsgMyStuff(var Msg: TMessage); begin ShowDialog('It works!'); end; procedure TMyForm.Edit1Exit(Sender: TObject); begin PostMessage(Handle, MSG_DO_STUFF, 0, 0); end; Share this post Link to post
luciano_f 5 Posted Friday at 10:57 AM How could I create a global code for the entire system using my colleagues' approaches? Something like this Procedure P_SetFocus(Control : TWinControl); Begin // How could you help? End; Share this post Link to post
Anders Melander 2068 Posted Friday at 11:13 AM 14 minutes ago, luciano_f said: How could I create a global code for the entire system using my colleagues' approaches? You can't. The "global" solution to doing something that causes problems is to stop doing something that causes problems. 1 Share this post Link to post
Remy Lebeau 1642 Posted Friday at 03:26 PM 4 hours ago, Anders Melander said: Another is the PostMessage trick Peter mentioned. Or TThread.ForceQueue(), eg: procedure TMyForm.Edit1Exit(Sender: TObject); begin TThread.ForceQueue(nil, procedure begin ShowDialog('It works!'); end ); end; Share this post Link to post
luciano_f 5 Posted Friday at 05:28 PM Is there any way to know when a controller has lost its Caret ? Based on that, I could create code like this postmessage(handle, WM_NEXTDLGCTL, 0, 0); postmessage(handle, WM_NEXTDLGCTL, 1, 0); only when the problem occurs. Share this post Link to post
Anders Melander 2068 Posted Friday at 07:35 PM It hasn't "lost" the caret. It just doesn't have focus - hence no caret. Share this post Link to post
luciano_f 5 Posted Friday at 08:50 PM 1 hour ago, Anders Melander said: It hasn't "lost" the caret. It just doesn't have focus - hence no caret. And how do you identify that you don't have the focus to put a code that sends the focus to another control and then returns to it ? Share this post Link to post
Anders Melander 2068 Posted Friday at 09:47 PM 43 minutes ago, luciano_f said: And how do you identify that you don't have the focus GetFocus & SetFocus if (MyEdit.Handle <> GetFocus) then SetFocus(MyEdit.Handle); or simply if (not MyEdit.Focused) then MyEdit.SetFocus; You have all this info available in the VCL source and the Windows API. ...but why not simply avoid doing the thing that causes the problem in the first place? Share this post Link to post
luciano_f 5 Posted Friday at 10:31 PM 43 minutes ago, Anders Melander said: GetFocus & SetFocus if (MyEdit.Handle <> GetFocus) then SetFocus(MyEdit.Handle); or simply if (not MyEdit.Focused) then MyEdit.SetFocus; You have all this info available in the VCL source and the Windows API. ...but why not simply avoid doing the thing that causes the problem in the first place? I don't know what's causing the problem. My software is very large. I just simulated the problem, but I don't know if the problem is in the one exit event, as the problem is random. Your codes don't work. Test them with my example, and you'll notice that focused always returns true. Share this post Link to post
Anders Melander 2068 Posted yesterday at 01:01 AM 1 hour ago, luciano_f said: focused always returns true. That must mean that Windows' internal focus state has become messed up because Focused is implemented as: function TWinControl.Focused: Boolean; begin Result := (WindowHandle <> 0) and (GetFocus = WindowHandle); end; My guess is that Windows' internal SetFocus is implemented something like this: function WinApi.SetFocus(ANewFocusHandle: THandle): THandle; begin Result := FCurrentFocusHandle; if (FCurrentFocusHandle = ANewFocusHandle) then exit; if (FCurrentFocusHandle <> 0) then SendMessage(FCurrentFocusHandle, WM_KILLFOCUS, ANewFocusHandle, 0); // WM_SETFOCUS propagates to OnExit in Delphi SendMessage(ANewFocusHandle, WM_SETFOCUS, FCurrentFocusHandle, 0); // Update the internal focus state FCurrentFocusHandle := ANewFocusHandle; end; In Delphi, the WM_SETFOCUS message causes the OnExit event of the currently active control to fire. If you, in the OnExit handler then does something that causes the focus to change (e.g. activating another form) then the internal focus state will become invalid. So again, I suggest you focus your efforts on fixing the problem instead of working around it - large code or not. Share this post Link to post
luciano_f 5 Posted yesterday at 10:46 AM 9 hours ago, Anders Melander said: That must mean that Windows' internal focus state has become messed up because Focused is implemented as: function TWinControl.Focused: Boolean; begin Result := (WindowHandle <> 0) and (GetFocus = WindowHandle); end; My guess is that Windows' internal SetFocus is implemented something like this: function WinApi.SetFocus(ANewFocusHandle: THandle): THandle; begin Result := FCurrentFocusHandle; if (FCurrentFocusHandle = ANewFocusHandle) then exit; if (FCurrentFocusHandle <> 0) then SendMessage(FCurrentFocusHandle, WM_KILLFOCUS, ANewFocusHandle, 0); // WM_SETFOCUS propagates to OnExit in Delphi SendMessage(ANewFocusHandle, WM_SETFOCUS, FCurrentFocusHandle, 0); // Update the internal focus state FCurrentFocusHandle := ANewFocusHandle; end; In Delphi, the WM_SETFOCUS message causes the OnExit event of the currently active control to fire. If you, in the OnExit handler then does something that causes the focus to change (e.g. activating another form) then the internal focus state will become invalid. So again, I suggest you focus your efforts on fixing the problem instead of working around it - large code or not. My friend, did you understand what I've been saying all along ? My software is large, and I don't know where the problem lies. The problem occurs randomly in several places, but the modules where the problem occurs are huge, with over 20,000 lines each. I don't know if the problem is caused by an "OnExit" event. What I did here on the forum was just a simulation to demonstrate this problem of loss of focus in the VCL. Quote I don't know what's causing the problem. My software is very large. I just simulated the problem, but I don't know if the problem is in the one exit event, as the problem is random. Share this post Link to post
Anders Melander 2068 Posted yesterday at 03:48 PM At the moment, as far as I've been able to conclude from what you've written, the only lead you have, to the cause of the problem, is the OnExit pattern. If I were you, I would run with that and try to eliminate that pattern everywhere (because it's a bug in user code) it occurs. That can't be too hard; Either just search for OnExit/OnEnter and examine the handler code, or place a breakpoint in SetFocus and look for recursion. The recursion is likely what breaks the focus state. Good luck. Share this post Link to post
luciano_f 5 Posted 6 hours ago (edited) 22 hours ago, Anders Melander said: At the moment, as far as I've been able to conclude from what you've written, the only lead you have, to the cause of the problem, is the OnExit pattern. If I were you, I would run with that and try to eliminate that pattern everywhere (because it's a bug in user code) it occurs. That can't be too hard; Either just search for OnExit/OnEnter and examine the handler code, or place a breakpoint in SetFocus and look for recursion. The recursion is likely what breaks the focus state. Good luck. I think my colleague misunderstood. OnExit/OnEnter isn't the cause of my problem. The modules that have the problem don't use these events. However, I created the demo project on this forum simply because it was the easiest way to demonstrate the problem. In my ERP, this loss of focus issue is random and has been occurring for a while. Since I'd already learned about the possible causes some time ago, I eliminated all OnExit/OnEnter events from the entire software. However, the problem continues to occur this way. I need a way to eliminate it when the problem occurs. Edited 6 hours ago by luciano_f Share this post Link to post
Pat Foley 54 Posted 2 hours ago To fix your example set the tabstop to False on the Edit controls. Better to set the Key = 0 when Key = VK_RETURN is handled to let the focus move to next control (Excel style moves to next cell on enter). More important is have the tabs z-order end at a submit button that confirms the entries. Sample's editkeydown handles the Edits and a Listbox to let the user enter the right item the first rather a screen that covers not only the app but other running apps. Also the InputBox allows an edit. Also could use cmMessages or activecontrol. procedure TForm1.EditKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState); const K_Eater = 0; var ii : Integer; s :string; begin if (Key = VK_RETURN) and (Sender is TEdit) then begin var Edit := Sender as TEdit; // var CurrentEditText := Edit.Text; Edit.text := InputBox('Fixer', '?', Edit.text); if ssShift in Shift then SelectNext(Sender as TWinControl, False, True) else SelectNext(Sender as TWinControl, True, True); Key := K_Eater; // snuff the bad end; if (Key = VK_RETURN) and (Sender is TListBox) then begin var LB := TListBox(Sender); ii := LB.ItemIndex; if ii > -1 then begin s := LB.Items[ii]; LB.Items[ii] := InputBox('lister', '?', s); Key := K_Eater; end; end; end; Share this post Link to post