创建无边界表单而不会丢失Windows命令 [英] Create a borderless form without losing Windows commands
问题描述
我已经将表单更改为无边界表单,只是将 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 tobsNone
, but now my application loses the windows anchor and some commands likeWIN + ↑ (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 theFormCreate
, 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屋!