Create a borderless form without losing Windows commands
Asked Answered
B

1

5

I've changed my form to a borderless form, I just changed the BorderStyle property to bsNone, but now my application loses the windows anchor and some commands like

WIN + ↑ (Align the form Client)
WIN + ↓ (Minimize the form)
WIN + →(Align the form Right)
WIN + ←(Align the form Left)

I've tried to set BorderStyle: bsSizeable and use the below code inside of the FormCreate, but this does not worked:

procedure TfrmBase.FormCreate(Sender: TObject);
begin
  SetWindowLong(Handle
               ,GWL_STYLE
               ,GetWindowLong(Handle, GWL_STYLE)
                AND (NOT WS_CAPTION)
                AND (NOT WS_THICKFRAME)
               );


  Refresh;
  FormColor := oLauncher.oCor;
end;

This results:

My form

The image above is what I want, but the Windows commands that I already have mentioned don't work

Have any way to set the BorderStyle: bsNone and don't lose these commands?

EDITED

If I use the WS_THICKFRAME my form returns a little top border and the windows commands works well, but I don't want that top border.

My form2

EDITED 2

I got very close to the expected result, but have a little problem yet...

I put this on my FormCreate

SetWindowLong(Handle
             ,GWL_STYLE
             ,GetWindowLong(Handle, GWL_STYLE)
              AND (NOT WS_CAPTION)
              );

And I create the method

private
   procedure WmNCCalcSize(var Msg: TWMNCCalcSize); message WM_NCCALCSIZE;

and then

procedure TfrmBase.WmNCCalcSize(var Msg: TWMNCCalcSize);
begin
  inherited;
  if Msg.CalcValidRects then
  begin
    InflateRect(Msg.CalcSize_Params.rgrc[0], 0, 6);
    Msg.Result := 0;
  end;
end;

I got this method here

Now the border has disappeared, but when my Form loses the focus, the top / bottom border is shown again....

How can I avoid this?

enter image description here


SOLVED

I left the border as BorderStyle: bsSizeable, then I did it:

private
  procedure WmNCCalcSize(var Msg: TWMNCCalcSize); message WM_NCCALCSIZE;
[...]
procedure TfrmBase.WmNCCalcSize(var Msg: TWMNCCalcSize);
var
  R: TRect;
begin
  if not Msg.CalcValidRects then
    R := PRect(Msg.CalcSize_Params)^;
  inherited;
  if Msg.CalcValidRects then
    Msg.CalcSize_Params.rgrc0 := Msg.CalcSize_Params.rgrc1
  else
    PRect(Msg.CalcSize_Params)^ := R;

  Msg.Result := 0;
end;

procedure TfrmBase.FormCreate(Sender: TObject);
begin
  BorderStyle := bsNone;
  SetWindowLong(Handle
               ,GWL_STYLE
               ,WS_CLIPCHILDREN or WS_OVERLAPPEDWINDOW
               );
end;

procedure TfrmBase.FormShow(Sender: TObject);
begin
  Width := (Width - 1);
end;

Solution at GitHUB

I've create a repository here

Bag answered 12/2, 2019 at 17:15 Comment(14)
so basically you want this?Serried
@Serried Yes, I've already done this in C# with WPF, but I don't know how to apply this logic in Delphi... I did that in C# and I didn't lose the windows commands.Bag
basically you do the same thing (override createparams and override wndproc)...Serried
For a system command to work, you need to have the corresponding system command enabled. I mean, you need WS_SIZEBOX, for instance for win->, to enable SC_SIZE. But that comes with the thick frame. IOW, the frame is what makes it work.Antoniaantonie
@SertacAkyuz I tried this but a little border appears, look my question again, please.Bag
That was my point.Antoniaantonie
Hey @SertacAkyuz I found your answer here that helped me a little. Do you know how I can avoid the EDITED 2 problem?Bag
You might be on the right track. You should perhaps paint the border yourself.Antoniaantonie
I don't get the deal though. Eventually you have a sizeable window, why don't you want to have it proper?Antoniaantonie
I want build my own title bar without losing the windows commands and without using styles... But if I use a BorderStyle: bsNone, my form loses these commands, then I'm using a BorderStyle: bsSizeable and i'm working to hide the title and his top border.Bag
RE (solved): "* left the border as BorderStyle: bsSizeable*" - Note the "BorderStyle := bsNone;" in the OnCreate event handler, if you leave it out this doesn't work, the design setting doesn't take effect. Anyhow, I presume "Width - 1" is forcing a frame update, you can probably achieve it with SetWindowPos (SWP_FRAMECHANGED) or RedrawWindow (RDW_FRAME).Antoniaantonie
@SertacAkyuz If I leave BorderStyle: = bsNone; when I use WIN + Arrows the window looks strange, some parts of the screen get blackBag
Ok, thanks. Seems to display different behavior on different environment, I don't like it. Try to test on as many OS as you can before deploying.Antoniaantonie
It's an internal software that help my team day-by-day, till now I have tested on Windows 10 and Windows 7, and with this change that I've done, works well on both of them.Bag
A
2

