【问题标题】:Create a borderless form without losing Windows commands在不丢失 Windows 命令的情况下创建无边框表单
【发布时间】:2019-02-12 17:15:12
【问题描述】:

我已将表单更改为无边框表单,我只是将 BorderStyle 属性更改为 bsNone,但现在我的应用程序丢失了 windows 锚点和一些类似的命令

WIN + ↑(对齐表单客户端)
WIN + ↓(最小化表单)
WIN + →(对齐表单右)
WIN + ←(对齐表单左)

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

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;

这个结果:

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

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

已编辑

如果我使用WS_THICKFRAME,我的表单会返回一个小的上边框,并且 windows 命令运行良好,但我不想要那个上边框。

已编辑 2

我已经非常接近预期的结果,但还有一点问题......

我把这个放在我的FormCreate

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

然后我创建了方法

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

然后

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;

我得到了这个方法here

现在边框已经消失了,但是当我的Form失去焦点时,又显示了上/下边框....

我怎样才能避免这种情况?


已解决

我离开边界为BorderStyle: bsSizeable,然后我做到了:

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;

Github 上的解决方案

我创建了一个repository here

【问题讨论】:

  • 所以基本上你想要this
  • @whosrdaddy 是的,我已经在 C# 中使用 WPF 完成了此操作,但我不知道如何在 Delphi 中应用此逻辑...我在 C# 中做到了,我没有输windows 命令。
  • 基本上你做同样的事情(覆盖 createparams 并覆盖 wndproc)...
  • @SertacAkyuz 我试过了,但出现了一个小边框,请再看看我的问题。
  • 那是我的观点。

标签: delphi delphi-xe rad-studio


【解决方案1】:

您提到的一些命令是与窗口大小相关的系统命令。这需要厚框架,没有它“WIN + 右”和“WIN + 左”将不起作用。此外,您需要最小化框和最大化框才能使 WIN + 向上/向下命令起作用。

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

procedure TForm1.FormCreate(Sender: TObject);
begin
  BorderStyle := bsNone;
  SetWindowLong(Handle, GWL_STYLE, WS_CLIPCHILDREN or WS_OVERLAPPEDWINDOW);
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;

此时读取消息的documentation是强制性的,参数在不同阶段有不同的含义等等。


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

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

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

  // don't call inherited
end;


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



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

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.

【讨论】:

  • 这有效-,你帮了我很多!现在我只需要做一些调整......
猜你喜欢
  • 1970-01-01
  • 2019-12-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多