VCL样式 - 表单的客户端大小减少

时间:2012-02-25 18:33:59

标签: delphi delphi-xe2 vcl-styles

我不知道这是不是一个错误...但是当我设置除“Windows”之外的任何其他VCL样式时,窗口宽度会减小。

Windows style Any other style looks like this...   -

有没有解决方案?

更新 我已将此提交给QC:http://qc.embarcadero.com/wc/qcmain.aspx?d=103697 希望他们能解决它......

8 个答案:

答案 0 :(得分:5)

这不是vcl样式bug,这是vcl样式的工作方式,每个样式(皮肤)都有自己的边框宽度和高度,有时与本机窗口边框不匹配尺寸。

检查下一张图片

enter image description here

碳风格的边框宽度和高度为5像素

enter image description here

Amakrits风格的边框宽度和高度为6像素

enter image description here

您可以使用VCL Styles Designer

检查每种样式的边框样式大小
  • 对象 - >表格 - >图像 - > LeftBorder - >宽度
  • 对象 - >表格 - >图像 - > RigthBorder - >宽度
  • 对象 - >表格 - >图像 - > BottomBorder - >高度

因此,根据上述属性,表单的Style钩子重新计算Client区域的边界。

答案 1 :(得分:4)

确实看起来确实是一个VCL错误。当在项目选项中设置样式而不是系统样式时,{。1}}属性未从.dfm文件正确流式传输。

我建议您向QualityCentral提交报告。在此期间,您可以通过在创建表单后在.dpr文件中设置样式来解决此问题。

ClientWidth

但是,我认为这不会让你走得太远,因为你可能希望能够动态创建表单,而不必在启动时创建全部表单。

答案 2 :(得分:4)

好的 - 我做了一些调查,发现了这个bug的根本问题(跳到最后解决方法)。分散在互联网上并在此消息之前讨论的大多数/所有其他解决方法似乎只是掩盖了错误的症状,而没有真正找到根本原因 - 而那些其他解决方法可能会产生其他不良副作用或限制(正如他们的一些作者所指出的那样。)

根本问题是TFormStyleHook.WMNCCalcSize消息在wParam参数为FALSE时未提供WM_NCCALCSIZE消息的任何处理。功能基本上不完整。所以默认窗口处理程序被称为 - Windows提供的默认处理程序 - 当然返回Windows默认样式的客户端rect,而不是用户指定的VCL样式。要修复此错误,当WM_NCCALCSIZEwParam时,Embarcadero必须添加FALSE的处理,以便仍然返回VCL样式信息。这对他们来说是一个非常简单的修复方法,现在我已经调查并发现了问题,我希望修复程序可以应用于VCL的下一个版本。

为了证明这是问题的原因,我记录了发送到表单的所有消息(通过覆盖WndProc),并为每条消息指出Win32 GetClientRect提供的客户端是否为正确的VCL风格。我还注意到WM_NCCALCSIZE函数调用的类型(wParam的值)。最后,我注意到WM_NCCALCSIZE处理程序返回的新客户端rect。

我发现在应用程序运行时,几乎每一条WM_NCCALCSIZE消息都将wParam设置为TRUE(这样可以正常工作),因此该错误是隐藏的,不会发生。这就是为什么Embarcadero到目前为止已经解决了这个问题。但是,邮件会在wParam设置为FALSE的情况下发送,并且会在关键时刻发生:恰好在ClientWidth / ClientHeight属性设置为来自DFM TCustomForm.ReadState档案。 TControl.SetClientSize函数通过从当前总窗口宽度减去当前客户端宽度(由Windows GetClientRect测量)来运行,然后添加新的客户端宽度。 换句话说,TControl.SetClientSize要求当前窗口客户端rect准确,因为它使用它来计算新客户端rect。并且由于它不是,因此表单的宽度设置错误,其余的都是历史。

哦,你想知道为什么宽度受到影响而不是高度?这很容易证明 - 在设置ClientWidth之后但在ClientHeight设置之前,发送了另一个WM_NCCALCSIZE - 这次wParam为{{1} }}。 VCL样式正确处理它并将客户端大小设置回适当的值 - 因此TRUE的计算结果正确。

请注意,Windows的未来版本可能会破坏得更厉害:如果Microsoft决定更频繁地发送ClientHeight消息,WM_NCCALCSIZE设置为wParam,即使表单可见,事情也会中断VCL非常糟糕。

