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? ;>
Related
I have a systray application without any taskbar icon. Nowdays most systray icons are hidden and I want to make an easier access to app.
There are no forms created with Application.FormCreate so Delphi cannot show icon itself.
How to show a normal app icon always on taskbar even when there is no visible form available?
I want to catch the click like this and when its clicked show the GUI with my custom function:
class procedure TTrayMain.HandleMessages(var Msg: TMsg; var Handled: Boolean);
begin
if (Msg.wparam = SC_RESTORE ) then begin
MenuPopup (popup,2);
Exit;
end;
A Taskbar button cannot exist without a window to represent. There are only 3 ways for a Taskbar button to be created:
create a visible window with the WS_EX_APPWINDOW style
create a visible top-level unowned window
use ITaskbarList::AddTab()
All of them require a window. But, that doesn't mean the user has to see the window. You can create a visible window with width/height set to 0 (some frameworks do this so no single app window owns the Taskbar button, but the button can show/hide the entire app as a whole), or move it offscreen. And then the window can respond to state changes as need, such as via the Taskbar button.
Otherwise, since your tray app likely has a hidden window to receive icon notifications, you can try using that window with ITaskbarList. I just don't know if it will actually do anything meaningful when the user clicks on the button. So, consider changing your tray app to use a visible but unseen window for notifications, then it can have a Taskbar button without involving ITaskbarList.
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.
I have recently come to need some popup menus to be linked with multiple TTreeViewItem controls. The first time, I right click any of the TTreeViewItems, the delay in the showing of the popup is enough to make me believe that the click didn't register somehow and then it appears. All the subsequent times I click, the reaction is instantaneous.
I have to make my applications so that they are compatible and smooth even on Pentium 4s and the subsequent buit-in horsepower of their generation.
To reproduce:
Place a TTreeView on an empty form.
Add 5 or 6 TTreeViewItems.
Add a TPopupMenu and add a single TMenuItem to it.
Link the TPopupMenu with 2-3 of the TTreeViewItems.
Optionally add another TPopMenu with a single TMenuItem and link with the rest.
Run the application and right-click on any of the TTreeViewItems.
Is there a method I can call to bring the popup menu into the cache immediately after form creation and also not make it visible in doing so?
I use TAcroPDF activeX control to print pdf documents. Application prints page, but if it needs to print 2 pages, it prints the first page, then shows message dialog, which asks user to turn the paper and then it prints the second page. The concept is pretty straightforward, but for unknown cause it fails to run normally. It really prints the first page, but then blips ShowMessage window (hides instantly) and the application goes seemingly on. I can then recover lost Showmessage window hidden behind the main application window, but (what is strange) I can easily change focus between showmessage window and main application. It looks like the program runs in other thread, because of ActiveX??? When I click on "OK" button, the code after ShowMessage('Please, turn the page'); is executed. Can anybody tell me whats is going on? Here is example:
procedure TForm1.btn1Click(Sender: TObject);
begin
acrpdf1.src := Getcurrentdir + '\temp.pdf';
acrpdf1.PrintAll;
ShowMessage('Please, turn the page'); //it does not hover over main window, it hides behind
Form1.Color:=clRed; //it is executed after closing Showmessage window
end;
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.