Is it OK to hide Delphi's "Application" Window? - delphi

We just moved our application from a MDI container to a single document interface. Our users are used to using a "Windows" menu in the MDI parent to show windows side by side. We want to train them to right click on the Windows taskbar and use the window management functions there.
With Delphi applications we noticed that the windows shell leaves room for the hidden "Application" window. So if I only have two windows open it will arrange room for three. The Application window is not really shown but there is space left for it.
This is made worse by the fact that we have two different applications. If they only have one window open in each application and want to show them side by side windows will actually try to account for 4 windows.
So instead of seeing two windows each taking 1/2 of the screen I see two windows that take up 1/4 of the desktop and the rest of the screen is open.
I found that adding a line to hide the application window as my application starts up will fix this problem.
ShowWindow(Application.Handle, SW_HIDE);
Edit in case someone does not read
down to the answer. Based on Craig's
answer below I am setting the windows
style to WS_EX_TOOLWINDOW instead of
hiding the window.
SetWindowLong(Application.Handle, GWL_EXSTYLE,
GetWindowLong(Application.Handle, GWL_EXSTYLE) or WS_EX_TOOLWINDOW);
My (original) questions is: Is this safe (hiding the application window)? I'm wondering if I may be breaking something else by hiding the Application window. Are there any side effect I need to be aware of? Is there a better way to solve this issue?
I'm using Delphi 2007. The issues seem to be consistent across Windows XP, Vista, and 7.
Update: Some of the answers seem to think the problem is with the Application forms icon being visible. That is not the case. I already have MainFormOnTaskbar set to true.
Also if you are testing this be aware that the Delphi IDE (only tested with 2007) makes things worse. Try this. Open the Delphi IDE and two instances of notepad. Minimize the IDE but have both notepads un-minimized. Right click on choose Show Windows Side by Side. You will see each notepad take up 1/3 of the screen. Close the IDE and choose Show Windows Side by Side again and each will take up 1/2 half of the screen.

In Delphi 2007 (and above) the Application Window does not show on TaskBar at all if
Application.MainFormOnTaskbar := True;
line is in a project file (*.dpr). For example
begin
Application.Initialize;
Application.MainFormOnTaskbar := True; // <--
Application.CreateForm(TForm7, Form7);
Application.CreateForm(TForm8, Form8);
Application.Run;
end.
That is default setting for new applications, but this line is absent if you ported an application from previous Delphi version - you should add this line manually.

As long as MainFormOnTaskBar is true, you can fix the problem by adding this to your DPR:
SetWindowLong(Application.Handle, GWL_EXSTYLE,
GetWindowLong(Application.Handle, GWL_EXSTYLE) or WS_EX_TOOLWINDOW);
Later versions of Delphi automatically include the WS_EX_TOOLWINDOW flag when they create the TApplication handle.

I'm still using Delphi 7, and I've been using this technique in combination with the 'WndParent:=GetDesktopWindow' trick to get a taskbar button for each window, somewhat like Microsoft Office started doing since some version (I guess XP?)

I believe at this is the way that applications minimize to the system tray.

Related

icon on the taskbar does not move to second monitor

I recently got a new development computer with again 2 monitors, but unfortunate it has windows 10.
Now I also have some tools still developed in Delphi 7 and they work but with one annoying problem.
When I start an Delphi 7 application the icon on the taskbar does not move to the second monitor.
The taskbar is setup to show icons on the taskbar of the monitor they are open. This works for all applications, except for the Delphi 7 applications.
The icon always stays on the primary monitor.
I have the source available, so I wonder is there something I have to put into the source code, or setup some property, to get this working ?
Googling this bring up lots of cases where the form does not moves to the second monitor, but I did not find anything about the icon on the taskbar.
This is because the window handle associated with the taskbar button is the window handle owned by the Application object rather than the window handle of your main form.
In later versions of Delphi you would write Application.MainFormOnTaskBar := True in your .dpr file and that would change behaviour so that the taskbar button was associated with your main form instead. I believe that MainFormOnTaskBar was introduced in Delphi 2007.
Migrating to a modern version of Delphi is the ideal way to solve the problem. If you cannot do that then you'll need to hack your way around the VCL code to ensure that the main form's window handle is the one associated with the taskbar button. That's not likely to be an easy job. Fundamentally, you are paying the price for continuing to develop with tools that are long out of date.
As Remy points out there isn't much hacking required to do the bare minimum. Change the window style of the Application window to remove the WS_EX_APPWINDOW style, and have the main form override the CreateParams method to set its owner window to NULL, i.e. Params.WndParent := 0.
I suspect that the behaviour will not be quite as smooth as you'd get with a modern VCL app. For instance, the VCL has been modified to reduce the amount of window recreation it does which is more important now that the main window is associated with the taskbar button.

