在运行时创建自定义标题栏和边框,TMainMenu起作用

时间:2014-05-28 18:18:12

标签: delphi vcl delphi-xe3

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

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

SendMessage(Handle, WM_NCACTIVATE, ORD(active), -1);

我这样做的原因是因为我无法让TMainMenu一致地绘制自己,在单步执行代码之后,似乎正确处理WM_NCACTIVATE消息会绘制主菜单。 我在那里尝试的另一种方法是在主菜单上调用UpdateItems,但这并没有给出任何结果。

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

procedure TBasicForm.WMNCHITTEST(var message : TMessage);
begin
  inherited;
  case message.Result of
    HTMINBUTTONE, HTMAXBUTTON, HTCLOSE: message.Result := HTCAPTION;
  end;
end;

我通过处理WM_NCLBUTTONDOWN获得了我自己的按钮(我在处理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)应该短暂显示原始标题栏。

1 个答案:

答案 0 :(得分:4)

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

<强> A 即可。在某些方式之间切换焦点时会有很多闪烁。

即可。这个问题可能有很多原因,但主要是在画布上使用几个调用draw方法,你可以使用位图缓冲区( TBitmap )来克服这个问题,将所有标题栏绘制到画布上位图,最后只要一次通过位图就调用Canvas.Draw。

<强> A 即可。有时原始的右上角按钮会出现,但他们不再对鼠标做出反应。

查看下一个问题的答案。

<强> A 即可。关闭表单时,标题栏(仅限标题)栏将恢复其旧外观。

Q 这是因为在表单恢复或调整大小时需要使表单的NC区域无效,因此您必须添加对WM_WINDOWPOSCHANGING,WM_SIZE,WM_MOVE,WM_NCMOUSEMOVE等其他消息的支持, WM_NCLBUTTONDOWN,WM_NCRBUTTONDOWN等等

要避免所有这些工作,您可以使用VCL样式,为此必须覆盖 TFormStyleHook 类并实现一组自定义样式挂钩并应用于您要自定义标题的表单bar使用 RegisterStyleHook 方法,如此

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