wsMaximized表单似乎没有最大化

时间:2013-10-20 17:15:05

标签: delphi delphi-7 vcl delphi-5

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

enter image description here

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

然后又在2006年:

然后在2008年再次:

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

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

重现的步骤

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

  • 在表单上放置LabelEdit

    enter image description here

  • OnEnter添加TEdit个事件:

    procedure TForm1.Edit1Enter(Sender: TObject);
    begin
       Label1.Font.Style := Label1.Font.Style + [fsBold];
    end;
    
  • 并设置表单:

    • WindowState wsMaximized
    • AutoScroll错误

而bazinga,失败了。

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

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

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

  10.   

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

注意:Position poDesigned 更改为其他任何内容并不能解决问题。

解决方法

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

procedure TForm1.tmrVclMaximizeHackTimer(Sender: TObject);
begin
   Self.WindowState := wsMaximized;
end;

我后来改进了这个hack,以便在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;

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

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;

但没有黑客比黑客更好。

5 个答案:

答案 0 :(得分:12)

我可以用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;

此方法 OnShow期间无法正常工作。

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

procedure TForm1.FormCreate(Sender: TObject);
begin
  ShowWindowAsync(Handle, SW_MAXIMIZE);
end;

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

答案 1 :(得分:10)

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

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


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

D2007:

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

  ..
  { Apply any constraints }
  if Showing then AdjustSize;
end;

XE2:

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

  ..
    // Apply any constraints
    if FAutoSize and Showing then
      DoAdjustSize;
end;

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

<小时/>
我可以建议的解决方法(尽管我还没有彻底测试过)是强制显示早期最大化的形式:

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;

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

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


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

答案 2 :(得分:1)

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

Excerpt from an old gamedev.net thread

  

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

     

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

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

procedure TForm1.FormShow(Sender: TObject);
begin
   ShowWindow(Handle, SW_MAXIMIZE);
end;

答案 3 :(得分:0)

希望我使用的解决方案有助于其他人(我知道窗口首先显示设计时间大小):

  • 添加一个间隔小于1的计时器(不要放0)。
  • 代码:theTimer.Enabled:=False;WindowState:=wsMaximized;

对我来说永远不会失败。

只要显示表单并完成此类节目的所有待处理任务,定时器触发并且窗口最大化。

前段时间,我正在使用鼠标单击鼠标点击最大化按钮,但我发现检查Windows操作系统版本,插件(在Linux上)等使事情变得如此困难。那个工作就好像用户要求最大化窗口一样。

我现在使用的Timer完全相同,但避免操作系统检查等。

更不用说:在DesignTime上将WindowState放到wsNormal上(不要将它设置为wsMinimized,也不要设置为wsMaximized)。

答案 4 :(得分:0)

哇!我没看到帖子:

  

ShowWindowAsync(把手,SW_MAXIMIZE);

谢谢你,用它来解决我的问题比使用Timer更好,但不完美,它仍然会导致一点闪烁(窗口显示在不完整的渲染上,然后它进入最大化状态),它好多了比计时器更少的时间显示在非最大化状态。

它与OnShow方法上的先前SetBounds()兼容。

我希望:在显示之前设置表单的初始大小(宽度,高度)以及初始位置(左侧,顶部),但必须最大化显示该表单;这种位置和大小适用于用户未最大化窗口的时间。

ShowWindowAsync适用于这样的目标,并且不需要添加一个丑陋的计时器(在其代码中将interval = 1和.Enabled = False作为第一句)。

当我阅读帖子时,我怎能错过它?

所以,现在我将使用(就像示例os初始大小相对于监视器):

procedure TtheForm.FormShow(Sender: TObject);
var
   theInitialDefaultWidth,theInitialDefaultHeight:Integer;
begin
     theInitialDefaultWidth:=Round(Screen.Width*3/5);
     theInitialDefaultHeight:=Round(Screen.Height*3/5);
     WindowState:=wsNormal; // So it can still have at design time wsMaximized, this is for the SetBounds to work on a non maximized state
     SetBounds((Screen.Width-theInitialDefaultWidth)div 2,(Screen.Height-theInitialDefaultHeight)div 2,theInitialDefaultWidth,theInitialDefaultHeight); // Set default position and default size as i wish
     ShowWindowAsync(Handle,SW_MAXIMIZE); // Make the window to be shown maximized when it will be visible
     // ... // Rest of actions for the FormShow method
end;

完美的作品!我不需要触摸设计时属性,我可以让它们原样(WindowState = wsMaximized,Position = poScreenCenter等)。问题的100%代码解决方案。

非常感谢!

P.D。:它能在Linux上运行吗?我的意思是当代码编译为Linux(在Lazarus中)时,我必须对它进行测试并看看,如果它确实有效,它将对我迄今使用的内容产生很大的影响。