如何使悬停在最小化、最大化和关闭按钮上的行为正常?
How to make hovering over Minimize, Maximize, and Close buttons behave?
在 Delphi 应用程序中,当您将鼠标悬停在边框图标上时,例如:
- 最小化
- 最大化
- 恢复
它的行为不正确:
与行为正确的应用程序比较:
复制步骤
- 单击 文件、新建、VCL 表单应用程序 - Delphi
- 单击运行 (F9)
- 将鼠标悬停在最小化、最大化或关闭按钮上。
如何修复?
- Windows 10,64 位(运行 本机在桌面 PC 上)
- Delphi XE6
Edit - 它也因 Delphi 7:
而失败
并在 Delphi 5:
并在 Delphi 4:
我假设(即害怕)它是由 ThemeServices 引擎引起的;他们可能认为不尊重用户的偏好很酷。但看起来它是更基本的东西。
兼容模式
- none:失败
- Windows 8: 失败
- Windows 7: 失败
- Windows Vista (Service Pack 2): 失败
- Windows Vista (Service Pack 2): 失败
- Windows Vista:失败
- Windows XP (Service Pack 3)(non-client 禁用区域主题):有效
- Windows XP (Service Pack 2)(non-client 禁用区域主题):有效
- Windows 98 / Windows Me(non-client 禁用区域主题):作品
- Windows 95(non-client 禁用区域主题):有效
Skype
在 Skype 中也失败;也写在Delphi:
高 DPI 是诱因
我终于弄明白为什么它在我用过的每 Windows 10 台机器上都失败了;但不适合所有人。高 dpi.
将您的 dpi 设置为 97 (101%) 或更高。
足够接近
Dalija 的解决方案有效:
我们将忽略工具提示的问题,改天再战。
还应注意,Windows 10 会提示您可能需要注销并重新登录才能使某些应用程序在更改 DPI 后正常工作。 Delphi.
确实如此
还需要注意的是,Delphi 不能容忍 DPI 像这样在背后改变。这包括调整 缩放 滑块。这还包括将应用程序放置在除主显示器之外的任何显示器上。
而且我们从来没有弄清楚问题是什么;只为用户 运行 多个显示器踢了它。
QC 错误报告
因为 Bor...Impr...CodeG...Embarca... Idera 的 QC 站点在 pay-wall 后面,这里是 bug report:
如您所见:没人关心。
高 DPI 是导火索,也是解决之道。
出现问题的应用程序不支持高 DPI。悬停问题的解决方法是使用1、2或3下的解决方案之一让他们知道或打开关联的兼容模式。
注意:当启用高 DPI 感知时,应用程序的其余部分是否会正常运行是另一个问题,并且会因应用程序而异。
兼容模式下检查"Disable display scaling on high DPI settings"
调用 SetProcessDPIAware
作为 .dpr
文件中的第一个调用 - 如 Ian Boyd 所述,调用此函数可能导致竞争条件,首选方法是使用清单。 SetProcessDPIAware
使用具有 true
或 true/PM
设置的自定义清单("Enable runtime themes" 中包含的默认 Delphi 清单不支持高 DPI)
当前版本的 Delphi VCL 和 FMX 框架缺乏对每个监视器 DPI 感知的支持,因此仅当您自己处理每个监视器 DPI 时才使用 true/PM
清单。向 QP 报告为 VCL and FireMonkey lack Per-Monitor DPI support for Windows 8.1 (and Windows 10)
<asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>true</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
或
<asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>true/PM</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
更新:
Delphi VCL 是错误行为的根源,具体问题出在 TForm
class 或其祖先中的某处。当直接使用 Windows API 时,结果 windows 表现正常。
Windows API 行为正常的代码:
MessageBox(0, 'Correct', 'Caption', MB_OK);
ShowMessage('Correct'); // if themes are enabled -> Windows Task dialog is used
完整的 Delphi 示例应用程序,它在不使用 VCL 的情况下创建主要 window - 行为正常
program win;
{$R *.res}
uses
Windows,
Messages,
SysUtils;
var
Msg: TMSG;
LWndClass: TWndClass;
hMainHandle: HWND;
function WindowProc(HWND, Msg: Longint; wParam: wParam; lParam: lParam): Longint; stdcall;
begin
if Msg = WM_DESTROY then PostQuitMessage(0);
Result := DefWindowProc(HWND, Msg, wParam, lParam);
end;
begin
LWndClass.hInstance := hInstance;
with LWndClass do
begin
lpszClassName := 'WinApiWnd';
Style := CS_PARENTDC or CS_BYTEALIGNCLIENT;
hIcon := LoadIcon(hInstance, 'MAINICON');
lpfnWndProc := @WindowProc;
hbrBackground := COLOR_BTNFACE + 1;
hCursor := LoadCursor(0, IDC_ARROW);
end;
RegisterClass(LWndClass);
hMainHandle := CreateWindow(LWndClass.lpszClassName, 'Window Title', WS_CAPTION or WS_MINIMIZEBOX or WS_SYSMENU or WS_VISIBLE, 0, 0, 360, 200, 0, 0, hInstance, nil);
while GetMessage(Msg, 0, 0, 0) do
begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
end.
行为不当的 VCL 表单:
var
f: TForm;
f := CreateMessageDialog('Broken', mtWarning, mbOKCancel, mbOk);
f.ShowModal;
f.Free;
f := TForm.Create(nil);
f.ShowModal;
f.Free;
在 Delphi 应用程序中,当您将鼠标悬停在边框图标上时,例如:
- 最小化
- 最大化
- 恢复
它的行为不正确:
与行为正确的应用程序比较:
复制步骤
- 单击 文件、新建、VCL 表单应用程序 - Delphi
- 单击运行 (F9)
- 将鼠标悬停在最小化、最大化或关闭按钮上。
如何修复?
- Windows 10,64 位(运行 本机在桌面 PC 上)
- Delphi XE6
Edit - 它也因 Delphi 7:
而失败并在 Delphi 5:
并在 Delphi 4:
我假设(即害怕)它是由 ThemeServices 引擎引起的;他们可能认为不尊重用户的偏好很酷。但看起来它是更基本的东西。
兼容模式
- none:失败
- Windows 8: 失败
- Windows 7: 失败
- Windows Vista (Service Pack 2): 失败
- Windows Vista (Service Pack 2): 失败
- Windows Vista:失败
- Windows XP (Service Pack 3)(non-client 禁用区域主题):有效
- Windows XP (Service Pack 2)(non-client 禁用区域主题):有效
- Windows 98 / Windows Me(non-client 禁用区域主题):作品
- Windows 95(non-client 禁用区域主题):有效
Skype
在 Skype 中也失败;也写在Delphi:
高 DPI 是诱因
我终于弄明白为什么它在我用过的每 Windows 10 台机器上都失败了;但不适合所有人。高 dpi.
将您的 dpi 设置为 97 (101%) 或更高。
足够接近
Dalija 的解决方案有效:
我们将忽略工具提示的问题,改天再战。
还应注意,Windows 10 会提示您可能需要注销并重新登录才能使某些应用程序在更改 DPI 后正常工作。 Delphi.
确实如此还需要注意的是,Delphi 不能容忍 DPI 像这样在背后改变。这包括调整 缩放 滑块。这还包括将应用程序放置在除主显示器之外的任何显示器上。
而且我们从来没有弄清楚问题是什么;只为用户 运行 多个显示器踢了它。
QC 错误报告
因为 Bor...Impr...CodeG...Embarca... Idera 的 QC 站点在 pay-wall 后面,这里是 bug report:
如您所见:没人关心。
高 DPI 是导火索,也是解决之道。
出现问题的应用程序不支持高 DPI。悬停问题的解决方法是使用1、2或3下的解决方案之一让他们知道或打开关联的兼容模式。
注意:当启用高 DPI 感知时,应用程序的其余部分是否会正常运行是另一个问题,并且会因应用程序而异。
兼容模式下检查"Disable display scaling on high DPI settings"
调用
SetProcessDPIAware
作为.dpr
文件中的第一个调用 - 如 Ian Boyd 所述,调用此函数可能导致竞争条件,首选方法是使用清单。 SetProcessDPIAware使用具有
true
或true/PM
设置的自定义清单("Enable runtime themes" 中包含的默认 Delphi 清单不支持高 DPI)
当前版本的 Delphi VCL 和 FMX 框架缺乏对每个监视器 DPI 感知的支持,因此仅当您自己处理每个监视器 DPI 时才使用 true/PM
清单。向 QP 报告为 VCL and FireMonkey lack Per-Monitor DPI support for Windows 8.1 (and Windows 10)
<asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>true</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
或
<asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>true/PM</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
更新:
Delphi VCL 是错误行为的根源,具体问题出在 TForm
class 或其祖先中的某处。当直接使用 Windows API 时,结果 windows 表现正常。
Windows API 行为正常的代码:
MessageBox(0, 'Correct', 'Caption', MB_OK);
ShowMessage('Correct'); // if themes are enabled -> Windows Task dialog is used
完整的 Delphi 示例应用程序,它在不使用 VCL 的情况下创建主要 window - 行为正常
program win;
{$R *.res}
uses
Windows,
Messages,
SysUtils;
var
Msg: TMSG;
LWndClass: TWndClass;
hMainHandle: HWND;
function WindowProc(HWND, Msg: Longint; wParam: wParam; lParam: lParam): Longint; stdcall;
begin
if Msg = WM_DESTROY then PostQuitMessage(0);
Result := DefWindowProc(HWND, Msg, wParam, lParam);
end;
begin
LWndClass.hInstance := hInstance;
with LWndClass do
begin
lpszClassName := 'WinApiWnd';
Style := CS_PARENTDC or CS_BYTEALIGNCLIENT;
hIcon := LoadIcon(hInstance, 'MAINICON');
lpfnWndProc := @WindowProc;
hbrBackground := COLOR_BTNFACE + 1;
hCursor := LoadCursor(0, IDC_ARROW);
end;
RegisterClass(LWndClass);
hMainHandle := CreateWindow(LWndClass.lpszClassName, 'Window Title', WS_CAPTION or WS_MINIMIZEBOX or WS_SYSMENU or WS_VISIBLE, 0, 0, 360, 200, 0, 0, hInstance, nil);
while GetMessage(Msg, 0, 0, 0) do
begin
TranslateMessage(Msg);
DispatchMessage(Msg);
end;
end.
行为不当的 VCL 表单:
var
f: TForm;
f := CreateMessageDialog('Broken', mtWarning, mbOKCancel, mbOk);
f.ShowModal;
f.Free;
f := TForm.Create(nil);
f.ShowModal;
f.Free;