Delphi 11.1窗体偶尔无法正常显示在屏幕右下角问题求助
问题描述
在Delphi 11.1的Windows应用中,点击按钮启动5分钟定时器,触发时将Form3显示在屏幕右下角,多数情况正常,但偶尔会显示在屏幕左上角(此时Screen.Height=1080、Screen.Width=1920数值正确)。应用采用Per Monitor v2清单,单台1920×1080分辨率显示器。
相关代码:
procedure TForm1.Timer2Timer(Sender: TObject); begin Timer2.Enabled := False; // Set form position on bottom right of the screen with Form3 do begin Top := Screen.Height - Form3.Height - GetTaskBar_Y_Bottom_Height(); Left := Screen.Width - Form3.Width - GetTaskBar_X_Right_Width(); end; // Now show the form Form3.Show; end; procedure TForm1.Button1Click(Sender: TObject); begin Timer2.Enabled := True; end;
排查建议
检查自定义任务栏尺寸函数的返回值:
GetTaskBar_Y_Bottom_Height()和GetTaskBar_X_Right_Width()这两个函数可能在某些场景下返回异常值(比如负数或超大值),直接导致计算后的Top/Left超出合理范围,Windows会自动把窗口重置到左上角。建议在赋值前加日志输出,记录这两个函数的返回值和最终计算出的Top、Left,抓异常发生时的具体数据:var TaskBarBottomHeight, TaskBarRightWidth: Integer; CalcTop, CalcLeft: Integer; begin Timer2.Enabled := False; TaskBarBottomHeight := GetTaskBar_Y_Bottom_Height(); TaskBarRightWidth := GetTaskBar_X_Right_Width(); CalcTop := Screen.Height - Form3.Height - TaskBarBottomHeight; CalcLeft := Screen.Width - Form3.Width - TaskBarRightWidth; // 输出到调试窗口或日志文件 OutputDebugString(PChar(Format('TaskBarBottomHeight: %d, TaskBarRightWidth: %d, CalcTop: %d, CalcLeft: %d', [TaskBarBottomHeight, TaskBarRightWidth, CalcTop, CalcLeft]))); with Form3 do begin Top := CalcTop; Left := CalcLeft; end; Form3.Show; end;确认Form3的Position属性:
如果Form3的Position属性不是poDesigned或poManual,Windows可能自动调整窗口位置。比如设为poScreenCenter时,就算手动赋值Top/Left,也可能被系统覆盖。要确保Form3的Position属性在设计时或代码里设为poManual,避免系统干预。排查Per Monitor v2的DPI适配问题:
启用Per Monitor v2后,窗口的Height/Width是缩放后的像素值,而Screen.Height/Screen.Width是物理分辨率,两者可能不匹配。建议用GetWindowRect获取Form3的实际像素尺寸来计算:var WindowRect: TRect; FormActualWidth, FormActualHeight: Integer; begin GetWindowRect(Form3.Handle, WindowRect); FormActualWidth := WindowRect.Right - WindowRect.Left; FormActualHeight := WindowRect.Bottom - WindowRect.Top; // 用实际尺寸计算定位 CalcTop := Screen.Height - FormActualHeight - TaskBarBottomHeight; CalcLeft := Screen.Width - FormActualWidth - TaskBarRightWidth; end;同时确保Form3的
Scaled属性设为True,DPI变化时能正确调整尺寸。检查线程竞态问题:
VCL定时器默认在主线程触发,但如果有其他线程操作Form3的属性(比如修改Visible、Position),可能导致竞态条件,让定位代码失效。排查是否有其他线程碰过Form3,所有UI操作必须在主线程执行。改用SetWindowPos控制窗口显示:
如果Form3显示前已经处于可见状态,Show方法可能不会更新位置。建议用SetWindowPos直接设置位置并显示,替代手动赋值Top/Left后调用Show:SetWindowPos(Form3.Handle, HWND_TOPMOST, CalcLeft, CalcTop, 0, 0, SWP_NOSIZE or SWP_SHOWWINDOW);这个API能更可靠地控制窗口的位置和显示状态。
考虑系统临时状态变化:
定时器触发时,系统可能正处于DPI临时调整、任务栏状态变化(比如用户临时隐藏又恢复)等场景。可以在计算位置前重新获取任务栏状态,确保GetTaskBar_Y_Bottom_Height()和GetTaskBar_X_Right_Width()拿到的是最新数据。
内容的提问来源于stack exchange,提问作者user2972081

