Unusual form's appearance after calling TCustomForm.SetFocusedControl function - delphi

First of all, my goal isn't that to set the focus on a control but I'm trying to understand why a form has a different appearance after deactivating and reactivating it.
Here is TCustomForm.SetFocusedControl function's description:
Sets focus to a control on the form.
Use SetFocusedControl to give a control on the form input focus. SetFocusedControl returns false if the Control specified by the Control parameter was already in the process of receiving focus, true otherwise.
Note: A return value of true does not indicate the control has successfully received input focus. If the Control can't have focus (for example, if it is not visible), SetFocusedControl will still return true, indicating that an attempt was made.
I've created a simple test application in order to reproduce the observed behavior:
procedure TForm1.Button1Click(Sender: TObject);
var
Res : Boolean;
begin
Res := Self.SetFocusedControl(Edit2);
if(Self.ActiveControl <> nil) then
begin
Memo1.Text :=
'ActiveControl is ' + Self.ActiveControl.Name + sLineBreak +
'SetFocusedControl result is ' + BoolToStr(Res, True);
end;
end;
Here are steps I followed to reproduce that behavior:
1) At start, the form appears as follows:
2) After clicking on "Button1", this is what I can see:
Memo1.Text reports that Edit2 is the active control but it hasn't the tipical focused appearance (selection and cursor).
Form's caption isn't grayed and clicking on it doesn't cause any change.
3) I've clicked outside the form (on the windows taskbar).
Form's caption became grayed.
4) I've reactivated the form by clicking on its caption:
Edit2 now looks like it has focus.
Could someone explain the differences between the form at point 2 and at point 4? In both cases, Edit2 was the active control and I can't understand that appearance difference.
Further informations:
Tested on Delphi 2007, Windows 10 Pro.

Despite what the documentation says, TForm.SetFocusedControl() does not actually set input focus onto Edit2 (the Win32 SetFocus() function is not called). Button1 receives the input focus when clicked on, and remains in focus after SetFocusControl() is called. That is why Edit2 does not render as focused. If you want to move input focus to Edit2, call Edit2.SetFocus() instead of Self.SetFocusedControl(Edit2).
However, calling SetFocusedControl() does change the VCL's internal state. It sets the Form's ActiveControl and FocusedControl properties, and it sets the global Screen object's ActiveControl, ActiveCustomForm, ActiveForm, and FocusedForm propeties and reorders the entries of its CustomForms and Forms properties.
When the Form is deactivated and then reactivated, the VCL sets input focus to the last known "focused" control, which is now Edit2 instead of Button1.

Related

Setting the WindowState is showing the form

Many of my application forms inherit from a base form which loads the saved window size, position and state recorded during FormClose and applies it during FormShow.
The inherited; line in the descendant version of FormShow is in the usual place at the start of the method, and is generally followed by a fair amount of code that requires the visual components on the form to have been created so they can be manipulated and set up.
The problem I am having is that the form is usually hidden until the end of the descendent FormShow event, which is the expected behaviour, unless the WindowState is being set to wsMaximized in the ancestor class FormShow event. In that case, the form becomes visible as soon as the inherited; line is executed, and you get to watch as the remaining visual elements get organised.
When setting the WindowState property of a VCL.Forms.TForm, the following code is executed:
procedure TCustomForm.SetWindowState(Value: TWindowState);
const
ShowCommands: array[TWindowState] of Integer =
(SW_SHOWNORMAL, SW_MINIMIZE, SW_SHOWMAXIMIZED);
begin
if FWindowState <> Value then
begin
FWindowState := Value;
if not (csDesigning in ComponentState) then
begin
if Showing then
ShowWindow(Handle, ShowCommands[Value])
else if HandleAllocated and (FWindowState = wsMaximized) then
RecreateWnd;
end;
end;
end;
The apparent cause of the issue is somewhere in that method; either the ShowWindow or the RecreateWnd, which seems to be triggering the form to be immediately painted.
Apart from moving my LoadFormState(Self); method from TBaseForm.FormShow to TBaseForm.FormActivate, is there any other way to set the form to be maximized without it actually showing the form?
You should not be calling your LoadFromState procedure either from TBaseForm.FormShow or TBaseForm.FormActivate.
TBaseForm.FormShow is called every time visibility of your form changes from False to True.
So for instance if you hide your from when showing another and then show it again after another form is closed TBaseForm.FormShow will fire and thus you will load form's dimensions and position from the saved state which might not be the same state as of when the form was hidden. User might have moved and resized the from since application was started.
This will lead to form moving its position without users wish to do so and thus it will annoy your users as hell.
Also don't use TBaseForm.FormActivate as this fires every time Form receives focus. So changing focus from another back to this one will the TBaseForm.FormActivate and thus change your form dimensions and position to the saved state which might not be teh state when form lost its focus.
The correct place for you to call your LoadFormState procedure with which you load initial properties of your form is within the forms constructor.

