Minimize Delphi Application with Live Popup Menu - delphi

I'm attempting to run this in a timer:
Application.Minimize;
ShowWindow( Application.handle, SW_HIDE );
It's been in the code forever and we just discovered that it doesn't work when you have a popupmenu active, it doesn't minimize the MDI parent window.
I figure if I can close the popup menu before running this code, then I'll be ok. Problem is, this code is in an MDI Parent and I have no idea where the current popup menu is. It doesn't matter if it's part of another form's tool bar, this forms tool bar, the product of a right click or that seemingly pointless key next to the space bar.
So, is there a way to hide the active popup menu in my entire program?
Also, if there's a better hunk of code than what I'm using to minimize that'll circumvent this issue, that'd be awesome info too.

To close a popup menu you can use
if GetCapture <> 0 then
SendMessage(GetCapture, WM_CANCELMODE, 0, 0);
in your code before you try to minimize the form.

Related

Dynamically showing and hiding main menu doesn't immediately apply

My Delphi application initially starts by showing the main form, but with a user login panel only. I hide the main menu, so that the unauthenticated user is not able to access it. Upon user login, I dynamically assign the menu by setting Form.Menu := MyMainMenu;.
However, when the user logs in, and I assign the main menu as mentioned, it isn't immediately visible until the form is resized in some way. Minimizing the window and showing it again also triggers the main menu to show. However, I need it to show right away when I assign it.
I have tried adding the following attempts just after assigning the main menu, but makes no difference:
Self.Repaint;
Self.Refresh;
Application.ProcessMessages;
The only thing I can do is to slightly resize the form in run-time, then it triggers it to show. However, not only is this sloppy, but my application shows in the Maximized state by default.
Note: I am using VCL Styles. Without styles, it shows just fine. Trying Vcl-Styles-Utils and its fixes does not make a difference.
How do I get the newly assigned main menu to immediately show without a "resize" hack?
On a side note, when I close the application, I have a memory leak:
Without VCL Styles, this memory leak doesn't occur. Not that I'm asking for a solution for that, but an additional symptom which might help identify the root issue.

Delphi MDI Child forms and MainMenu

We are upgrading a Delphi 5 application to Delphi XE7. The application has a number of MDIChild (FormStyle fsMDIChild) forms which open with WindowState wsNormal. When I open one of these forms, and close it again, my main menu (TMainMenu) on the main form (FormStyle fsMDIForm) aligns itself to the right and never goes back again. If I resize the MDIChild form before closing it (eg. click on "Normal" or "Minimize" icon), this does not happen.
This does not happen with the Delphi 5 version, so I assume something gets handled differently with XE 7.
Anyone else experienced this before? If so, how did you fix it?
UPDATE:
I have successfully replicated this problem:
Create a new VCL app
Make main form (MainForm) formStyle MDIForm
Create MainMenu for MainForm with menu items
Create second form (ChildForm)
Make ChildForm formStyle MDIChild
Create MainMenu for ChildForm with menu items
IMPORTANT (THIS IS WHAT BREAKS IT): Select for ChildForm BorderIcons only biSystemMenu and biMaximise
Create button on MainForm which creates and shows ChildForm
Run application
Click button
Maximise ChildForm
Close ChildForm (Has to be maximised otherwise malfunction does not occur)
Observe that MainForm's menu is now right-aligned with an unclickable 'Minimise' icon on the left If biMinimise is added to ChildForm's BorderIcons, the problem disappears.
I'm pretty sure this is a bug. Correct me if I'm wrong.
Thanks
J
The behaviour that you report is not observed when creating a brand new project, and creating forms in the manner that you describe.
Clearly then, there is some code in your project that is causing this issue. You need to do some debugging to identify this problem code. Start by stripping code away until the issue vanishes. The last code that you stripped away should contain the clues to lead you to the cause of thee problem. Continue in this manner until you have isolated the problem.

How can I make a form visible and maximize it to fill a secondary monitor without it activating?