Delphi buttons show white border on Aero glass

I have been trying to find a good-looking design using Aero in Delphi 2010. One of the obvious uses one sees, is where the glass frame is extended to include the OK/Cancel buttons at the bottom of the screen. I notice though that this doesn't look quite right in Delphi 2010 - there is a white border all around each button.
This image shows the problem: the top 3 buttons are from my app, the bottom two were taken from Paint.NET's Layer Properties dialog.
I tried various combinations of DoubleBuffered and a few combinations of placing the controls on other controls first, but the problem remains. Any ideas?
If no one has a clean solution, as a workaround use TBitBtn with DoubleBuffered = false.
It appears that the only workaround is owner-draw, or a third-party button control Check out the Glass Button by Roy Klever or, as stated in the QC entry linked below, TBitBtn with DoubleBuffered=false, which was the accepted answer above to this question.
This is a bug in Windows Aero DWM or else a bug in the windows common controls, or a bug in the way the VCL class hierachy handles common control window messages and painting when painting on glass. In short, windows common controls do not paint themselves properly on glass, or rather DWM composition (Aero) is broken. Surprise surprise.
The standard VCL button component uses the Window Class BUTTON from Windows Common Controls.
Note that TSpeedButton does not use the windows common control, and does not have this problem. however, it also does not accept focus.
It appears Embarcadero knows about this issue, it is QC # 75246, which is closed because it is really a bug in the common controls library, as Won't Fix, with the suggestion to use TBitBtn. Buttons are not alone, this is part of a group of QC reports including panels, and other common controls.
However I have a commercial TcxButton (part of developer express components) which accepts keyboard focus, and does not draw this glitch. Any code that uses the Win32 common control button control appears to have this problem. It may be possible that a low level Win32 API hacker might find a workaround for this. I am looking into it. This answer will be updated if I figure it out.
One interesting detail: TcxButton has three drawing styles, cxButton.LookAndFeel.Kind = {lfOffice11,lfFlat,lfStandard}. Selecting lfOffice11 adds this glitch back in. It looks like a strange interaction between the glass feature in aero in Vista/Win7 and the common control/xptheme button drawing code.
It may be that the only workaround is to use a completely app-drawn button control and to not use Windows common controls buttons, or any button control that relies upon the XP theme engine to draw buttons, on an aero glass pane.
Edit: July 28, someone at Embarcadero has closed the above QC Entry, which was a mistake. I am urging them to reopen it, if only to clarify if this is indeed a Windows bug in the common controls dll.
If you wish to play around, make a copy of the VCL source code for the TButton and TCustomButton classes from StdCtrls, as I have done here, modify CNCtlColorBtn, so that you force one of three things to happen - PerformEraseBackground, DrawParentBackground or inherited, and see the results. Interesting stuff.
procedure TCustomGlassButton.CNCtlColorBtn(var Message: TWMCtlColorBtn);
begin
PerformEraseBackground(Self, Message.ChildDC);
Message.Result := GetStockObject(NULL_BRUSH);
(*
with ThemeServices do
if ThemesEnabled then
begin
if (Parent <> nil) and Parent.DoubleBuffered then
PerformEraseBackground(Self, Message.ChildDC)
else
DrawParentBackground(Handle, Message.ChildDC, nil, False);
{ Return an empty brush to prevent Windows from overpainting we just have created. }
Message.Result := GetStockObject(NULL_BRUSH);
end
else
inherited;
*)
end;
Some interesting reading on Vista era glass/DWM/aero APIs (C++ developers blog)
Here I'm providing some code that makes TButton look right on Glass. Unfortunately it makes the form "click-throw", so I don't think it's a good idea. But maybe you can find a way to fix the form's "click-throw".
if you are able to use win32 api, try exploiting NM_CUSTOMDRAW notification (not ownerdraw), as i do (yes, buttons DO send it, including radio and checkboxes. For these it is best to use WM_CTLCOLORSTATIC though.). This is how it is done in C++, but the idea is the same. While my idea is good, it so happens that my buttons do DISAPPEAR once per program execution from the window, when they are customdrawn and i need to hover mouse over them so they are visible again. That's why i'm still looking for comments for this. Note that it is really difficult to reproduce disappearing buttons in one-form applications. I hovewer am experiencing this behaviour in every project.
case WM_NOTIFY:
switch(((LPNMHDR)lParam)->code){
case NM_CUSTOMDRAW:
{
NMHDR *nmh=(NMHDR*)lParam;
//these 6000 through 6004 are button identifiers assigned by me
if(nmh->idFrom >= 6000 && nmh->idFrom <= 6004){
switch(((LPNMCUSTOMDRAW)nmh)->dwDrawStage){
case CDDS_PREERASE:
//BackgroundBrush is a HBRUSH used also as window background
FillRect(((LPNMCUSTOMDRAW)nmh)->hdc, &((LPNMCUSTOMDRAW)nmh)->rc, BackgroundBrush);
break;
}
}
break;
}
break;

TIWMenu and TIWRegion hides Submenus

When I put a TIWMenu and a TIWRegion on a TIWAppForm with
IWMenu.align := alTop;
IWRegion.align := alClient;
the submenu of IWMenu is hidden behind the region. That means, when I click on File, a submenu should come up with Open, Save, Save As ..., and it does, but I only see the upper border as the rest shows up behind the IWRegion.
I tried to change the z-order, but it does not help. Neither does setting the IWRegion.ClipRegion := False;
Does anybody have a clue what I do wrong?
I am using Delphi BDS4/2006 with IntraWeb 9.0.42, Windows Vista x64, and Firefox 3.5, IE8, and IE Tab in Firefox.
I drop the two components on the form, connect the main menu to IWMenu, and there the problem is.
Could you be more specific about your setup (e.g. Windows version) and steps to reproduce? Sounds like something wrong in how you've set this up.
I can't reproduce this problem, either in BDS2006 (IW ver 8) nor in RAD2009 (IW ver 10).
I started with a standalone IW application, dropped in a TIWMenu (Align=alTop), TIWRegion (Align=alClient), and TMainMenu (with a few menu and submenu items), and attached the TIWMenu to the TMainMenu. Running this under Win XP sp3, I see the menu and submenu items just fine (over the IWRegion).
The problem was with the ServerController that used a style sheet. This prevented the z-index from being set by the application and therefore the z-order was wrong for the TIWMenu component.
Removing the style sheet resolved the problem. The style sheet had some IDs conflicting with IW.

How to make a window to stay under all the other windows ('bottom-most')

Exact Duplicate: How to make 'always on bottom' window?
Related: Window on desktop?
I'm developing a small application in Delphi that need to behave like a shell (replacement) launching pad (for Windows Embedded). I have some icons/buttons on it that will launch other applications. The point is that applications need to stay all the time in front of the "shell". Additionally the applications are started using simple-click, but if double click (accidentally) is used the application will go behind (the "shell" will be focused)
Since this application will replace the actual shell (Explorer) will have to behave similarly to Explorer ... so it has to stay in "background" all the time and should not appear in ALT+TAB list.
I tested a lot of combinations of SetWindowPos with HWND_BOTTOM, SWP_NOACTIVATE etc. without success..
Additionally I found some info regarding this but it doesn't work as advertised:
How to keep a form always in the background (bottommost)
Any hints how to achieve all these ?
Update: For hiding the window from ALT-TAB list/switcher (and from Taskbar, but since I'm interested to create shell replacement that will be no Taskbar) I found the following articles:
Hide a Delphi Application Button from the TaskBar
Hide a Delphi 2007 Application Button from the TaskBar (with MainFormOnTaskBar)
Hasn't that been asked before?
It's unclear if you are trying to make your application behave as a 'launchpad' on top of the current shell, or if you are trying to make your application be the shell.
There is a major difference between those two.
It sound like the last option is what you really want, and then taskbars etc shouldn't be any trouble at all - they won't be there, since they are part of the old shell (explorer.exe) that you have replaced.
SharpEnvironment, an open source shell-replacement made with Delphi, may give you some hints on the way.
I'm not sure what the exact effect is that you're trying to achieve, but I think you basically want to change the workarea on your screen.
You can obtain the current workarea as a TRect with Monitor.WorkareaRect.
You can set it via SystemparametersInfo()
The example below turns your form into something that more or less resembles the way the Windows taskbar works:
no border
always on top
stuck at the bottom of the screen
other windows applications that maximize, will only maximize until the top of our form.
Let me know if this is what you mean.
procedure TfrmMain.FormCreate(Sender: TObject);
var
Rect :TRect;
begin
Height := 25;
BorderStyle := bsNone;
FormStyle := fsStayOnTop;
Rect := Monitor.WorkareaRect;
Rect.Bottom := Rect.Bottom - Height;
Left := Rect.Left;
Width := Rect.Right;
Top := Rect.Bottom;
SystemparametersInfo(SPI_SETWORKAREA,0, #Rect,SPIF_SENDCHANGE);
end;
Some things to keep in mind:
When your program finishes, the workarea on your desktop will still be the way you've set it, so remember to restore it before terminating the application. I'm sure you'll figure out how to do that.
When you resize the windows taskbar, running applications are notified of the change, and maximized applications adjust their size to the new workarea. The above code doesn't seem to trigger that, so you might need to find a way to do that.
#dummzeuch
The solution you mentioned does not include any code just some suggestions ... additionally does not handle the ALT-TAB issue that I need to solve too (My window should not apear into the ALT-TAB list, similar to how Explorer - as a shell - behave).
#Wouter
I need to create a shell replacement (borderless full screen form) that need to stay on the "bottom" all the time similar to regular Explorer shell (that will replace). So it should stay on the bottom not on the top as you suggested.
This "shell" replacement is used for a Windows Embeded solution where I can not use the regular Explorer shell. This shell form include some "soft buttons" (TImage to emulate icons) for some selected executable files that can be started from it. The problem is
that is that I need to make sure that the shell stay behind all the time, otherwise it can hide some other applications (ex: multiple fast click on an icon that will start the application but will refocus on shell, or when using ALT TAB).
In this context, I have another question: there is any way to wait for any application to start ?
I will really appreciate some code hints to get started.
It's a Win32 FAQ for decades.
See on professional Win32 api group
Is this what you want?
procedure SetWindowPosToBack(handle: HWND);
begin
SetWindowPos(Handle,HWND_BOTTOM,0,0,0,0,SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOSIZE);
end;

Menu Accelerator Keys Not Showing Up (Delphi 2009)

I've tried my best and cannot figure out what happened here. It worked fine in Delphi 4. After upgrading to Delphi 2009, I don't know if this is the way it is supposed to work, or if it's a problem:
This is what my program's menu looks like in Design Mode under Delphi 2009:
Notice that every word in the Main Menu and the File submenu have one letter underlined. It is supposed to be like this. This underlined letter is called the Accelerator Key and is standard in Windows applications so that you can use the Alt-key and that letter to quickly select the menu item and then submenu item with the keyboard rather than with your mouse.
You get them this way by using the "&" character as part of the caption of the item, for example: Save &As...
When I run my application, and use the mouse to open the File menu, it looks like this:
The characters are underlined in the main menu, but are not underlined in the File menu.
If instead, I use the Alt-F key to open up the File submenu, then it looks correct like this:
and all the Accelerator Key letters are properly underlined.
I've played with the AutoHotKeys option but that's not the problem.
Has someone encountered this problem before? Is the example in the 2nd image correct behavior that I don't know of? Or is there some option or coding mistake that I might have missed?
Nov 2009 (one year later): mghie seems to have got to the root of this and figured out the problem. See his accepted answer below.
There is a standard Windows setting (under display properties) to normally hide those accelerators unless the Alt key is held down. That would explain why opening the menu with Alt+F10 shows them for you. Maybe that's the cause?
[EDIT]: No, it's not. I just tried, and a simple TForm with a menu item shows the accelerator, but as soon as I add a TImageList and set the ImageIndex of the single menu item, or simply set OwnerDraw to true, then the accelerator underline disappears. I guess that really is a bug in the VCL.
BTW, this is on Windows XP.
Workaround:
I have debugged this using Delphi 2009 on Windows XP 64, and the root cause for the missing accelerators seems to be that Windows sends WM_DRAWITEM messages with the ODS_NOACCEL flag set, which it shouldn't if the system is set to show accelerators at all times. So you could say that it is not a VCL bug, but a Windows problem which the VCL does not work around.
However, you can work around it in your own code, you just need to reset the flag before passing the message to the VCL. Override the window proc
protected
procedure WndProc(var Message: TMessage); override;
like so:
procedure TYourForm.WndProc(var Message: TMessage);
const
ODS_NOACCEL = $100;
var
pDIS: PDrawItemStruct;
ShowAccel: BOOL;
begin
if (Message.Msg = WM_DRAWITEM) then begin
pDIS := PDrawItemStruct(Message.LParam);
if (pDIS^.CtlType = ODT_MENU)
and SystemParametersInfo(SPI_GETKEYBOARDCUES, 0, #ShowAccel, 0)
then begin
if ShowAccel then
pDIS^.itemState := pDIS^.itemState and not ODS_NOACCEL;
end;
end;
inherited;
end;
This is demonstration code only, you should not call SystemParametersInfo() every time a WM_DRAWITEM message is received, but once at program start, and then every time your program receives a WM_SETTINGCHANGE message.
It is a "feature" introduced with Windows 2000:
The Old New Thing: Why does Windows hide keyboard accelerators and focus rectangles by default?
It would appear that Delphi 4 didn't support this Windows feature.
To have 2000 and XP menus show accelerator keys, right-click an empty spot on the desktop, choose Properties, click the Appearance tab, and under Effects, uncheck Hide Underlined Letters for Keyboard Navigation until I Press the Alt Key. Click OK twice.
Not sure how to do it in Vista.
I don't think it is a Delphi generated bug as you have the same behavior with Notepad on Vista. Also in Delphi itself BTW...
I must confess that I did not pay attention before your question. Thanks for pointing it out.
As Jim McKeeth noted above (correctly), this is "by design" behavior. If the menus are triggered through keyboard action the accelerators should be shown, but if triggered by the mouse the accelerators are intentionally not shown.
I have my XP configured to show accelerators at all times, but a quick test with that option changed confirms that the menus should not show underlines either (Visual Studio responded as I expected, no underlines when using the mouse). However, Microsoft Office ignores this setting and always shows the underlines. So it looks like a bug in how the menus are drawn in Delphi (I don't have any experience with Delphi myself).
I found the option for Vista as well: http://www.vistax64.com/vista-general/42125-always-show-menu-underline-keyboard-accelerators.html
You can turn this on in the new Ease of Access Center (go to Control
Panel, click Ease of Access and then click Ease of Access Center). In
the Ease of Access Center, click Make the keyboard easier to use, and
at the very bottom select the Underline keyboard shortcuts and access
keys check box.
While doing further research I found this related bug on Delphi forums: http://qc.codegear.com/wc/qcmain.aspx?d=37403
It looks like in your case the child windows (the drawn menus) aren't getting or aren't handling WM_UIUPDATESTATE message from their parent window, which is what causes the redraw with accelerators.

Resources