Can you make a Borderless Application Main Window in Windows, without WS_POPUP style?
Asked Answered
M

3

8

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. 
Munificent answered 4/2, 2011 at 19:11 Comment(3)
I found your bug. You set GWL_STYLE twice. The second call to SetWindowLong should be to set GWL_EXSTYLE.Starter
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?Krimmer
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.Munificent
S
13

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.

Starter answered 4/2, 2011 at 19:20 Comment(17)
This doesn't work. My guess is that you cannot avoid the WS_POPUP style if you wish not to have a border. But maybe you can make the window behave normally even though the WS_POPUP bit is there?Litchfield
@David: Yes, that works too. I suggest you replace your non-working answer above with that perfectly well-working comment!Litchfield
The best way to explore this is to start with a blank Win32 project in Visual Studio and experiment with the window styles in between the calls to CreateWindow and ShowWindow. Once you bring the VCL into play then it gets very confusing to understand what's going on. Once you know what combination of styles you need then you can map it back to the VCL. That said, Andreas's answer appears to do the job, so long as your Delphi is up to date enough.Starter
@David: You probably mean the opposite. Anyway, is that a problem?Litchfield
Normally, with an empty VCL project (and thus BorderStyle := bsSizeable), WS_EX_APPWINDOW is set. It is also set if you change BorderStyle to bsNone. But if you set MainFormOnTaskbar to false, then WS_EX_APPWINDOW will not be set. Hence, "[my] answer results in WS_EX_APPWINDOW being excluded".Litchfield
@Andreas I've updated my answer and deleted my rambling comments. Does this now meet with your approval?Starter
Yes, now I think you got the best solution. :)Litchfield
Oh, and a +1. Sorry, almost forgot that! (And I think that you forgot to or WS_POPUP to get rid of the window frame, at least in a VCL CreateParams setting.)Litchfield
Cool. I hadn't thought of SetWindowLong yet.Munificent
@Andreas This is C++, and it doesn't need WS_POPUP.Starter
I removed my answer, since it apparently doesn't work well with Windows 7. Still, I would suggest you add the Delphi CreateParams code, to get a better answer, and more upvotes! Style should be WS_POPUP or WS_MINIMIZEBOX.Litchfield
@Andreas I've changed it to WS_POPUP, although it doesn't seem to make any difference to me. I'm sure Warren can translate this to CreateParams. But I actually think this is better plain Win32 API code rather than VCL, for the purpose of understanding. You don't want to have the VCL setting things behind your back.Starter
@no it doesn't!! The extract above when pasted into a blank win32 project in visual studio meets all your requirements.Starter
Incidentally, besides being a good answer, David makes a good point, that working with the raw API without using any VCL classes, can be very helpful to determining how to do a particular thing with the windows apis.Munificent
I think that your answer would be slightly better without the ellipsis in the code sample.Munificent
@Warren don't agree, I intended to draw attention to GWL_STYLE and GWL_EXSTYLE; CreateWindow has a million parameters that aren't relevant to the problemStarter
Nice one. I thought I had found a corner-case where windows couldn't do some basic, obvious thing. :-)Munificent
C
4

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);
Carven answered 4/2, 2011 at 19:37 Comment(2)
This has some limitations for an application main window. I would consider this a good solution for an about-box, but not for an app main window.Munificent
I wouldn't consider it a good solution to strip the border from a main window at all. :) But can you elaborate on what goed wrong and what you want this code to do?Carven
S
-4

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;
Snowblind answered 4/2, 2011 at 21:53 Comment(1)
Stating the obvious here a little.Houphouetboigny

© 2022 - 2024 — McMap. All rights reserved.