I have an application which is designed for multiple monitors. It starts up, and we try to avoid activating windows that do not need to be activated, because the user only does keyboard input in one place, and each time we Activate a new form on a secondary monitor, it grabs keyboard focus, something we wish to avoid.
Our in-house base TForm class has a method like this, which is using the Win32 ShowWindow function directly, avoiding the VCL framework's internal visibility change system which grabs focus:
procedure TOurForm.ShowWithoutActivate;
begin
ShowWindow(Self.Handle, SW_SHOWNOACTIVATE);
Self.Visible := true;
end;
If I just did this, it would grab focus:
Self.Visible := true; // TWindow.Visible = true, will grab focus, plus make window visible.
This works, but the next thing I'd like to be able to do is set Maximized state so that the
form will maximize itself on the Monitor that it is currently on. How do we get it onto a particular monitor? The same way it always worked, with modification of the Left and Top properties of the Form. We have to take care that if we store Left/Top/Width/Height on form, and then restore it, that the results are still valid when we reload it. That is NOT what I'm asking about.
I'm specifically asking about how to maximize the form now that I have "showed" it using the above custom function. One hack begets another hack. Here is how far down this rabbit hole I've gone:
When a TForm, which is also a TWinControl's private field FShowing is false, setting Form.Maximized has no effect.
When a TForm has its TWinControl.FShowing field set true, setting the windowState to wsMaximized also causes the form to activate.
Is it possible to both make this form visible and make it take the window state I want it to take without activating? If I can't do this, then users are going to lose their keyboard focus when I show this form on a secondary monitor, something that I really want to avoid.
What I tried is to use Win32 ShowWindow API to do SW_SHOWMAXIMIZED:
ShowWindow(Self.Handle, SW_SHOWMAXIMIZED);
The above seems to grab focus (activate).
When you create the top-level window set the extended window style to
WS_EX_NOACTIVATE | WS_EX_APPWINDOW
WS_EX_NOACTIVATE stops the window activating. This also makes it disappear from the taskbar, so you need WS_EX_APPWINDOW to fix that problem.
Call ShowWindow(hWnd, SW_MAXIMIZE) and the window will be maximized but not activated.
You need to be able to activate the window once it is visible, so in the WM_ACTIVATE handler (the irony!) you need to clear the WS_EX_NOACTIVATE flag thus:
case WM_ACTIVATE:
{
DWORD exstyle = GetWindowLong(hWnd, GWL_EXSTYLE);
if (exstyle & WS_EX_NOACTIVATE)
{
SetWindowLong(hWnd, GWL_EXSTYLE, exstyle & ~(DWORD)WS_EX_NOACTIVATE);
}
}
Apologies for the C++. This should be simple to translate into Delphi.
EnumDisplayMonitors API enumerates the monitors and their coordinates on the joint desktop, area into which a particular monitor maps to its specific position.
To find out which monitor is "current" you would want to compare the current window position against monitor coordinates rcMonitor/rcWork. Or, you have MonitorFromPoint and friends to help you.
Once to decided which monitor you want, you can either move your window (MoveWindow, SetWindowPos) to monitor's work area, or use this rectangle in response to WM_GETMINMAXINFO message to send the window to this position as a part of standard maximization.
To add to this, this small C++ application [1, 2, 3] demos the concept mentioned above, shows monitor information, and changes position where the window would be maximized to.

How to force user to click a button in only one active window and not be able to click things in other windows?

I have a small question.
I think there must be a easy way to do it but I just can't find the keyword, so please teach me.
Here is the question:
For example, default ShowMessage pops a window with 'OK' button, then program stops temporarily before you click 'OK'.
This makes sense, but the problem is, you can still interact with other objects before you click 'OK'.
It certainly causes some problems like this:
var
Count: Integer; //initial it to 1
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage(IntToStr(Count));
Inc(Count);
end;
By not closing the message box, program always stops at same place and the variable remains the same.
How do I lock the popping window so you can't interact with other objects?(System forces you to turn your focus back to the popping window if you do so)
Is it possible to achieve this with ShowMessage?
It would be helpful if I can get some tips.
Thanks a lot.
Edit:
Using the unit QDialogs.ShowMessage version causes a non-modal problem. Beware of those units you are not familiar lol.
ShowMessage shows a modal dialog. In other words, it disables its owning window while the dialog shows. And so you cannot interact with the other windows in your app. ShowMessage already does what you want.
If you find that you can interact with other windows in your application whilst the dialog is showing, then you must have got the window ownership wrong.

Delphi TPageControl not responding to clicks on tabs

