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

ove*_*tor 4 delphi vcl delphi-xe3

我正在使用自定义表单的一个相当大的项目中工作,我想自己绘制这些表单的非客户区域。我不能使用vcl样式,因为许多表格需要在运行时选择带有颜色的标题栏和边框,据我所知,样式在设计上是广泛应用的。

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

SendMessage(Handle, WM_NCACTIVATE, ORD(active), -1);
Run Code Online (Sandbox Code Playgroud)

之所以这样做,是因为我无法使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;
Run Code Online (Sandbox Code Playgroud)

我通过处理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;
...
Run Code Online (Sandbox Code Playgroud)

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

RRU*_*RUZ 5

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

。在某些形式之间切换焦点时,会有很多闪烁。

Q。这个问题可能有多种原因,但主要是在画布上使用了对draw方法的多次调用,您可以使用位图缓冲区(TBitmap)将所有标题栏绘制到位图的画布上,最后调用Canvas来解决此问题。通过位图绘制一次。

。有时会显示原始的右上按钮,尽管它们不再对鼠标做出反应。

请参阅下一个问题的答案。

。关闭表单时,标题栏(仅标题栏)恢复为旧外观。

问:这是因为在还原或调整表单大小时需要使表单的NC区域无效,因此必须添加对一些其他消息的支持,例如WM_WINDOWPOSCHANGING,WM_SIZE,WM_MOVE,WM_NCMOUSEMOVE,WM_NCLBUTTONDOWN,WM_NCRBUTTONDOWN等

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

TStyleManager.Engine.RegisterStyleHook(TMyForm1, TMyCustomformStyleHook1);
TStyleManager.Engine.RegisterStyleHook(TMyForm2, TMyCustomformStyleHook2);
Run Code Online (Sandbox Code Playgroud)