I implement this chevron tool bar in my application and it works perfectly great; however, when I clicked on any items on the menu, my application loses focus. Even if I move my mouse over the the corner of the form, the cursor doesn't change to the resize handle. I need to click on the form or application in order to regain focus which I would not like to have to do. Calling MainForm.Setfocus after calling the menu item doesn't help. I would like to have the focus be automatically on my application so my users don't need to click on the form before doing the things they need to do.
Any idea on how to regain focus on the form and/or application?
Thanks
Intercept the WM_KillFocus message.
pseudo code
don't have Delphi on this terminal, will fill in the blanks when home.
type
TForm1 = class(TForm)
...
protected
procedure WMKillFocus(message: TWM_Killfocus); message WM_KillFocus;
...
procedure TForm1.WMKillFocus(message: TWM_Killfocus);
begin
//do stuff to prevent the focus from shifting.
//do *NOT* call SetFocus, it confuses Windows/Delphi and leads to suffering
//Call PostMessage or handle the KillFocus message
//From MSDN
//While processing this message, do not make any function calls that display
//or activate a window. This causes the thread to yield control and can
//cause the application to stop responding to messages. For more information
//see Message Deadlocks.
//Also don't use SendMessage, PostMessage is OK though.
//Suggestion:
PostMessage(Self.handle, WM_SETFOCUS, 0, 0);
end;
Related
I am using TWebBrowser in my Delphi firemonkey application and would like to disable right click on the page.
Is there any way for this.
The default TWebBrowser for Firemonkey doesn't do this natively as of 10.3 Rio. Outside of a different browser component, your best bet is to use Javascript. If you are controlling the content that is served, that's pretty easy. See How do I disable right click on my web page?
If you are dealing with another website whose content you don't have control over, you can try to inject the Javascript using TWebBrowser.EvaluateJavaScript()
procedure TForm1.DisableRC;
var
strJS: string;
begin
strJS := 'document.addEventListener("contextmenu", function(e){ e.preventDefault();}, false);';
webbrowser1.EvaluateJavaScript(strJS);
end;
The code works if you call DisableRC; from say, a button click. But if the URL reloads or content changes, you will need to call it again.
I tried placing a call to DisableRC() in the TWebBrowser.OnDidFinishLoad event, to execute it after page navigation completes, but the event ended up firing thousands of times in an infinite loop. Using TThread.Queue made no difference. Possibly this is because the evaluation of Javascript makes the event fire again.
What ended up working was placing a TTimer on the form, disabled by default, with the following code in OnTimer:
procedure TForm1.Timer1Timer(Sender: TObject);
begin
DisableRC;
Timer1.Enabled := false;
end;
And then enabling the Timer in the TWebBrowser.OnDidFinishLoad event.
It's somewhat of a hack, but hopefully it is helpful to get you started in your implementation.
I believe you may be able to modify this code and trap the message WM_RBUTTONDOWN. Replace the WM_LBUTTONDOWN in the referenced code with WM_RBUTTONDOWN. I'd try it but i only have C++ Builder installed.
I am working with Delphi XE7 in Windows 10. I have a TMainMenuBar on both a primary form and a modal secondary form. The problem is that the accelerator keys on the secondary form do not activate the menu if the secondary form also contains a TMemo. For example, if the secondary form has a File menu, Alt+F does not open the file menu. However, if alt is pressed and released, "File" is highlighted and "F" is underlined and pressing F will open the menu. Note that the problem disappears if the TMemo is removed. Also, a Tmemo on the primary form does not cause problems with the menu on the primary form.
I have searched on "TActionMainMenuBar Accelerator Keys on Secondary Form" but none of the hits describe this particular problem, although other issues with this component and accelerator keys are discussed. Does anyone know how to achieve the desired behavior while still using TActionMainMenuBar on both forms? (I prefer to not use a standard TMenu for various reasons.)
This is a VCL design issue. (Below explanation maybe somewhat off in parts. I'm tracing with XE2 and the behavior is not exactly equivalent. You may need to leave off one of the message handlers at the solution part.)
Menu bar accelerators generate a WM_SYSCOMMAND message. Your exact accelerator case is given as an example in API documentation:
If the wParam is SC_KEYMENU, lParam contains the character code of the
key that is used with the ALT key to display the popup menu. For
example, pressing ALT+F to display the File popup will cause a
WM_SYSCOMMAND with wParam equal to SC_KEYMENU and lParam equal to 'f'.
Action menu bars are proprietary VCL components. As such default window procedure of a form have no chance to handle an accelerator message for them. The component itself have the code to simulate the behavior (TCustomActionMainMenuBar.WMSysCommand), but it has to be delivered the message to be able to do that. VCL's design issue is that, only an action menu bar on the main form is given that opportunity.
A TWinControl receiving a WM_SYSCOMMAND (secondary form itself or the memo in this case), makes its parent form (secondary form) perform a CM_APPSYSCOMMAND. Upon receiving the message, the form (again the secondary form) sends the message to the Application window. CM_APPSYSCOMMAND handler of the Application, transforms the message again to a WM_SYSCOMMAND and sends it to the main form.
I can only guess, but the purpose behind this design can be, to be able to access the main menu from secondary forms that don't have menu bars.
In any case, short of switching to native menus, you have to intercept and forward the message to the action menu bar on the secondary form before the message is processed by the VCL. There can be other ways to accomplish this, but what I tried and seemed to work is this:
type
TSecondaryForm = class(TForm)
...
protected
procedure CMAppsyscommand(var Message: TMessage); message CM_APPSYSCOMMAND;
procedure WMSysCommand(var Message: TWMSysCommand); message WM_SYSCOMMAND;
...
procedure TSecondaryForm.CMAppsyscommand(var Message: TMessage);
begin
if ActionMainMenuBar1.Perform(PMessage(Message.LParam).Msg,,
PMessage(Message.LParam).WParam, PMessage(Message.LParam).LParam) = 0 then
inherited;
end;
// you may not need the below handler
procedure TSecondaryForm.WMSysCommand(var Message: TWMSysCommand);
begin
if ActionMainMenuBar1.Perform(Message.Msg,
TMessage(Message).WParam, TMessage(Message).LParam) = 0 then
inherited;
end;
I have this awkward situation that causes an exception. I've found the problem but the solution is so far tricky to implement for me.
In our application, if a user stays in-active for a certain period of time, the security time-out procedure kicks in to prompt the user the password entry box.
However, whenever a form has a message box displayed during the FormShow() event for any particular reason (thing to pay attention here; the formShow event execution hasn't been fully completed yet) and the user decided to not click the OK button of the dialog box for a certain time, the security code kicks in and tries to hide all forms so that it can prompt a password.
This scenario will trigger the exception "Cannot change Visible in OnShow or OnHide".
Security code loops all forms using TScreen.FormCount and hides them using TForm(TScreen.Forms[ii]).Hide individually. Hide procedure will cause the exception, because I think this form has not completed it's loading procedure fully yet.
I've done tests, and if I display a message box after the FormShow() event is executed, the security code works perfectly fine and hides all windows without any issues.
I've tried several properties and window message checking to do an "if check" before hiding forms, like Screen.Forms[ii].Visible, Screen.Forms[ii].Active but no luck so far. The mentioned form will be visible and there's no guarantee that it will be active, and if it's active how am I going to hide other non active forms. So my question is, which property or Windows message would indicate that a form is fully loaded, or at least it has past the FormShow event of any given form that exists in TScreen.Forms?
I need an answer to what I am asking please, I need a generalized solution that needs to be implemented in the security code, I can't go through over a thousand forms we have in this giant application and individually try to find solutions to any validation/warning logic exist in those forms.
Thank you
The simple answer is to stop showing the modal dialog in the OnShow of the owner form. Wait until the form has finished showing before you display the modal dialog. If you make that change, and that change alone, your existing code will start to work.
The question title you chose was:
Can't hide a window that's not been fully initialized
And so the obvious solution is to wait until the window has been fully initialized.
The simplest way to achieve this is to move your code that currently runs in OnShow into a handler for CM_SHOWINGCHANGED:
procedure CMShowingChanged(var Message: TMessage); message CM_SHOWINGCHANGED;
Implement it like this:
procedure TMyForm.CMShowingChanged(var Message: TMessage);
begin
inherited; // this is what invokes OnShow
if Visible then
begin
// do what you previously did in OnShow
end;
end;
David Heffernan's solution gave me an idea and I solved this issue on my end.
I created following;
const
WM_SHOW_MESSAGE = WM_USER + 1;
private
procedure WMShowMessage(var Msg: TMessage); message WM_SHOW_MESSAGE;
Inside constructor;
PostMessage(Handle, WM_SHOW_MESSAGE, 0, 0);
And this will have the message box logic:
procedure MyMethod.WMShowMessage(var msg: TMessage); message WM_SHOW_MESSAGE;
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Splash Screen Programatically
Show a splash screen while a database connection (that might take a long time) runs
Which is the best place to initialize code such as loading INI file? I want first to show the form on screen so the user know that the app is loading and ONLY after that I want to call lengthy functions such as LoadIniFile or IsConnectedToInternet (the last one is REALLY slow).
The OnCreate is not good because the form is not yet ready and it will not show up on screen.
I do this I DPR but not working always:
program Test;
begin
Application.Initialize;
Application.Title := 'Test app';
Application.CreateForm(TfrmTest, frmTest);
frmTest.Show; <---------------------- won't show
LateInitialize;
Application.Run;
end.
The form will not show until LateInitialize (4-5 seconds) is executed.
procedure LateInitialize;
begin
CursorBussy;
TRY
// all this won't work also. the form won't show
frmTest.Visible:= TRUE;
Application.ProcessMessages;
frmTest.Show;
Application.ProcessMessages;
frmTest.BringToFront;
frmTest.Update;
Application.ProcessMessages;
DoSomethingLengthy; {4-5 seconds}
FINALLY
CursorNotBussy;
END;
end; <--------- Now the form shows.
And yes, frmTest it is my only form (the main form).
After calling frmTest.Show, you can call frmTest.Update to let it render onscreen, before then calling LateInitialize. But until Application.Run is called, the main message loop will not be running, so the form will not be able to do anything else until then.
Another option is to use the form's OnShow event to post a custom window message back to the form via PostMessage(), then have the form call LateInitialize when it receives that message at a later time. That will allow the form to process painting messages normally until LateInitialize is called.
Anything that blocks the main thread for more than a few milliseconds/seconds really should be moved into a separate worker thread instead (especially things like IsConnectedToInternet). The main thread should be used for running the UI.
An easy way to do this, is to send a message to yourself.
I do this all the time
const
MSG_AFTERCREATE = WM_APP + 4711;
...
procedure OnCreate(Sender: TObject);
procedure AfterCreate(var message: TMessage); message MSG_AFTERCREATE;
...
Implementation
procedure OnCreate(Sender: TObject);
begin
PostMessage(Self.Handle, MSG_AFTERCREATE, 0, 0);
end;
procedure AfterCreate(var message: TMessage);
begin
//Do initializing here... the form is done creating, and are actually visible now...
end;
Variant 1: Use TTimer with a 1 second delay, run it from main form's OnShow
In TTimer do the initialisation
This will give time for most components to initialize and draw themselves
Variant 1.1: use message method in function and call Win API PostMessage (but not SendMessage aka Perform) from OnShow. This is seemilar but more cheap and fast. However that message "do init now" sometimes may be received before some complex component on the form would fully draw itself.
Variant 2: use threads (OmniThreadsLib or even plain TThread)
Launch it from MainForm OnCreate and let it prepare all data in background, then enable all needed buttons, menus, etc
That is truly the best way if you have long and blocking functions, liek you described IsConnectedToInternet.
Variant 3: use SplashScreen before showing main form.
That is good because users see that application not read yet.
That is bad for that very reason - people start feeling your program is slow. Google Chrome was told to draw their main form as picture in 1st moments just to make look "we are already started" even the actual control would be ready a bit later.
A long time ago in another forum far far away, someone posted the following to document the life cycle of a form. I have found it useful, so am sharing it here.
Create OnCreate
Show OnShow
Paint OnPaint
Activate OnActivate
ReSize OnResize
Paint OnPaint
Close query OnCloseQuery
Close OnClose
Deactivate OnDeactivate
Hide OnHide
Destroy OnDestroy
Try the OnActivate event.
I'm having trouble with the following scenario:
2 Edit's
Type something in Edit1 and press TAB, focus goes to Edit2
Edit1.OnExit -> show a Form with a message "Processing..." (makes a lengthy validation)
After the form closes, the focus on Edit2 seems to be "crashed"...
- the hole TEXT in Edit2 isn't selected
- the carret isn't flashing
Example:
Create a new form
Put 2 edits
Set this as OnExit event in Edit1:
procedure TForm1.Edit1Exit(Sender: TObject);
begin
with TForm.CreateNew(self) do
try
Width := 100;
Height := 50;
Position := poMainFormCenter;
show;
sleep(200);
finally
Free;
end;
end;
Run the application
Set focus in the Edit1 and press TAB
I'm using:
Delphi 7 Enterprise
Windows 7 x64
This is a known problem. Windows has problems when you change focus before it's completed the last focus change (eg., focus starts changing from Edit1 to Edit2, but Edit1.OnExit does something to change focus to another control or form.
This happens, for instance, when apps try to do validations in an OnExit event and then try to return focus to the original control when the validation fails.
The easiest solution is to post a message to your form handle in the OnExit instead, and handle the focus change need there. It will fire once the target control gets the input focus, and Windows doesn't get confused.
const
UM_EDIT1_EXITED = WM_USER + 1;
type
TForm1=class(TForm)
...
private
procedure UMEdit1Exited(var Msg: TMessage); message UM_EDIT1_EXITED;
end;
implementation
procedure TForm1.Edit1Exit(Sender: TObject);
begin
PostMessage(Handle, UM_EDIT1_EXITED, 0, 0);
end;
procedure TForm1.UMEdit1Exited(var Msg: TMessage);
begin
// Show your other form here
end;
From an old Borland NG post by Dr. Peter Below of TeamB:
here is my general sermon on the "show dialog from OnExit" problem:
If an OnExit handler is triggered (which happens in response to the
Windows
message WM_KILLFOCUS) Windows is in the midst of a focus change. If you do
something in the handler that causes another focus change (like popping up
a message box or doing a SetFocus call) Windows gets terribly confused.
The
missing cursor is a symptom of that.
If you have to display a message to your user from an OnExit handler, do
it
this way:
Define a constant for a user message somewhere in the INterface
section
of your unit, above the type declaration for your form
'Const
UM_VALIDATE = WM_USER + 200;'
Give your Form a handler for this message, best placed in the private
section of the class declaration:
Procedure UMValidate( Var Msg: TMessage ); message UM_VALIDATE;
Post a UM_VALIDATE message to the form from the OnExit handler if
the contents of the field are not ok. You can pass additional
information in the wparam and lparam parameters of the message, e.g.
an error number and the Sender object. In fact you could do the whole
validation in the UMValidate handler!
I'm not sure precisely what's going on here, but it looks like the order of processing of messages is a bit messed up. Instead of killing your other form with Free, use Release and the focus will behave as you desire.
Another option is to use ShowModal instead of Show. Normally you show a processing dialog modally because you don't want the user making modifications to the main form whilst you are processing. If you do that then you can carry on using Free.