Delphi 在运行时创建自定义标题栏和边框,TMainMenu运行正常

Delphi 在运行时创建自定义标题栏和边框,TMainMenu运行正常,delphi,vcl,delphi-xe3,Delphi,Vcl,Delphi Xe3,我正在从事一个相当大的项目,该项目使用自定义表单,并且希望自己绘制这些表单的非客户端区域。我不能使用vcl样式,因为许多表单都需要在运行时选择颜色的标题栏和边框,据我所知,样式是应用程序范围内的设计 到目前为止,我所完成的是绘制标题栏和边框,绘制标题,禁用最小化、最大化和关闭按钮,并用我自己的按钮替换它们。我通过截取WM_NCPaint和WM_NCACTIVATE消息,并在处理WM_NCACTIVATE时在继承的关键字后调用我自己的过程,并在不处理WM_NCPaint消息的情况下发送WM_ACT

我正在从事一个相当大的项目,该项目使用自定义表单,并且希望自己绘制这些表单的非客户端区域。我不能使用vcl样式,因为许多表单都需要在运行时选择颜色的标题栏和边框,据我所知,样式是应用程序范围内的设计

到目前为止,我所完成的是绘制标题栏和边框,绘制标题,禁用最小化、最大化和关闭按钮,并用我自己的按钮替换它们。我通过截取WM_NCPaint和WM_NCACTIVATE消息,并在处理WM_NCACTIVATE时在继承的关键字后调用我自己的过程,并在不处理WM_NCPaint消息的情况下发送WM_ACTIVATE消息来实现这一点:

SendMessage(Handle, WM_NCACTIVATE, ORD(active), -1);
procedure TBasicForm.WMNCHITTEST(var message : TMessage);
begin
  inherited;
  case message.Result of
    HTMINBUTTONE, HTMAXBUTTON, HTCLOSE: message.Result := HTCAPTION;
  end;
end;
我这样做的原因是因为我无法让TMainMenu自己一致地绘制,在单步执行代码之后,似乎正确处理的WM_NCACTIVATE消息绘制了主菜单。 我尝试过的另一种方法是在主菜单上调用UpdateItems,但没有给出任何结果

我通过如下方式处理WM_NCHITTEST消息来停用右上角的按钮:

SendMessage(Handle, WM_NCACTIVATE, ORD(active), -1);
procedure TBasicForm.WMNCHITTEST(var message : TMessage);
begin
  inherited;
  case message.Result of
    HTMINBUTTONE, HTMAXBUTTON, HTCLOSE: message.Result := HTCAPTION;
  end;
