【问题标题】:Wrong window position values when program is being closed while minimized程序在最小化时关闭时错误的窗口位置值
【发布时间】:2015-07-21 10:07:43
【问题描述】:

在我的 Windows 7 桌面上,我将 Windows 任务栏附加在屏幕左侧(而不是默认设置在底部),并在屏幕顶​​部附加了一个自定义桌面工具栏(“True Launch Bar”)。

在一个 Delphi XE8 VCL 项目中,我将主窗口位置值(formMain.TopformMain.Left)保存在 FormClose 事件中,然后在程序启动时,我使用这些值在 @ 中恢复主窗口位置987654326@事件。

这通常运作良好。 然而,当程序在 MINIMIZED (formMain.WindowState = wsMinimized) 时关闭时,表单位置值是错误的(即被工具栏的宽度/高度减少),因此窗口被恢复在程序开始时位置错误。

那么我该如何解决这个问题呢?

编辑:我试过大卫提到的other solution

var
  WindowPlacement: TWindowPlacement;
  R: TRect;
....
  WindowPlacement.Length := SizeOf(WindowPlacement);
  Win32Check(GetWindowPlacement(formMain.Handle, @WindowPlacement));
  R := WindowPlacement.rcNormalPosition;
  CodeSite.Send('formMainLeft by WinAPI', R.Left);        // normal: 323 minimized: 323
  CodeSite.Send('VCL formMain.Left',      formMain.Left); // normal: 423 minimized: 323

但是,这会产生同样的问题,因为它没有考虑工具栏占用的空间,因为它只获取 WORK AREA 值。

此外,这不是大卫提到的重复问题,而是类似的问题。这个问题源于最小化状态下的错误屏幕值,而另一个问题是关于整体恢复状态和大小。

另请注意:我不想恢复窗口状态(例如最小化)AND 位置,但只恢复窗口位置,所以我不能使用 David 在另一个问题中提到的 SetWindowPlacement

EDIT2:我现在已经用这段代码解决了这个问题:

  if formMain.WindowState = wsMinimized then
  begin
    MinimizedOffsetTop  := Screen.WorkAreaTop;
    MinimizedOffsetLeft := Screen.WorkAreaLeft;
  end
  else if formMain.WindowState = wsNormal then
  begin
    MinimizedOffsetTop  := 0;
    MinimizedOffsetLeft := 0;
  end;
  SettingsIni.WriteInteger('Persistence', 'Top',  formMain.Top  + MinimizedOffsetTop);
  SettingsIni.WriteInteger('Persistence', 'Left', formMain.Left + MinimizedOffsetLeft);

(在窗口最大化的情况下,我不保存和恢复窗口位置,只保存和恢复最大化窗口状态)。

【问题讨论】:

  • 您可以使用 JediVCL 库,其中有 TJvFormPlacementTJvFormStorage 组件可以自动保存和自动恢复
  • 虽然 JEDI 无疑为此提供了有用的类,但包括 JEDI 通常会强制您包含大量依赖项。在顶部到达两个 Win32 API。
  • 我不想为此使用 JEDI,因为我想完全控制自己的保存和恢复。
  • @kobik GetWindowRect 在窗口最小化时得到错误的值。例如:R.Left 则变为 -32000!
  • 不要使用 screen.workareatop/left,您的表单可能不在与主监视器具有相同工作区偏移量的监视器上。见stackoverflow.com/questions/29747238/…

标签: delphi desktop-application delphi-xe8 window-position


【解决方案1】:

当一个窗口被最小化时,它会记住最后的标准化边界,并在您恢复窗口时将自己恢复到那里。这些界限可通过GetWindowPlacement 提供给您。此 API 返回工作区相对坐标。这样做是因为这允许窗口最小化并恢复到相同的工作区相对位置,即使工作区在其间发生了变化。

当您要求最小化窗口的LeftTop 时,显然VCL 调用GetWindowPlacement。否则它将如何获得它返回的坐标?当然,它会返回让您感到困惑的工作区相对坐标。人们可能会认为这些属性有时与屏幕相关而有时与工作区域相关是一个错误。

您的解决方案很明显。调用GetWindowPlacement获取工作区相对坐标。当您需要重新应用这些坐标时,请致电SetWindowPlacement

您说您不能使用SetWindowPlacement,因为这会强制窗口最小化。但事实并非如此。将showCmd 成员设置为SW_SHOWNORMALSW_RESTORE

多年来,我们一直在使用这些 API 来存储和恢复窗口位置。众所周知,它们运作良好。

【讨论】:

  • 大卫,如果这很容易实现,为什么不提供一个示例代码来演示这三个步骤:1. GetWindowPlacement,2. 将 WindowPlacement 保存到 INI 文件,3. 恢复WindowPlacement 灵活,例如当在第一步最小化时,在第二台显示器上的同一位置恢复窗口,并且在恢复时应该是正常的。谢谢!
  • 该评论消除了我做更多事情的动力。无论如何,您已经有了一个满意的解决方案。我为其他读者写了这个答案。
  • 不,我对此不满意,因为当窗口在第二台显示器(没有左侧工具栏)上最小化时,它没有正确恢复。所以我正在寻找另一种解决方案。但是您没有用示例代码证明您的断言。请这样做。
  • 如果您对它不满意,请删除它。无论如何,您已经展示了非常好的 GWP 代码,取自我怀疑的一个答案。以同样的方式调用 SWP,修改showCmd,正如我在答案中所说的那样。你可以这样做。您已经调用了 SWP。只需将其更改为 showCmd
  • @David 表单的窗口将在 VCL 调用 ShowWindow 之前显示。这将改变 VCL 方案中的某些执行顺序,例如在表单变得“可见”之前运行的对齐代码,或者像我之前的示例一样。这可能需要也可能不需要代码更改。我不是反对,只是警告......
猜你喜欢
  • 1970-01-01
  • 2015-07-21
  • 2011-06-17
  • 1970-01-01
  • 2013-10-09
  • 2016-07-30
  • 1970-01-01
  • 2018-08-24
  • 1970-01-01
相关资源
最近更新 更多