wsMaximized表单似乎没有最大化

Ian*_*oyd 36 delphi vcl delphi-7 delphi-5

将表单设置为WindowState = wsMaximized有时会使表单最大化但不会:

在此输入图像描述

长期错误:这是我在2003年首次在Borland新闻组中提出的一个问题:

然后在2006年:

然后在2008年再次:

有人在2012年的Embarcadero论坛上提出这个问题:

现在是时候将18岁的bug移植到Stackoverflow了.也许某人终于想出了一个解决方法.

重现的步骤:

我的帖子包含了六种失败模式,但最简单的是:

  • 删除表单上的a LabelEdita:

    在此输入图像描述

  • 为以下内容添加OnEnter活动TEdit:

    procedure TForm1.Edit1Enter(Sender: TObject);
    begin
       Label1.Font.Style := Label1.Font.Style + [fsBold];
    end;
    
    Run Code Online (Sandbox Code Playgroud)
  • 并设置表格:

    • WindowStatewsMaximized
    • AutoScroll

而bazinga,失败了.

2008年帖子的另一组步骤之一:

  1. 创建一个新的应用程序和一个表单.
  2. 在设计时将表单设置为最大化(WindowState = wsMaximized).
  3. 删除窗体上的ListView控件
  4. 在OnShow期间,将20个空项添加到列表视图中:

    procedure TForm1.FormShow(Sender: TObject);
    var
         i: Integer;
    begin
         for i := 1 to 20 do
              ListView1.Items.Add;
    
    end;
    
    Run Code Online (Sandbox Code Playgroud)
  5. 在设计时将表单的AutoScroll属性设置为false(AutoScroll = False)

当然,我没有追求的是"在nRadStudio的版本中修复.只需使用它".我正在寻找一个实际的修复(如果有的话); 这可能包括在CodeGear最终修复它时引用VCL源的相关更改.(如果它甚至是固定的).

注意:PositionpoDesigned更改为其他任何内容都无法解决问题.

解决方法

我一直在使用的一个可怕,丑陋,可怕,恶心的解决方法是在计时期间启动计时器OnShow,然后当计时器触发时,最大化表单:

procedure TForm1.tmrVclMaximizeHackTimer(Sender: TObject);
begin
   Self.WindowState := wsMaximized;
end;
Run Code Online (Sandbox Code Playgroud)

我后来改进了这个黑客,发布了一条消息OnShow; 这与计时器消息基本相同,无需使用计时器:

const
  WM_MaximizeWindow = WM_APP + $03;

procedure TForm1.FormShow(Sender: TObject);
begin
  if (Self.WindowState = wsMaximized) then
  begin
     Self.WindowState := wsNormal;
     PostMessage(Self.Handle, WM_MaximizeWindow , 0, 0);
  end;
end;

private
   procedure WMMaximizeWindow(var Message: TMessage); message WM_MaximizeWindow;

procedure TForm1.WMMaximizeWindow(var Message: TMessage);
begin
   Self.WindowState := wsMaximized;
end;
Run Code Online (Sandbox Code Playgroud)

有时候我发明了OnAfterShowDelphi从未做过的事件:

const
  WM_AfterShow = WM_APP + $02;

procedure TForm1.FormShow(Sender: TObject);
begin
  PostMessage(Self.Handle, WM_AfterShow, 0, 0);
  if (Self.WindowState = wsMaximized) then
  begin
     Self.WindowState := wsNormal;
     FMaximizeNeeded := True;
  end;
end;

private
   procedure WMAfterShow(var Message: TMessage); message WM_AfterShow;

procedure TForm1.WMAfterShow(var Message: TMessage);
begin
   if FMaximizeNeeded then
   begin    
      FMaximizeNeeded := False;
      Self.WindowState := wsMaximized;
   end;
end;
Run Code Online (Sandbox Code Playgroud)

但没有黑客比黑客更好.

kob*_*bik 13

我可以用D7/Win7重现.

我根本不使用wsMaximized(你描述的类似随机问题).

解决方法:使用OnActivate- >ShowWindow(Handle, SW_MAXIMIZE)例如:

procedure TForm1.FormActivate(Sender: TObject);
begin
  // Maximize only once when the Form is first activated
  if not FMaxsimized then
  begin
    FMaxsimized := True;
    ShowWindow(Handle, SW_MAXIMIZE);
  end;
end;
Run Code Online (Sandbox Code Playgroud)

这种方法将无法在正常工作OnShow.

更好的解决方法:ShowWindowAsync在期间使用OnShowOnCreate例如:

procedure TForm1.FormCreate(Sender: TObject);
begin
  ShowWindowAsync(Handle, SW_MAXIMIZE);
end;
Run Code Online (Sandbox Code Playgroud)

