8
votes

I want to create a window that will be the main window and that Windows itself recognizes as a main application window. However, when I make my window borderless, and without any non-client area at all, Windows no longer recognizes that this window is an application main window. This has several side effects:

  1. WindowsKey+M minimizes all windows except my application's main window.

  2. Clicking once on the taskbar (in win7) and then again, should toggle the application main window's state/visibility between normal and minimized state. This does not work for such a window.

In bare Win32 programming terms, I'm asking about parameter values for dwStyle as when calling CreateWindow (WS_... constants), or CreateWindowEx (WS_EX_... constants). For delphi users, these values would be set in the CreateParams method, which you would override, and then set Params.Style := WS_xxx; For MFC/C++ users and C users, something in your framework would eventually be calling CreateWindow, with this dwStyle value.

In delphi terms, setting your form.BorderStyle=bsNone, results in dwStyle=WS_POPUP. However I want a borderless window without using dwStyle=WS_POPUP.

Note: All the answers below are good, but using each in production scenarios is problematic, and my attempts to do so, have resulted in encountering many glitches, which for a professional quality application, I still find I can not work around. Davids answer is a great pure Win32 API answer though, and fits the bill. It seems that an industrial strength solution should combine multiple qualities, including all those I have in my question above. In short, borderless forms using BorderStyle=bsNone (dwStyle=WS_POPUP) block all Windows functionality that usually applies to main windows of applications, and all the solutions below solve part of it.

Based on David's suggestions, I wrote the following, which doesn't work: I want a window without a border, that behaves in all ways, like a windows application window, to the system, that is, it can be minimized/restored by clicking on the window in the taskbar, and will be minimized by WindowsKey+M. I am beginning to think that the only way to do this is to add non-client paint code and to resize the top nonclient area bounds to zero. This is of course not a trivial idea.

It turns out that I made a simple mistake in my coding (hence the two paragraphs above) and in fact the code below does now work as I desire. This one is in pascal, but it should be easy to convert it to C++ or anything else.

program NoBorderProject;

uses
  Windows, Messages;
  {the Messages unit contains the windows
  Message constants like WM_COMMAND}

{$R *.RES}

var
  wClass: TWndClass;
  Msg: TMsg;
  win:HWND;
function WindowProc(hWnd,Msg,wParam,lParam:Integer):Integer; stdcall;
begin
 if Msg = WM_DESTROY then PostQuitMessage(0);
 Result := DefWindowProc(hWnd,Msg,wParam,lParam);
end;

begin
 wClass.lpszClassName:= 'CN';
 wClass.lpfnWndProc :=  @WindowProc;
 wClass.hInstance := hInstance;
 wClass.hbrBackground:= 1;
 RegisterClass(wClass);
 win := CreateWindow(wClass.lpszClassName,'Title Bar',
              WS_POPUP,//WS_OVERLAPPEDWINDOW or WS_VISIBLE,
              10,10,340,220,0,0,hInstance,nil);
 SetWindowLong(win, GWL_STYLE, WS_POPUP or WS_MINIMIZEBOX);
 SetWindowLong(win, GWL_EXSTYLE, 0 );
 ShowWindow(win,SW_SHOW);
 while GetMessage(Msg,0,0,0) do
   DispatchMessage(Msg);
end. 
3
I found your bug. You set GWL_STYLE twice. The second call to SetWindowLong should be to set GWL_EXSTYLE.David Heffernan
Here with D7, MainForm.BorderStyle = bsNone doesn't prevent WinKey+M from working. Also minimize/restore toggles just fine. Could this be attributed to the MainFormOnTaskbar property from TApplication that is set by default in newer Delphi versions?NGLN
NGLN- Exactly. Delphi 7 has a fake top level application window and so Windows itself doesn't think your form is your application main window, it uses the ancient Delphi hack of a hidden special main window. This behaviour is also the root of the Z-Order bugs inherent in the Delphi 7 era main form hack.Warren P

3 Answers

13
votes

The following gets the job done:

hWnd = CreateWindow(...);
SetWindowLong(hWnd, GWL_STYLE, WS_POPUP | WS_MINIMIZEBOX);
SetWindowLong(hWnd, GWL_EXSTYLE, 0);
ShowWindow(hWnd, ...);

You were probably missing WS_MINIMIZEBOX.

4
votes

A bit icky, but you can set the window region by putting this in YourForm.OnShow event:

var
  r: TRect;
begin
  r := ClientRect;
  OffsetRect(r, 0, GetSystemMetrics(SM_CYCAPTION));
  OffsetRect(r, GetSystemMetrics(SM_CXFRAME), GetSystemMetrics(SM_CYFRAME));
  SetWindowRgn(Handle, 
      CreateRectRgn(
          r.Left, r.Top, 
          ClientWidth + r.Left, ClientHeight + r.Top), True);
-4
votes

You need to override TForm.CreateParams and set or remove any style that you are interest in

procedure TYourForm.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  Params.Style := Params.Style and
  Params.ExStyle := Params.ExStyle or ;
end;