I have an app with a TPageControl on the main form. The pagecontrol has several tabs. The app can be minimized to a tray icon. Sometimes after running minimized for a while, when I restore the main window (via a right-mouse click on the tray icon), the tab that was last displayed is displayed, but I can't select any other tabs!
If I click on another tab, the appearance changes so that tab then appears to be the active one (i.e the tab itself moves to the front of the row of tabs), but the body of the tab remains as it was. I also have menu items and shortcut keys to select the other tabs and they behave the same. If I type Alt-O (options) the options tab at the top becomes active but I can't see what is on the body of that tab - I still see the other tab's contents.
I have verified that focus moves off the first tab when I click on another tab and moves back when I click on that tab.
I haven't yet established if the behaviour is confined to a particular tab as it takes a while for it to happen.
Any ideas?
Update
Interesting note. I have established that the problem occurs under these circumstances. The app is started, then minimized to the tray. An alert condition is detected, pops up a window and restores the main window (this is intended behaviour of the app). It is at this point the fault is observed - i.e. I cant see the other tabs when I click on them.
Start app. Tab 1 is displayed
Minimize app. to tray
Wait for popup to show, main form is restored
Click on Tab 2 FAULT OBSERVED (Tab 2 body does not display)
Put breakpoint in TWinControl.CreateHandle
Click on Tab 3 - breaks
Run - does not show Tab 3 body
Click on Tab 1 - does not break
Click on Tab 3 - does not break
Click on Tab 4 - breaks
Run - does not show Tab 4 body
Click on Tab 1, 2, 3, 4 - does not break
So it seems the tabs are creating their handles the first time they are clicked on, and from that point on they think they exist, but they don't show. If the popup is disabled the fault is not observed. The popup is triggered from an Application.OnIdle task.
Another update: Some progress. After poking around on the web I made some changes.
I removed the following code:
procedure RestoreMainWindow ;
begin
MainForm.WindowState := wsNormal ;
MainForm.visible := true ;
Application.Restore ;
Application.BringToFront ;
ShowWindow (Application.Handle, SW_SHOW) ; { show the taskbar button }
end ;
and replaced it with:
procedure RestoreMainWindow ;
begin
MainForm.Show () ;
MainForm.WindowState := wsNormal ;
Application.BringToFront () ;
ShowWindow (Application.Handle, SW_SHOW) ; { show the taskbar button }
end ;
I removed:
procedure TTADMainForm.SendToTray (Sender: TObject) ;
begin
MainForm.visible := false ;
ShowWindow (Application.Handle, SW_HIDE) ; { hide the taskbar button }
end ;
...
Application.OnMinimize := SendToTray ;
and replaced it with:
procedure TTADMainForm.ApplicationEvents1Minimize(Sender: TObject) ;
begin
Hide();
WindowState := wsMinimized ;
TrayIcon1.Visible := True;
end ;
and the problem seems to have gone. HOWEVER. Now I can minimize the app after startup, the popup occurs and shows modally, the main form shows, all the tabs display and work. BUT. I can't minimize the form again. The OnMinimize handler doesn't get triggered after the first time. Grrrrr.
I still can't fathom why it works now, which is a little worrying. And how do I get it to minimize again??
Working entirely from 5 years ago memory, but here goes:
TPageControl uses a different window handle for each page within it. The tab bar is its own window handle, and the TPageControl is responsible for listening to tab changes and making the corresponding hide/show of pages. So, when you click on a tab and the tab jumps to the front of the pack, the TPageControl is supposed to hide the current page window and show the page window corresponding to the selected tab.
Normally, VCL controls don't create their window handle until it is actually needed - when it's actually shown, for example. This reduces window handle consumption. Critically important in Windows 3.1 and Win95, but not so critical in today's NT based 32 bit OS's.
To minimize resource load and startup time, TPageControl doesn't create window handles for all its hidden pages when the control is created. The page window handles will be created when they are first shown.
There are a few possibilities for why the page is not being drawn when the tab is clicked:
Exhausting the GDI window handle pool. Extremely unlikely unless you're on a 16 bit Windows OS. (Win 3.1 or Win95)
Memory leak that causes your app to spill into the swap file and thrash the hard disk. The app will grind to a near halt and look like it's frozen, with burps of UI activity every now and then.
Window handles being created on a background thread that has no message loop. Are you doing anything in background threads? Touching a VCL control in a background thread can cause the window handle to be created prematurely, and the window handle will be bound to the thread it was created on. If that thread has no message loop, then that window handle will never receive any messages, so it will never draw itself on screen.
No. 3 is your most likely culprit. So, what are you doing in that background thread? ;>

Resources