这将设置窗口的显示状态,而不等待操作完成.

  • @Ian Boyd使用ShowWindowAsync的解决方法相当优雅,不是吗?当然比黑客更好. (5认同)

Ser*_*yuz 10

我只测试了第一个复制案例(使用D7,D2007,XE2),并且能够用D7和D2007复制问题,但不能用XE2复制.

正如我所看到的,问题是标签的字体发生了变化,要求其父级重新对齐.这最终会导致对SetWindowPos窗体(in TWinControl.AdjustSize)的调用,即使窗体已经最大化,也会恢复宽度/高度 - 这会导致坐在屏幕上的奇怪的,行为最大化但不是视觉上最大化的形式.


我跟踪了D2007和XE2中的代码,以便能够提出不同的内容.这TWinControl.AlignControls两个版本的代码不同.最后的陈述是特别重要的.

D2007:

procedure TWinControl.AlignControls(AControl: TControl; var Rect: TRect);

  ..
  { Apply any constraints }
  if Showing then AdjustSize;
end;
Run Code Online (Sandbox Code Playgroud)

XE2:

procedure TWinControl.AlignControls(AControl: TControl; var Rect: TRect);

  ..
    // Apply any constraints
    if FAutoSize and Showing then
      DoAdjustSize;
end;
Run Code Online (Sandbox Code Playgroud)

我希望这能以某种方式帮助您设计/决定使用哪种解决方法.



我可以建议的解决方法(虽然我没有彻底测试过)是强制显示最早的表单:

procedure TForm1.FormCreate(Sender: TObject);
var
  wplc: TWindowPlacement;
begin
  if not AutoScroll and (WindowState = wsMaximized) then begin
    wplc.length := SizeOf(wplc);
    GetWindowPlacement(Handle, @wplc);
    wplc.rcNormalPosition.Right := wplc.rcNormalPosition.Left + Width;
    wplc.rcNormalPosition.Bottom := wplc.rcNormalPosition.Top + Height;
    wplc.showCmd := SW_MAXIMIZE;
    SetWindowPlacement(Handle, @wplc);
  end;
end;
Run Code Online (Sandbox Code Playgroud)

上面的工作原理是因为它强制OnEnter在VCL设置表单的可见标志之前将焦点设置为编辑控件(事件).反过来,标签的对齐请求不会导致表单大小调整.此外,由于在VCL调用时ShowWindow窗体的窗口已经可见,因此不会导致窗体在任何阶段以恢复状态显示.

但是,我不知道它是否有助于不同的复制方案.


最后,虽然我可以看到在新的Delphi版本中纠正了这种行为,但我不认为这是VCL中的一个错误.在我看来,当窗口显示状态正在改变时,用户代码应该负责不导致窗口调整.我将针对特定场景采取的行动方案是推迟修改标签的字体,直到VCL完成显示表单.

  • +1!我更喜欢您使用“SetWindowPlacement”的解决方案,因为表单立即显示为最大化状态(与我的解决方案相反,在我的解决方案中,窗口在最大化之前显示为正常)。此外,无论是否预设“wsMaximized”,您的方法都有效。(我个人不会使用“wsMaximized”,省略“if not AutoScroll”检查,并使用“SetWindowPlacement”最大化)。另外,分析得很好:)。 (2认同)

gor*_*rdy 1

我不认为这是 Delphi 中的错误,而是 Windows CreateWindow 函数中的错误(或只是奇怪的行为)。如果您搜索 CreateWindow 和 WS_MAXIMIZE 不起作用,您会发现类似的非常旧的线程和讨论,这些线程和讨论来自调用 CreateWindow 或 CreateWindowEx 的人,在样式参数中传递 WS_MAXIMIZE ,但在运行应用程序时没有看到最大化的窗口。

摘自旧的 gamedev.net 线程

问题是,当使用 WS_OVERLAPPEDWINDOW 时,WS_MAXIMIZE 显然不适用。如果将 WS_OVERLAPPEDWINDOW 替换为 WS_POPUP,您将获得最大化的窗口。当然,这可能不适用于所有版本的 Windows,甚至不适用于所有版本的 Windows shell UI。

WS_OVERLAPPEDWINDOW 是 MS 的旧默认窗口“类型”,他们显然对 CreateWindow/Ex 进行了编码以忽略某些样式,认为 ShowWindow 将使用 SW_SHOWDEFAULT 调用,这会导致窗口根据 CreateProcess 启动信息参数显示。这最终使用户可以通过使用 shell 的快捷方式设置来控制应用程序主窗口的显示方式。

解决方法是调用 ShowWindow。它也应该在 Delphi 中工作:

procedure TForm1.FormShow(Sender: TObject);
begin
   ShowWindow(Handle, SW_MAXIMIZE);
end;
Run Code Online (Sandbox Code Playgroud)