Some of the commands you refer to are system commands related to sizing of the window. That requires the thick frame, without it "WIN + right" and "WIN + left" won't work. Additionally you need the minimize box and the maximize box for the WIN + up/down commands to work.

Best is to start from scratch and include the styles you need, otherwise VCL might interfere. If there's a possibility of your form to be recreated, put styling in a CreateWnd override.

procedure TForm1.FormCreate(Sender: TObject);
begin
  BorderStyle := bsNone;
  SetWindowLong(Handle, GWL_STYLE, WS_CLIPCHILDREN or WS_OVERLAPPEDWINDOW);
end;


Then there's the frame that you don't want. In an edit in the question you inflate the client rectangle to get rid of it. Don't guess the frame width/height, do it like the below.

procedure TForm1.WMNCCalcSize(var Message: TWMNCCalcSize);
var
  R: TRect;
begin
  if not Message.CalcValidRects then
    R := PRect(Message.CalcSize_Params)^;
  inherited;
  if Message.CalcValidRects then
    Message.CalcSize_Params.rgrc0 := Message.CalcSize_Params.rgrc1
  else
    PRect(Message.CalcSize_Params)^ := R;
  Message.Result := 0;
end;

Reading the documentation for the message is mandatory at this point, the parameters have different meanings at different stages, etc..


The above leaves a window without any non-client area at all. The client rectangle is equal to the window rectangle. Although the caption is not visible, you can activate the system menu by pressing Alt+Space. The problem is, the system insists on drawing activation state. Now it draws a frame in the client area!!

Get rid of it by intercepting WM_NCACTIVATE, you also need it to draw your title according to the activation status:

procedure TForm1.WMNCActivate(var Message: TWMNCActivate);
begin
  if Message.Active then
    // draw active caption
  else
    // draw incactive caption

  // don't call inherited
end;


You might have to deal with some glitches, messing up with the window has consequences. In my test, the minimized form does not have an associated icon in the alt+tab dialog for instance.



Below is my test unit in full.

unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
  System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs;

type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
  protected
    procedure WMNCActivate(var Message: TWMNCActivate); message WM_NCACTIVATE;
    procedure WMNCCalcSize(var Message: TWMNCCalcSize); message WM_NCCALCSIZE;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
  BorderStyle := bsNone;
  SetWindowLong(Handle, GWL_STYLE, WS_CLIPCHILDREN or WS_OVERLAPPEDWINDOW);
end;

procedure TForm1.WMNCActivate(var Message: TWMNCActivate);
begin
  if Message.Active then
    // draw active caption
  else
    // draw incactive caption

  // don't call inherited
end;

procedure TForm1.WMNCCalcSize(var Message: TWMNCCalcSize);
var
  R: TRect;
begin
  if not Message.CalcValidRects then
    R := PRect(Message.CalcSize_Params)^;
  inherited;
  if Message.CalcValidRects then
    Message.CalcSize_Params.rgrc0 := Message.CalcSize_Params.rgrc1
  else
    PRect(Message.CalcSize_Params)^ := R;
  Message.Result := 0;
end;

end.
Antoniaantonie answered 13/2, 2019 at 15:39 Comment(1)
This worked -, you helped me a lot man!! Now I just need to make some adjustments...Bag

© 2022 - 2024 — McMap. All rights reserved.