We're having an issue with layered windows and system menus in Delphi 2009. That is, our layered windows (which have no border) have no system menu. When I say system menu, I am referring to the menu you get when clicking an application's icon, right clicking it's title-bar or (in Windows 7, with the addition of the shift key,) right clicking an application in the task-bar:
When you attempt to access the system menu, e.g. by right-clicking on the task-bar icon, of such a layered window, instead the layered window is risen. Why is this? Is there some sort of style to set, or some sort of event to handle?
Here's a hastily made demo showing the issue. It can really be reproduced with any form with a bsNone borderstyle, though.
http://ompldr.org/vODd5dw
You need to add back the WS_SYSMENU style which is removed with bsNone border style.
type
TLayeredForm = class(TForm)
procedure FormCreate(Sender: TObject);
protected
procedure CreateParams(var Params: TCreateParams); override;
end;
...
procedure TLayeredForm.CreateParams(var Params: TCreateParams);
begin
inherited;
Params.Style := Params.Style or WS_SYSMENU;
end;
Related
I upgraded to XE4 recently and am now in the progress of finding the changes and so-called fixes from XE3.
One that surprised me a lot is that the context menu for the button on the taskbar doesn't appear any more.
It is very easy to duplicate: Just create a new Firemonkey project in XE4 and run it on Windows. Right click on the taskbar application button and see if the context menu comes up. I mean the menu with "Close", "Restore", "Minimize", etc.
This is only on Windows XP and Server 2003. On Win7 it works and shows the "Close" menu item.
Also the title of the button is now different. It should be "Form 1" as the caption of the main form, but instead it is Project1 as the executable name.
This is on all Windows versions.
Can someone help me with this one? People still use XP and this behavior is quite unexpected for the user.
Thanks
Some time has passed since I asked the question, but I found a solution in the meantime and will post it in case it is useful for someone else.
The problem with the wrong caption and missing menu comes from the fact that Delphi XE4 changed the window structure of the Firemonkey apps. Before that the window of the main form was placed on the taskbar and had the appropriate caption and context menu. In XE4 the app creates a new window with a class name "TFMAppClass" and uses it as the main application window that is placed in the taskbar. The main form window is a sibling of that one.
This leads to not being able to set the taskbar button caption, not having a context menu, not responding properly to clicks on the button and not being to hide the button when the main form is hidden.
So, what was needed was to hide the app window from the taskbar and show the form window instead. To do it once is not enough though, because the app window's style is reset on each minimize/restore and it reappears on the taskbar.
To hide the app window simply call ShowWindow(AppWindowHandle, SW_HIDE).
To show the main form window on the taskbar we have to set the WS_EX_APPWINDOW extended window style with SetWindowLong() and call ShowWindow each time the app is shown, restored, etc and bring it to the foreground.
This is done by placing a hook to intercept the WM_CREATE, WM_SHOWWINDOW and WM_ACTIVATE messages and apply the styles when those messages are called. To make the usage easier all of the code is placed in a single unit and the hook is set in the initialization part.
There are no functions to call. To use the unit simply place it somewhere in the uses clause.
unit FM3TaskbarFix;
interface
implementation
{$IFDEF MSWINDOWS}
uses
Winapi.Messages, Winapi.Windows, System.Sysutils, Fmx.Forms, Fmx.Platform.Win;
var
GHookHandle: HHOOK; // Handle for the hook we set
GAppWnd : HWND = 0; // Handle of the main application window
function CallWndProc(nCode: Integer; iWParam: WPARAM; iLParam: LPARAM): LRESULT; stdcall;
var
ActiveThreadID, WindowThreadID: DWORD;
ProcMsg: TCWPStruct;
begin
Result := CallNextHookEx(GHookHandle, nCode, iWParam, iLParam);
if (nCode < 0) then
Exit;
ProcMsg := PCWPStruct(iLParam)^;
case ProcMsg.message of
WM_CREATE:
// Save the "main" app window handle for later usage. There is only one window with the TFMAppClass class per app
if (GAppWnd = 0) and (PCREATESTRUCT(ProcMsg.lParam)^.lpszClass = 'TFMAppClass') then
GAppWnd := ProcMsg.hwnd;
WM_ACTIVATE, WM_SHOWWINDOW:
begin
// Hide the app window. This has to be called on each minimize, restore, etc.
if IsWindowVisible(GAppWnd) then
ShowWindow(GAppWnd, SW_HIDE);
// Only handle Show/Activate. wParam of 1 means the app is shown or activated, NOT hidden or deactivated
// Also apply the style settings only to the Application.MainForm
// We don't want to show other forms on the taskbar
if (ProcMsg.wParam = 1) and
(GetWindow(ProcMsg.hwnd, GW_OWNER) = GAppWnd) and Assigned(Application.MainForm) and
(WindowHandleToPlatform(Application.MainForm.Handle).Wnd = ProcMsg.hwnd) then
begin
// Show the main form on the taskbar
SetWindowLong(ProcMsg.hwnd, GWL_EXSTYLE, GetWindowLong(ProcMsg.hwnd, GWL_EXSTYLE) or WS_EX_APPWINDOW);
ShowWindow(ProcMsg.hwnd, SW_SHOW);
ActiveThreadID := GetWindowThreadProcessId(GetForegroundWindow, nil);
WindowThreadID := GetWindowThreadProcessId(ProcMsg.hwnd, nil);
AttachThreadInput(WindowThreadID, ActiveThreadID, True);
try
SetForegroundWindow(ProcMsg.hwnd);
SetActiveWindow(ProcMsg.hwnd);
finally
AttachThreadInput(WindowThreadID, ActiveThreadID, False);
end;
end;
end; { WM_ACTIVATE, WM_SHOWWINDOW }
end; { case }
end;
initialization
GHookHandle := SetWindowsHookEx(WH_CALLWNDPROC, CallWndProc, 0, GetCurrentThreadID);
finalization
UnhookWIndowsHookEx(GHookHandle);
{$ENDIF}
end.
Btw, this code is based on two samples from the net. I don't know who the authors are, but I give them credit for the idea.
There is one issue that remains. When the app is first minimized the button of the app window reappears temporarily, instead of the form button. After the app is restored or minimized again this doesn't happen anymore.
When I create a new VCL application in Delphi 2006 and run it (without adding any of my own code or refernce any of my own units), the application won't have all of menu items one would expect in the context menu of it's taskbar button. The application's system menu (the menu you get when left-clicking the form's icon), however, has all the regular menu items. As you can see in the following screenshots, Move (Verschieben), Size(Größe ändern) and Maximize(Maximieren) are missing from the former
I could not reproduce this in Delphi XE (the only other vesion of Delphi I have access to) and I haven't found anybody else reporting this behavior, either.
I have also looked through the properties of TForm and TApplication whether there was one to control these menus, but haven't found one.
All applications I know of have the same set of menu items in those two menus and I would like my application to do, too. How do I get these two menus to show the same set of items?
The difference lies in Application.MainFormOnTaskBar, a property introduced in D2007 which is set automatically True.
To acquire the same effect in earlier versions, I always use the following approach:
Project.dpr:
uses
Windows,
...
Application.CreateForm(TMainForm, MainForm);
ShowWindow(Application.Handle, SW_HIDE);
Application.Run;
FMain.pas:
TMainForm = class(TForm)
private
procedure WMSysCommand(var Message: TWMSysCommand);
message WM_SYSCOMMAND;
protected
procedure CreateParams(var Params: TCreateParams); override;
...
procedure TMainForm.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
with Params do
begin
ExStyle := ExStyle or WS_EX_APPWINDOW;
WndParent := GetDesktopWindow;
end;
end;
procedure TMainForm.WMSysCommand(var Message: TWMSysCommand);
begin
if Message.CmdType = SC_MINIMIZE then
ShowWindow(Handle, SW_MINIMIZE)
else
inherited;
end;
This works only when MainForm.Visible is set True design time.
In D2006, the taskbar button is owned by the TApplication window. Clicking on the Taskbar button displays the TApplication system menu, which is altered by the VCL to always remove the Maximize, Size, and Move menu items. When clicking on a TForm, on the other hand, the Form's system menu is displayed instead, which is altered by the VCL according to the Form's BorderStyle and BorderIcon properties. So you are really dealing with two separate menus for two separate windows.
In modern Delphi versions, with the new TApplication.ShowMainFormOnTaskbar property set to true, the taskbar is owned by TForm instead of TApplication, so clicking on the Taskbar button will display the Form's system menu instead of the the TApplication system menu. So in this case, you are really dealing with a single menu for a single window.
One of the big selling points I saw when I had to buy delphi for my job was the ability to support tablet pc's. Now the client of the company where I work want to use a tablet pc. I've been trying hard to find examples of delphi with tablet pc but I don't find any. Does anybody has experience with it? Any kind of tutorials or examples?
I don't seem to be able even to bring a virtual keyboard when a component gain focus and hide it when it loses it.
Delphi 2010 introduced some nice touch and gesture support to Delphi.
To get more info about it, go to EDN website and look for CodeRage 4 replays. There is a session titled "Hands on gestures in VCL" by Seppy Bloom. Also in CodeRage 5 there is a session titled "Gesturing Capabilities for New Application and Current Projects" by Vesvolod Leonov.
Chapter 6 of Marco Cantu's "Delphi 2010 Handbook" also covers touch and gesture in Delphi.
Eventually, you can check Chris Bensen's weblog for some introductory posts and demo source code about touch and gesture support in Delphi.
I don't seem to be able even to bring
a virtual keyboard when a component
gain focus and hide it when it loses
it.
In Delphi 2010 and newer versions a touch-enabled keyboard component is already available. To make it is visible or hide it when focus is changed, you can handle CM_FOCUSCHANGED VCL message, and make the keyboard visible when the control gaining focus is derived from a certain class or meets some special conditions. Here is a sample code:
type
TForm1 = class(TForm)
Edit1: TEdit;
Button1: TButton;
Memo1: TMemo;
TouchKeyboard1: TTouchKeyboard;
private
procedure ActivateVirtualKeyboard(Control: TWinControl; Keyboard: TTouchKeyboard);
procedure CmFocusChanged(var Msg: TCMFocusChanged); message CM_FOCUSCHANGED;
public
{ Public declarations }
end;
/// Implementation
procedure TForm1.ActivateVirtualKeyboard(Control: TWinControl; Keyboard: TTouchKeyboard);
var
APoint : TPoint;
begin
if Control is TCustomEdit then
begin
APoint := Control.ClientToScreen(Point(0,0));
APoint := Keyboard.Parent.ScreenToClient(APoint);
Keyboard.Left := APoint.X;
Keyboard.Top := APoint.Y + (Control.Height);
Keyboard.Visible := True;
end
else
Keyboard.Visible := False;
end;
procedure TForm1.CmFocusChanged(var Msg: TCMFocusChanged);
begin
ActivateVirtualKeyboard(Msg.Sender, TouchKeyboard1);
end;
The code above calls ActivateVirtualKeyboard each time focus is changed. Msg.Sender is the control which gained focus. ActivateVirtualKeyboard checks if the control is a TCustomEdit descendant (components like TEdit or TMemo descend from this class). If the control is derived from TCustomEdit, then it places virtual keyboard right beneath the control, and makes the keyboard visible; otherwise, it hides the keyboard.
In the sample code we have an edit, a memo, and a button on Form1. The keyboard should be visible for Edit1 and Memo1, and hid when Button1 has focus.
The calculation for keyboard position on the screen isn't that clever, and keyboard might go too down if the control having focus is very close to the bottom edge of the form. Anyway, positioning a control on the screen is out of the scope of your question.
In Delphi 2009 I found that the flicker of a PageControl - which occurs during resizing of the form - can be reduced by setting its DoubleBuffered property to true.
However if I add controls to the PageControl tabsheets, they will flicker regardless of their DoubleBuffered property setting. I have also tried with and without runtime themes enabled.
Setting ParentBackground to False for components on the PageControl helped a lot. However this results in a different color of these panel components, they all have a darker background now. Maybe this can be fixed easily (without losing Theme support).
I also installed VCL Fix Pack which has a fix for QC 56252 (TPageControl flickers a lot with active theming).
This is far from perfect, but you might want to use this:
protected
procedure WMExitSize(var Message: TMessage); message WM_EXITSIZEMOVE;
procedure WMEnterSize(var Message: TMessage); message WM_ENTERSIZEMOVE;
procedure TFormMain.WMEnterSize(var Message: TMessage);
begin
if Assigned(PageControlView.ActivePage) then
PageControlView.Align := alNone;
end;
procedure TFormMain.WMExitSize(var Message: TMessage);
begin
if Assigned(PageControlView.ActivePage) then
PageControlView.Align := alClient;
end;
It's the best I found this far, and will reduce the windows update of your page control. It might be less pretty, though, but that's a matter of opinions...
Regarding Notification Area recommendations by Microsoft, I'm looking for ideas or a Delphi component to implement Notification Area Flyouts.
The first "natural" idea is to use a standard Delphi form, but I'm facing two issues with it:
I can't get the form border behavior using the standard "BorderStyle" property. Tried to "mimic" the border using the GlassFrame property along with BorderStyle set to bsNone, but there's no GlassFrame when there's no border (at least, in Delphi 2007).
I can't figure out how to make the form close when the user clicks everywhere out of the form itself. Yesterday I was trying with different messages, but no one works as expected.
I will thank any clue or component to make it happen :)
Best regards.
jachguate.
ps. There's a related question in converting notification area icon to Program icon in Win7 (Delphi).
update[0]
I'm still looking for advise. #skamradt answer looks very good, but unfortunately doesn't work well in practice.
update[1]
Finally, The auto-close behavior is working with the WM_ACTIVATE message after a calling SetForegroundWindog to force flyout "activation"
begin
FlyoutForm.Show;
SetForegroundWindow(FlyoutForm.Handle);
end;
Now, I'm looking for advise to reach the border behavior and visual style, because the closest behavior is achieved with style as WS_POPUP or WS_DLGFRAME, while the closest visual goal is achieved setting style as WS_POPUP or WS_THICKFRAME.
I believe what your after is the following:
TForm1 = class(TForm)
:
protected
procedure CreateParams(var Params: TCreateParams); override;
procedure WMActivate(Var msg:tMessage); message WM_ACTIVATE;
end;
procedure TForm1.CreateParams(var Params: TCreateParams);
begin
inherited;
Params.Style := WS_POPUP or WS_THICKFRAME;
end;
procedure TForm4.WMActivate(var msg: tMessage);
begin
if Msg.WParam = WA_INACTIVE then
Hide; // or close
end;
This will give you a sizeable popup window with a glass frame. You can't move the window without additional programming, since the standard windows caption is missing. When another window gets focus, the FormDeactivate event gets fired...but only if you switch to another form in the same application. To handle it regardless of the application switched, use the message capture method.