Jump to content
luciano_f

Bug VCL I'm losing my TControls course ?

Recommended Posts

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 by luciano_f

Share this post


Link to post

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 by PeterBelow

Share this post


Link to post
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

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
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.

  • Like 1

Share this post


Link to post
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

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
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
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
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
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
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

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
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 by luciano_f

Share this post


Link to post

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

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×