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

Matheus Miranda
  • 434
  • 1
  • 6
  • 17
  • so basically you want [this](https://stackoverflow.com/q/31199437/800214)? – whosrdaddy Feb 12 '19 at 17:29
  • @whosrdaddy 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. – Matheus Miranda Feb 12 '19 at 17:35
  • 1
    basically you do the same thing (override createparams and override wndproc)... – whosrdaddy Feb 12 '19 at 17:36
  • 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. – Sertac Akyuz Feb 12 '19 at 17:55
  • 1
    @SertacAkyuz I tried this but a little border appears, look my question again, please. – Matheus Miranda Feb 12 '19 at 18:04
  • 1
    That was my point. – Sertac Akyuz Feb 12 '19 at 18:07
  • Hey @SertacAkyuz I found your answer [here](https://stackoverflow.com/a/8394131/10145630) that helped me a little. Do you know how I can avoid the **EDITED 2** problem? – Matheus Miranda Feb 13 '19 at 13:38
  • You might be on the right track. You should perhaps paint the border yourself. – Sertac Akyuz Feb 13 '19 at 13:47
  • I don't get the deal though. Eventually you have a sizeable window, why don't you want to have it proper? – Sertac Akyuz Feb 13 '19 at 13:48
  • 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. – Matheus Miranda Feb 13 '19 at 13:57
  • 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). – Sertac Akyuz Feb 13 '19 at 18:40
  • @SertacAkyuz If I leave `BorderStyle: = bsNone;` when I use WIN + Arrows the window looks strange, some parts of the screen get black – Matheus Miranda Feb 14 '19 at 10:24
  • 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. – Sertac Akyuz Feb 14 '19 at 14:25
  • 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. – Matheus Miranda Feb 14 '19 at 15:01

1 Answers1

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.
Community
  • 1
  • 1
Sertac Akyuz
  • 54,131
  • 4
  • 102
  • 169