end;
我通过处理WM_nclubuttondown获得了自己的按钮(我在处理WM_NCACTIVATE时调用的过程中绘制),这不是一个完美的解决方案,但可以很容易地改进。(我相信我不需要详细说明这一点

不过,到目前为止,这听起来还不错

  • 当以某种方式在窗体之间切换焦点时,会有很多闪烁
  • 有时会出现原来的右上角按钮,尽管它们不再对鼠标做出反应
  • 关闭表单时,标题栏(仅标题栏)将恢复为原来的外观
直接的问题是,我如何解决这三个问题?可能是我的做法完全错了,在这种情况下,问题是,我如何实现自定义绘制的标题栏和边框,最好不要过多地干预边框和标题栏的功能,也不要使用主标题栏菜单画得好吗

正如我所说的,这是一个有很多形式的相当大的项目,所以在表单设计器中改变事物是我考虑的最后一件事。

若要重现我遇到的问题,请创建一个新表单,并按照前面所述处理WM_NCHITTEST、WM_NCACTIVATE和WM_NCPAINT

...
procedure WMNCHITTEST(var message : TMessage); message WM_NCHITTEST;
procedure WMNCACTIVATE(var message : TMessage); message WM_NCACTIVATE;
procedure WMNCPAINT(var message : TMessage); message WM_NCPAINT;
...
implementation
...
procedure TBasicForm.WMNCHITTEST(var message : TMessage);
begin
  inherited;
  case message.Result of
    HTMINBUTTONE, HTMAXBUTTON, HTCLOSE: message.Result := HTCAPTION;
  end;
end;

procedure TBasicForm.WMNCACTIVATE(var message : TMessage);
begin
  inherited;
  Canvas.Brush.Style := bsSolid;
  Canvas.Brush.Color := clRed;

  Canvas.Rectangle(0, 0, Width, GetSystemMetric(SM_CYCAPTION) + GetSystemMetric(SM_CYBORDER));
  Canvas.Rectangle(0, 0, GetSystemMetric(SM_CXBORDER), Height);
  Canvas.Rectangle(Width - GetSystemMetric(SM_CXBORDER), 0, Width, Height);
  Canvas.Rectangle(Width - GetSystemMetric(SM_CXBORDER), Heigth - GetSystemMetric(SM_CYBORDER), Width, Height);
end;

procedure TBasicForm.WMNCPAINT(var message : TMessage);
begin
  SendMessage(Handle, WM_NCACTIVATE, ORD(active), -1);
end;
...

现在,向项目中添加第二个表单,确保两个表单都已创建,并在两个表单之间反复切换焦点(同时尝试单击第二个表单,然后单击第一个表单的自定义绘制标题栏),这将导致一些闪烁,并显示关闭、最小和最大按钮。关闭表单(按alt+f4)应该简要显示原始标题栏。

编写一个适当的类来绘制表单的非客户端区域需要大量工作,您已经在处理一些基本的windows消息,但还有很多。根据我的经验,这些是我的建议

A。在某些形式之间切换焦点时,会出现大量闪烁

Q。此问题可能有许多原因,但主要原因是在画布上多次调用draw方法,您可以使用位图缓冲区(TBitmap)将所有标题栏绘制到位图的画布,并最终调用画布。只需通过位图绘制一次即可

A有时会出现原来的右上角按钮,尽管它们不再对鼠标做出反应

请参阅下一个问题的答案

A。关闭表单时,标题栏(仅标题)将恢复为原来的外观

Q这是因为在恢复或调整窗体大小时,需要使窗体的NC区域无效,因此必须添加对一些附加消息的支持,如WM_WindowPosChange、WM_SIZE、WM_MOVE、WM_NCMOUSEMOVE、WM_nCButtonDown、WM_nCButtonDown等

为了避免所有这些工作,您可以使用VCL样式,为此,您必须重写TFormStyleHook类并实现一组自定义样式挂钩,并在希望使用RegisterStyleHook方法自定义标题栏的表单中应用,如下所示

TStyleManager.Engine.RegisterStyleHook(TMyForm1, TMyCustomformStyleHook1);
TStyleManager.Engine.RegisterStyleHook(TMyForm2, TMyCustomformStyleHook2);

如果这个问题的格式不正确或缺少信息,我提前向您道歉,并请您通知我;我真的很想做任何事情来获得最好的答案。请出示SSCCE。此外,寻求帮助并没有帮助,还说您不准备更改您的设计。如果设计被破坏,就这样吧。拒绝使用VCL样式似乎也有点可疑。这听起来正是你所需要的。通过改变设计中的东西,我指的是表单设计师,因为有这么多表单。至于VCL样式,我假设你不能将不同的样式应用于不同的表单,这是我所需要的。我将调整我的问题,并尝试包括SSCCE。Thanks表示您对任何情况都感兴趣。您走错了方向。请创建一个无边界表单,并模仿正常表单的行为。@PeterVonča这是否与主菜单一起正常工作,这是否会扰乱表单上所有组件的位置?正如我所说,该项目已经创建了大量表单,有些是多个表单从这个表单继承的步骤。此外,它必须与普通表单并行工作,这取决于保存在注册表中的布尔值(至少现在)。对于使用VCL样式定制表单背景和标题栏的示例,您可以阅读我的博客文章,这似乎会起作用,但有一个问题,我是否可以对两个不同的表单使用相同的样式挂钩,并使每个表单具有不同的颜色?如果是,我将如何进行此操作,如果不是,我如何重新使用co当创建不同的样式挂钩时,de