I am using the code below to create a messagebox in Delphi 7. However I also have another form on screen who's FormStyle is set to fsStayOnTop and the messagebox appears behind this form.
Is there any way to force the messagebox to appear in front?
if Application.MessageBox('Amessage here','Title', +MB_APPLMODAL + MB_ICONQUESTION + MB_YESNO) = IDNO then
Call NormalizeTopMosts prior to showing the message box.
Use NormalizeTopMosts to allow a message box or dialog box that is
displayed using the Windows API functions (such as MessageBox and
MessageDlg) directly, appear on top of a topmost form. Otherwise the
topmost form remains on top, and may obscure the message box.
(Hope it's available in Delphi 7.)
Edit: Not sure about the downvote. If it hints in the direction that OP should use the native MessageBox function and set its parent HWND to the topmost window - I would agree. But maybe this is not possible for some reason.
In Windows.pas you can find more flags to MessageBox():
MB_APPLMODAL = $00000000;
MB_SYSTEMMODAL = $00001000;
MB_TASKMODAL = $00002000;
Read about them in MessageBox documentation
You can even use MB_TOPMOST flag.
Try MB_TASKMODAL flag instead of MB_APPLMODAL.
Related
I have a Delphi 2006 app which can pop up a modal dialog in response to an error condition. It seems to get itself into a state where one of these modal dialogs is open, positioned in front of the main form, but neither form is responding to messages. Clicking on either gives a "bonk". The app is running fine, the UI is updating the main form, but you can't do anything. I guess there is most likely another modal dialog under the main form. Whether it is one of mine or one from Windows I have no idea.
Other points:
the app responds to keyboard shortcuts OK. One of these shortuts shuts down the app gracefully and this worked. I have been unable to reproduce the situation since.
the app has a tray icon. This responds to right mouse clicks. If I minimize the app from here the main form minimizes and leaves the modal dialog displayed, still without focus. If I restore the main form, things are as they were, with neither window having focus. Alt-tab has similar results.
platform is Windows 7
I call DisableProcessWindowsGhosting before any forms are created
I open the modal dialogs with
ModalDialog.PopupParent := MainForm ;
ModalDialog.ShowModal ;
I postpone these error dialogs if other modal dialogs are open:
if (Application.ModalLevel = 0) then
{open modal dialog}
My question has two parts:
Is there a way of programmatically finding out what window has focus? I could then take some action for this scenario or a last resort I could them provide a shortcut key to bring it to the front or take some evasive action (depending on the dialog) like set the ModalResult to mrCancel.
How can this situation arise? Normally when I get a modal dialog behind the main form (I can do that by getting the modal dialog to open, minimizing the app from the tray icon, then restoring the app again - the app main form restores in front of the dialog, with the dialog still retaining focus), I can bring it to the front again by clicking on the tray icon, or close it with the Esc key but it didn't work in this case.
**UPDATE**
Misha's fix worked apart from non-delphi dialogs like TSaveDialog. I was able to get them to work as well by adding Application.ModalPopupMode := pmAuto ; just before the call to Execute.
By "got it to work" I mean that the save dialog was in front after the following sequence:
open save dialog
minimize app from tray icon
restore app from tray icon
whereas it was behind the main form without the ModalPopupMode := pmAuto.
So I'm hoping these changes will help the (as yet unreproduced) problem.
If a form that has focus takes too long to respond to messages (Form1), so that Windows thinks Form1 is unresponsive, and Form1 then displays a modal form (Form2), after Form2 is displayed and the application is processing messages again, Form1 will be brought to the front, thereby potentially "covering" Form2.
Putting this in the Application.OnIdle event will do the trick:
if Assigned(Screen.ActiveForm) then
begin
if (fsModal in Screen.ActiveForm.FormState) and
(Application.DialogHandle <= 0)) then
begin
Screen.ActiveForm.BringToFront;
end;
end;
The last active popup window (VCL or not) can be queried with GetLastActivePopup:
function GetTopWindow: HWND;
begin
Result := GetLastActivePopup(Application.Handle);
if (Result = 0) or (Result = Application.Handle) or
not IsWindowVisible(Result) then
Result := Screen.ActiveCustomForm.Handle;
end;
This is somewhat copied from TApplication.BringToFront.
Bringing this window to the front can be done by SetForegroundWindow:
SetForegroundWindow(GetTopWindow);
Note that Application.BringToFront might do the trick altogether, but I once experienced it did not function properly, a situation I have not been able to reproduce since though.
GetForegroundWindow() is the function you are looking for, if you know the title or have the handle of the modal window it's straightforward.
HWND GetForegroundWindow();
Retrieves a handle to the foreground window (the window with which the
user is currently working). The system assigns a slightly higher
priority to the thread that creates the foreground window than it does
to other threads.
http://msdn.microsoft.com/en-us/library/windows/desktop/ms633505%28v=vs.85%29.aspx
I used Misha’s solution and worked a little further (using NGLN’s code), to solve the problems rossmcm has seen (handlings non VCL dialogs).
The following code is running I a timer:
type
TCustomFormAccess = class(TCustomForm);
if Assigned(Screen.ActiveCustomForm) then
begin
if ((fsModal in Screen.ActiveCustomForm.FormState) and
(Application.DialogHandle <= 0)) then
begin
TopWindow := GetLastActivePopup(Application.Handle);
TopWindowForm := nil;
for i := 0 to Screen.CustomFormCount - 1 do
begin
CustomFormAccess := TCustomFormAccess(Screen.CustomForms[i]);
if CustomFormAccess.WindowHandle = TopWindow then TopWindowForm := CustomFormAccess;
end;
if Assigned(TopWindowForm) and (Screen.ActiveCustomForm.Handle <> TopWindow) then
begin
Screen.ActiveCustomForm.BringToFront;
end;
end;
end;
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
OpenEdge ABL automatically close a yes/no message after a certon amount of time has pasted
I have a yes/no message box that I want to clear after 14 seconds have past. I believe the first step to do this is to get the message box's handle so that I can make it disappear
I'm afraid the simple answer is "You can't".
This is not a limitation of Progress, it is inherent in Windows message boxes no matter what programming language you use. The message box is modal to the parent application, meaning that your application freezes until the message box is closed. No matter what coding you write to close the message box, it will not run while the message box is open.
The not so simple answer offers two possibilities: To have part of your application NOT freeze when modal forms are displayed displayed requires multi-threading, which we don't talk about in polite company. The other option is to display the message box as non-modal, i.e. it does not freeze your app. To achieve this, you cannot use MESSAGE VIEW-AS ALERT-BOX, because that will ALWAYS be modal. You will have to create your own message box - a window or frame that you can display and hide as you need.
I prefer using a Frame rather than a Window. Disable the default frame, show the Message frame which contains Yes/No buttons. Your app is not frozen, but the user can only interact with the message frame. When the user clicks a button or the time is up, hide the message frame and enable the default frame again.
FRAME Message-Frame:VISIBLE = TRUE.
FRAME default-frame:SENSITIVE = FALSE.
/* do your thing */
FRAME Message-Frame:VISIBLE = FALSE.
FRAME default-frame:SENSITIVE = TRUE.
Remember to set the TOP-ONLY attribute of the message frame to TRUE, or it won't appear above the default frame.
One can use a widget's handle, or manage it directly, like so:
DEF VAR ch-field AS CHARACTER.
DEF FRAME f-test
ch-field.
ch-field:Visible = yes.
pause 14 no-message.
ch-field:visible = no.
OR - get the widget's handle, and then set it's "Visible" attribute as needed.
How can I insert the text from an Edit control into any window where the mouse pointer is, without using the clipboard and/or window handle.
I am using Delphi 7.
Call GetCursorPos to find out where the cursor is.
Call WindowFromPoint to find the window under the cursor.
Send an EM_REPLACESEL message to replace the selection in that window. If nothing is selected then the text will be inserted at the caret.
Note: Be prepared for this to fail more commonly than it succeeds. Many applications don't used windowed edit controls. For a more robust solution you should look into UI Automation.
You asked for some code. Here's what it would look like:
var
Pos: TPoint;
Target: HWND;
...
if not GetCursorPos(Pos) then
RaiseLastOSError;
Target := WindowFromPoint(Pos);
if Target<>0 then
SendMessage(Target, EM_REPLACESEL, ord(True), LPARAM(PChar(Edit1.Text)));
If the window underneath the cursor is not an edit window, this will, probably, be benign.
I'd like to highlight my toolbar icons when their associated action has it's "checked" property set to true. I'd like to do it in a manner similar to how Microsoft Office 2003 did it, e.g. see the Bold and Align Left icons in this image:
The image is drawn with a box around it and the background has been changed to orange.
How could I implement this using Delphi 2009?
As a bonus, is there also a way to do the same thing with the icon in the menu for the associated item? e.g. as below with the Ruler and Markup entries:
Followup:
Thank you Andreas, for what is a simple and correct answer to my question. As a Delphi 4 programmer converted to Delphi 2009, I was not familiar enough with the VCL additions of Delphi 5 to 7, so the TActionManager, TActionToolbar and their workings were not in my Delphi vocabulary. And this is based on Delphi's ability to use a style that is akin to a Microsoft O/S's theme.
However, with your answer, and a bit of research using it, I confirm that I do not want to do this in this manner. An illuminating Embarcadero thread re: Popup menus: "Checked" items with icon no longer shown as sunken? with Peter Below contributing his expertise, made me realize that I don't want to change to the XP theme at all, since that will do baaaaad things to Vista and Windows 7 users, taking them back to the Bronze Age.
The true culprit here is that there are certain things that the O/S themes want you to respect. Delphi tries to follow the rules. I can go back to XP, the XP style/theme, and the XP Color Map. In this mode, checked items will be hilighted. And this is really all I wanted - to highlight the checked items. I used Office 2003 as an example, because it was the sort of hilighting I wanted. But I really didn't want to entirely change themes and force an entire new look on my program. It would especially make things look unnatural for Vista and Windows 7 users.
What would be ideal is if I could have another set of Images that would apply when the object is Checked ... but there is not a "CheckedImages" property. Or a way of programmatically or automatically modifying the image if it is checked (e.g. like disabled images get a grayscale version of their enabled image).
I still wouldn't mind hearing from anyone if there are any ways to implement this or something else that might work equally well, without changing the entire look of my program.
Add a TActionManager to the form, and create some actions (e.g., bold, italic, and underline). Make sure to set the AutoCheck property to true for each action. Then add a TActionToolbar. Double-click the action manager, and drag the three actions to the toolbar. Now add a TXPColorMap to the form, and assign it to the action manager. Also add a TImageList and add icons for bold, italic, and underline (from C:\Program Files (x86)\Common Files\CodeGear Shared\Images\GlyFX\Icons\BMP\16x16). Assign the image list to the action manager.
Set the toolbar icons to show only the glyph and not the caption. Finally, set the ActionManager's Style property to XP Style. The end result is what you seek.
One can also use an ordinary TToolBar, with DrawingStyle set to dsGradient, I just found out.
Without changing the entire theme, I think this is something that might work half decently:
Add extra images to the ImageList, so that for a given action, there are two (or more) images to choose from.
Instead of changing the "checked" property to true or false, change the ImageIndex to the alternative image, e.g.:
if WS = 1 then begin
ElTree.Align := alTop;
// TileTopBottomAction.Checked := true; --- take this out
// TileLeftRightAction.Checked := false; --- take this out
TileTopBottomAction.ImageIndex := 47; { hilighted image }
TileLeftRightAction.ImageIndex := 14; { regular image }
end
else begin
ElTree.Align := alLeft;
// TileTopBottomAction.Checked := false; --- take this out
// TileLeftRightAction.Checked := true; --- take this out
TileTopBottomAction.ImageIndex := 13; { regular image }
TileLeftRightAction.ImageIndex := 48; { hilighted image }
end;
Now the images will look like this on the toolbar:
and like this on the menu:
The nice things about this method is that it also works on the menu, and you can have multiple images to represent what you want. Also, it will not wreck the theme (XP, Vista, Windows 7, etc) that the program has taken on.
The disadvantage of this method is: You are limited to 16x16 image area to play with and cannot draw a box around it that are outside those limits as happens when you set the "checked" property to true.
I'm using Peter Below's PBThreadedSplashForm to display during application startup. It gets updated as various databases are opened during the creation of the data module (just before the main form is created).
The problem is that we have a check for the existence of certain things that is done during the creation of the data module. If any of those items are missing, a modal dialog is shown so that either the item can be created or the application closed. (The missing items are typically indexes, and the dialog is primarily used when we reindex databases, so it's aimed at the developers and not normal users.) However, the modal dialog is displayed behind the splash screen; it appears to be the focused window, as the caption changes, but it displays behind the splash screen.
The splash screen is created using the Windows API CreateWindowEx() from a different thread than the main process, using the following code:
procedure TPBSplashThread.CreateSplashWindow;
const
TopmostStyle: Array [Boolean] of DWORD = (0, WS_EX_TOPMOST );
NoActivateStyle : Array [Boolean] of DWORD = (0, WS_EX_NOACTIVATE );
var
wsize: TSize;
begin
wsize.cx := FSurface.Width + GetSystemMetrics( SM_CXEDGE ) * 2;
wsize.cy := FSurface.Height + GetSystemMetrics( SM_CYEDGE ) * 2;
FWnd := CreateWindowEx(
TopmostStyle[ FTopmost ] or WS_EX_TOOLWINDOW
or WS_EX_STATICEDGE or WS_EX_CLIENTEDGE
or NoActivateStyle[ Win32MajorVersion >= 5 ],
MakeIntResource( FWndClass ),
nil,
WS_POPUP or WS_BORDER,
Forigin.x, Forigin.y,
wsize.cx, wsize.cy,
0, 0, hInstance, self );
If FWnd = 0 Then
raise exception.create('TPBSplashThread.CreateSplashWindow: CreateWindowEx failed');
end;
FTopmost is a property (with the obvious meaning) that is never set, so it's False due to the default initialization during class construction. (I've also tried explicitly setting it to False, just in case, with no effect.) I've also tried using SetForegroundWindow() and SetWindowPos() during the OnShow event of the modal dialog with no effect.
Does anyone have any idea what might be causing this?
Ok. Finally got the problem solved. It appears to be caused by the WS_EX_NOACTIVATE flag in the call to CreateWindowEx(). Changing it to remove that seems to solve the problem; the modal dialog displays above the splash screen, and because it's modal the splash screen can't be brought above it.
The working code is:
FWnd := CreateWindowEx(
TopmostStyle[ FTopmost ] or WS_EX_TOOLWINDOW
or WS_EX_STATICEDGE or WS_EX_CLIENTEDGE,
MakeIntResource( FWndClass ),
nil,
WS_POPUP or WS_BORDER,
Forigin.x, Forigin.y,
wsize.cx, wsize.cy,
0, 0, hInstance, self );
Thanks, Rob and Craig, for the efforts.
If you're looking for an explanation, I can't help you.
If you're looking for a solution, you're not the first. Eddie Shipman encountered this same problem in May. His proposed solution was to make the other dialog (the one hidden by the splash screen) be a topmost window, but he ultimately avoided the issue by hiding the splash screen before the application needed to display any other windows.
Another suggestion was to post commands to the splash screen and have it display the message box. The dialog could be parented to the splash screen. It's tricky, though, because you're not afforded any of the luxuries of the VCL anymore since it doesn't work outside the main thread.
You should be able to fix this by parenting the dialog to the splash screen. Assign the splash screen's HWND to a global variable, and override the dialog's CreateParams method. If the global HWND has a value assign it to the Params.WndParent variable that CreateParams passes in. The fact that they're from different threads shouldn't matter, since it's just dealing with the HWND, not a VCL object, and Windows will handle the synchronization.