通过手动将FALSE发送到表单,可以很容易地证明该错误。重现的步骤:

  1. 在C ++ Builder中创建一个新的 VCL表单应用程序
  2. 项目选项外观部分将当前/默认VCL样式设置为 VCL样式。
  3. 向表单添加新的WM_NCCALCSIZE控件。
  4. 将以下代码添加到按钮的TButton事件:

    OnClick
  5. 运行项目并单击按钮。如果通过测试,则意味着VCL样式的NC宽度恰好与默认的Windows NC宽度一致。更改表单的边框样式或将VCL样式更改为其他样式,然后重试。

  6. 当然,解决方法是找到一种方法来拦截void __fastcall TForm1::Button1Click(TObject *Sender) { // Compute the current cumulative width of the form borders: int CurrentNonClientWidth = Width - ClientWidth; // Get the current rectangle for the form: TRect rect; ::GetWindowRect(Handle, &rect); // Ask the window to calculate client area from the window rect: SendMessage(Handle, WM_NCCALCSIZE, FALSE, (LPARAM)&rect); // Calculate the new non-client area given by WM_NCCALCSIZE. It *should* // match the value of CurrentNonClientWidth. int NewNonClientWidth = Width - rect.Width(); if (CurrentNonClientWidth == NewNonClientWidth) { ShowMessage("Test pass: WM_NCCALCSIZE with wParam FALSE gave " "the right result."); } else { ShowMessage(UnicodeString::Format(L"Test fail: WM_NCCALCSIZE with " "wParam FALSE gave a different result.\r\n\r\nCurrent NC width: %d" "\r\n\r\nNew NC width: %d", ARRAYOFCONST(( CurrentNonClientWidth, NewNonClientWidth)))); } } WM_NCCALCSIZE的{​​{1}}邮件,然后将其转换为wParam为{{{}}的邮件1}}。这实际上可以在全局基础上完成:我们可以从FALSE创建一个修复问题的派生类,然后全局使用钩子 - 这将解决所有表单上的问题,包括VCL创建的表单(例如来自Vcl.Dialogs单位)。在上面显示的示例项目中,修改主wParam,如下所示:

    TRUE

    现在运行项目并单击按钮;你会看到现在正确处理了WM_NCCALCSIZE。另外,您会看到,如果您在TFormStyleHook文件中明确设置了Project1.cpp,则现在可以正确使用它。

答案 3 :(得分:3)

对于那些希望为这种非常奇怪的行为找到真正聪明的解决方案的人,请看一下詹姆斯·约翰斯顿的答案。我已经将其应用到我的项目中,并且可以正常工作。以下是詹姆斯回答中的德尔福翻译。谢谢詹姆斯!

    services.AddHangfire(config =>
            config.UsePostgreSqlStorage(Configuration.GetConnectionString("Hangfire1ConnectionString"), new PostgreSqlStorageOptions {
                InvisibilityTimeout = TimeSpan.FromMinutes(720)

            }));

使用此代码,将尊重ClientWidth / ClientHeight尺寸,并且正确显示内部内容。 当然,Window的外部尺寸会更大以适应ClientWidth / ClientHeight尺寸,但这并不算太坏,因为通常窗口内容更为重要。

您可能希望将代码放在单独的单元中,以在任何项目中使用它。这只是直接的原始解决方案。

答案 4 :(得分:1)

该错误在Delphi Rio 10.3.3中仍然存在。我以为我通过使用Carlos Feitoza Filho的代码解决了这个问题。但是,当Windows Scaling处于打开状态(高DPI监视器)时,它不起作用。许多用户对此表示抱怨。

这是我一直有效的解决方案:使用FormResize事件!

procedure TForm1.FormResize(Sender: TObject);
begin
  ClientHeight := Button1.Top  + Button1.Height + Button1.Top; // whatever you want
  ClientWidth  := Button1.Left + Button1.Width  + Button1.Left; // whatever you want
end;

答案 5 :(得分:0)

这里没有XE2,但这听起来很熟悉。尝试将AutoScroll设置为True (奇怪的是,与this answer相反)以存储客户端表单大小,而不是边框​​大小。

答案 6 :(得分:0)

Delphi XE8中仍然存在此问题。一个简单的解决方法是使用以下代码,只需恢复设计时ClientWidth / ClientHeight,但需要注意一些警告(最重要的是,AutoScroll必须设置为False }):

type
  TFormHelper = class helper for Vcl.Forms.TCustomForm
  private
    procedure RestoreDesignClientSize;
  end;

procedure TfrmTestSize.FormCreate(Sender: TObject);
begin
  RestoreDesignClientSize;
end;

{ TFormHelper }

procedure TFormHelper.RestoreDesignClientSize;
begin
  if BorderStyle in [bsSingle, bsDialog] then
  begin
    if Self.FClientWidth > 0 then ClientWidth := Self.FClientWidth;
    if Self.FClientHeight > 0 then ClientHeight := Self.FClientHeight;
  end;
end;

给出以下设计时间表:

design time form

这将从以下位置更正运行时:

enter image description here

要:

enter image description here

我的博客上有更多细节和图片:http://marc.durdin.net/2015/07/fixing-the-incorrect-client-size-for-delphi-vcl-forms-that-use-styles/

答案 7 :(得分:0)

我在我的 C++Builder VCL 应用程序中解决了这个问题,方法是将以下代码添加到表单的 FormResize 函数中,无论所选样式如何,该函数似乎都可以工作并监视该程序正在最大化。我认为这段代码应该继续如果 Embarcadero 终于开始修复错误,那么工作正常:

|name   |employee_id|work_place         |sex_age       |skills_score    |depart_title                           |
+-------+-----------+-------------------+--------------+----------------+---------------------------------------+
|Michael|100        |[Montreal, Toronto]|[Male -> 30]  |[DB -> 80]      |[Product -> [Product, DeveloperLead]]  |
|Will   |101        |[Montreal]         |[Male -> 35]  |[Perl -> 85]    |[Product -> [Product, Lead,Test, Lead]]|
|Steven |102        |[New York]         |[Female -> 27]|[Python -> 80]  |[Test -> [Test, Lead,COE, Architect]]  |
|Lucy   |103        |[Vancouver]        |[Female -> 57]|[Sales -> 89,HR]|[Sales -> [Sales, Lead]]               |