创建无边界表单而不会丢失Windows命令 [英] Create a borderless form without losing Windows commands

查看:85
本文介绍了创建无边界表单而不会丢失Windows命令的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经将表单更改为无边界表单,只是将 BorderStyle 属性更改为 bsNone ,但是现在我的应用程序丢失了Windows锚点和一些命令,例如


WIN +↑(对齐窗体客户端)
WIN +↓(最小化窗体)
WIN +→(将窗体右对齐)
WIN +←(将窗体左对齐)


我尝试设置 BorderStyle:bsSizeable 并在 FormCreate 中使用以下代码,但这不起作用:

 过程TfrmBase.FormCreate(Sender:TObject); 
开始
SetWindowLong(句柄
,GWL_STYLE
,GetWindowLong(句柄,GWL_STYLE)
AND(不是WS_CAPTION)
和(不是WS_THICKFRAME)
);


刷新;
FormColor:= oLauncher.oCor;
结尾;

结果:





上面的图像是我想要的,但是我已经提到的Windows命令不起作用



有什么方法可以设置 BorderStyle:bsNone 并且不丢失这些命令吗?



已编辑



如果我使用 WS_THICKFRAME ,我的表单会返回一个顶部边框和Windows命令运行良好,但我不希望该顶部边框。





编辑2



我与预期结果非常接近,但是还有一点问题...



我把它放在我的 FormCreate



  SetWindowLong(句柄
,GWL_STYLE
,GetWindowLong(句柄,GWL_STYLE)
AND(不是WS_CAPTION)
);

然后创建方法

  private 
过程WmNCCalcSize(var Msg:TWMNCCalcSize);消息WM_NCCALCSIZE;

,然后

 过程TfrmBase.WmNCCalcSize(var Msg:TWMNCCalcSize); 
开始
继承;
如果Msg.CalcValidRects然后
开始
InflateRect(Msg.CalcSize_Params.rgrc [0],0,6);
Msg.Result:= 0;
结尾;
结尾;




我得到了这种方法






已解决



我以 BorderStyle:bsSizeable ,然后我做到了:

  private 
过程WmNCCalcSize(var Msg:TWMNCCalcSize);消息WM_NCCALCSIZE;
[...]
过程TfrmBase.WmNCCalcSize(var Msg:TWMNCCalcSize);
var
R:TRect;
如果不是Msg.CalcValidRects,则以
开始,然后
R:= PRect(Msg.CalcSize_Params)^;
继承了;
如果Msg.CalcValidRects然后
Msg.CalcSize_Params.rgrc0:= Msg.CalcSize_Params.rgrc1
else
PRect(Msg.CalcSize_Params)^:= R;

Msg.Result:= 0;
结尾;

过程TfrmBase.FormCreate(Sender:TObject);
开始
BorderStyle:= bsNone;
SetWindowLong(处理
,GWL_STYLE
,WS_CLIPCHILDREN或WS_OVERLAPPEDWINDOW
);
结尾;

过程TfrmBase.FormShow(Sender:TObject);
开始
宽度:=(宽度-1);
结尾;




GitHUB的解决方案



我在这里创建了此处的存储库



解决方案

您引用的某些命令是与窗口大小相关的系统命令。这就需要厚的框架,而没有 WIN + right。和 WIN +左将无法正常工作。另外,您还需要最小化框和最大化框才能使WIN +向上/向下命令起作用。


最好从头开始,并包含所需的样式,否则VCL可能会干扰。如果您的表格有可能被重新创建,请将样式放在 CreateWnd 替代中。

 过程TForm1.FormCreate(Sender:TObject); 
开始
BorderStyle:= bsNone;
SetWindowLong(句柄,GWL_STYLE,WS_CLIPCHILDREN或WS_OVERLAPPEDWINDOW);
结尾;


然后就出现了您不想要的框架。在问题的编辑中,您可以膨胀客户端矩形以摆脱该矩形。

 过程TForm1.WMNCCalcSize(var Message:TWMNCCalcSize);不要猜测框架的宽度/高度。 
var
R:TRect;
如果不是Message.CalcValidRects,则以
开头,然后
R:= PRect(Message.CalcSize_Params)^;
继承了;
如果Message.CalcValidRects然后
Message.CalcSize_Params.rgrc0:= Message.CalcSize_Params.rgrc1
else
PRect(Message.CalcSize_Params)^:= R;
Message.Result:= 0;
结尾;

阅读文档,该参数在不同阶段具有不同的含义,等等。



上面的窗口完全没有任何非客户区域。客户矩形等于窗口矩形。尽管标题不可见,但是您可以通过按Alt + Space激活系统菜单。问题是,系统坚持绘图激活状态。现在,它在客户区域中绘制了一个框架!


通过拦截 WM_NCACTIVATE 摆脱它,您还需要它来绘制您的根据激活状态的标题:

 过程TForm1.WMNCActivate(var Message:TWMNCActivate); 
如果Message.Active则以
开始
//然后绘制
//绘制活动标题
else
//绘制不活动的标题

//不调用继承的
结尾;


您可能不得不处理一些小故障,弄乱窗户会产生后果。在我的测试中,例如,最小化窗体在alt + tab对话框中没有关联的图标。






下面是我的完整测试单位。

  unit Unit1; 

界面

使用
Winapi.Windows,Winapi.Messages,System.SysUtils,System.Variants,
System.Class,Vcl.Graphics, Vcl.Controls,Vcl.Forms,Vcl.Dialogs;

类型
TForm1 = class(TForm)
过程FormCreate(Sender:TObject);
受保护的
过程WMNCActivate(var Message:TWMNCActivate);消息WM_NCACTIVATE;
过程WMNCCalcSize(var消息:TWMNCCalcSize);消息WM_NCCALCSIZE;
结尾;

var
Form1:TForm1;

实现

{$ R * .dfm}

过程TForm1.FormCreate(Sender:TObject);
开始
BorderStyle:= bsNone;
SetWindowLong(句柄,GWL_STYLE,WS_CLIPCHILDREN或WS_OVERLAPPEDWINDOW);
结尾;

过程TForm1.WMNCActivate(var消息:TWMNCActivate);
如果Message.Active则以
开始
//然后绘制
//绘制活动标题
else
//绘制不活动的标题

//不调用继承的
结尾;

过程TForm1.WMNCCalcSize(var消息:TWMNCCalcSize);
var
R:TRect;
如果不是Message.CalcValidRects,则以
开头,然后
R:= PRect(Message.CalcSize_Params)^;
继承了;
如果Message.CalcValidRects然后
Message.CalcSize_Params.rgrc0:= Message.CalcSize_Params.rgrc1
else
PRect(Message.CalcSize_Params)^:= R;
Message.Result:= 0;
结尾;

结尾。


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:

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.

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?


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

解决方案

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.

这篇关于创建无边界表单而不会丢失Windows命令的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