How do I assure that mouse pointer appears when mouse moves over form after commanding the drop down for a combobox to show?

I have a problem where:
I have a form with just a combobox.
The combobox has focus and mouse is not hovering over the form when item 3 happens.
I trigger the combobox's drop-down list to show on a key-press event.
When the drop-down list is visible and then I move my mouse pointer over the form, the pointer is either invisible, shows that it is busy, or shows the resizing icon but does not turn back to a normal pointer when over the form.
Is there something that can be done to assure that, when the drop-down of the combobox shows, that the mouse pointer is visible when I move the pointer over the form?
I have tried:
Applicaiton.ProcessMessages after showing the drop-down.
Changing focus to the form the combobox is on after showing the drop-down.
Adding Key := #0; after calling the drop-down to show.
procedure TForm1.ComboBox1KeyPress(Sender: TObject; var Key: Char);
begin
SendMessage(ComboBox1.Handle, CB_SHOWDROPDOWN, Integer(True), 0);
Key := #0;
end;
Tried using a timer to trigger the drop-down within the key-press event.
Tried using "SetCursor" after commanding the drop-down to appear.
Tried using ".DroppedDown", but did not see any difference in result from that of "SendMessage".
I would hope to be able to show the mouse pointer after the drop-down is displayed, but it is hidden instead. Thanks for any suggestions.
(NOTE: This problem I have run into is not exlusive to Delphi. I was able to duplicate the issue using Visual C# 2017. Either way, if there is a way to correct this, it would be good to know).
As already commented to the question, the issue is not Delphi related. You can observe the same behavior in dialog boxes which contains a similar combo that the OS presents. One example is the one on the "run" dialog.
Involving a single environment, re-setting the cursor in an OnDropDown event handler fixes the problem.
procedure TForm1.ComboBox1DropDown(Sender: TObject);
begin
winapi.windows.SetCursor(Screen.Cursors[Cursor]);
end;
Originally I tested the above because no one calls SetCursor after the drop down. Though it seems that no one calls it before either. So I have no idea about the cause or why the above fix works.

How do I make a TVirtualStringTree process key presses with a higher precedence?

We've got a certain search form at work that was revamped recently. Its functionality is that of a standard search form: enter a few criteria in some boxes at the top, hit the Search button, and display results in a grid down below. But it was ugly and very limited in functionality, so one of my coworkers rebuilt it... right before leaving for a new job. Now I'm trying to complete the last few details.
One of the changes was replacing the old TListBox grid with a much more powerful TVirtualStringTree. But in the process, it appears to have broken something: before, if you clicked on a row in the grid (giving the grid in put focus) and hit Enter, the appropriate event handler would fire and deal with your input, opening the detail view for the selected item. In this grid, however, pressing Enter causes the TButton on the form with the Default = true property to fire its OnClick instead.
How can I make the TVirtualStringTree take precedence when it has input focus, so that it will respond to the user pressing Enter itself before (and preferably instead of) dispatching it to the form?
Handle the WM_GETDLGCODE message and include DLGC_WANTALLKEYS in the returned value. For example:
procedure WMGetDlgCode(var Message: TWMGetDlgCode); message WM_GETDLGCODE;
....
procedure TMyControl.WMGetDlgCode(var Message: TWMGetDlgCode);
begin
inherited;
Message.Result := DLGC_WANTALLKEYS;
end;
Depending on whether or not your control already handles this message and returns something other than 0 you might need to use:
Message.Result := Message.Result or DLGC_WANTALLKEYS;
If you don't want to modify the code for this class then use an interposer or set the WindowProc property of the control to intercept its window procedure.

How do I display a label in an edit box, but switch to password-entry mode when it receives focus?

I use Delphi 10 and Windows 10.
The following code makes caret and selection disappear in Edit1.
procedure TForm1.Edit1Enter(Sender: TObject);
begin
Edit1.PasswordChar := '*';
end;
After the focus moves to the other control and in onClick it works well.
I can't use onClick because the focus moves by tab key and the Edit1 should start with default #0 because it holds text which is 'password' before the focus enters.
How can I solve this?
The edit control works as designed and as expected.
If you want the control to hide a password then set then TEdit.PasswordChar in the OI or on creation or ... but not every time you enter the control
If you want to have a hint then set the TEdit.TextHint property which will be shown if TEdit.Text is empty and the control is not focused

"Stay on top" main form and modal dialogs in Delphi XE

In Delphi XE Update 1, I’m getting seemingly random behavior of modal forms if the parent (main) form’s FormStyle is set to fsStayOnTop.
1) With MainFormOnTaskbar := False (the old way), everything “just works”. With the new MainFormOnTaskbar := True, modal forms get hidden behind the main form when the main form is set to “stay on top”. In most cases saying
modalForm.PopupParent := self;
just before the call to modalForm.ShowModal seems to help. But not always.
2) All my modal forms are simple, no frills, positioned at MainFormCenter, not using form inheritance, etc. And yet the PopupParent fix only works for about half of them, while the other half still get hidden behind the main form. Strangest of all, in one case the ordering of unrelated lines of code breaks or makes it. See lines marked (1) and (2) in this code:
procedure TEchoMainForm.DBMaintenancePrompt( actions : TMaintenanceActions );
var
frm : TDBMaintenanceForm;
begin
frm := TDBMaintenanceForm.Create( self );
try
frm.Actions := actions; // (1)
frm.PopupParent := self; // (2)
frm.ShowModal;
finally
frm.Free;
end;
end;
When executed in this order, the modal form shows correctly on top of the main form. But when I reverse the lines, the modal form hides behind main. The line marked (1) sets a property of the modal form, which results in several checkboxes being checked on unchecked in a TRzCheckGroup, sitting on a TRzPageControl (from Raize components). This is the setter method that runs when line (1) above executes:
procedure TDBMaintenanceForm.SetActions(const Value: TMaintenanceActions);
var
ma : TMaintenanceAction;
begin
for ma := low( ma ) to high( ma ) do
cgMaintActions.ItemChecked[ ord( ma )] := ( ma in Value );
end;
end;
This is enough for the modal form to show behind the main form if the order of the lines (1) and (2) is reversed.
This might point to TRzCheckGroup (which gets manipulated when the setter code runs), but I have two other forms that show the same problem and do not use TRzCheckGroup (or TRzPageControl). And I could not reproduce the problem with a separate sample app using Raize components. Disabling the form, the pagecontrol or the TRzCheckGroup for the duration of the setter has no effect.
It does not appear to be a timing issue, because when the modal form shows hidden once, it always does. The change in behavior only comes from rearranging the lines of code.
3) One last observation: my modal forms are fairly simple, so they get displayed pretty much instantly, with no visible delay. But when the main form is fsStayOnTop, then very often I can see the modal form show on top of it, then see it get “pushed” behind. Then, on hitting Esc, the (invisible) modal form shows on top of the main form for a fraction of a second, then gets closed.
Either I‘m missing something that’ll seem obvious in hindsight, or this is a call for psychic debugging, I don’t know. Any ideas, please?
UPDATE. I’ve tried to track down the problem on another form where it occurs. It has a few buttons (Raize) and a TSyntaxMemo (an enhanced memo component from eControl.ru). This form has almost nothing in common with the other forms that experience the problem. After removing parts of the code and testing, I can now reproduce the problem by making a tiny change in a method that assigns a string to the memo component:
This is my original code, which causes the form containing the editor to hide behind the main form:
procedure TEditorForm.SetAsText(const Value: string);
begin
Editor.Text := Value;
end;
When I change the assignment to an empty string, the form displays correctly:
procedure TEditorForm.SetAsText(const Value: string);
begin
Editor.Text := ''; // CRAZY! Problem goes away
end;
When I assign a single character to the editor, the form starts hiding again:
procedure TEditorForm.SetAsText(const Value: string);
begin
Editor.Text := 'a'; // Problem is back
end;
Of course the other two problematic forms do not use this editor component or any of its units.
I've tried deleting the memo control and adding it again (think creation order etc.), but it had no effect. Same if I create the memo in code. The form hides as soon as a non-empty string is assigned to the memo's Text property.
I had the same issue some time ago. My solution was to add a Self.BringToFront; to the OnShow event of the modal form.
Windows does not support many top most forms for app. And modal form is topmost by default. But you have this style for your own form.
One decision in mind: remove top most of your main form (no visible effect), call modal form, set back topmost style when modal finished.

Resources