在Windows 7 Pro 64位系统上使用Delphi XE7。 如果我选择炭黑色板岩' VCL风格,从32x32程序图标缩小的16x16像素标题栏图标看起来不像预期的那样。
它应该看起来像下面的小图标。如果我以16x16像素格式加载程序图标,它在标题栏中看起来很好,但由于16到32像素的放大,在任务栏中很难看。
答案 0 :(得分:6)
这是VCL样式http://qc.embarcadero.com/wc/qcmain.aspx?d=106224
的已知问题在Embarcadero的新QC网站上也看到这个问题:https://quality.embarcadero.com/browse/RSP-11572 ---自最初报道以来已经过了3年,但仍然没有修复。如果有足够的人投票支持这个问题,也许会得到一些关注。
作为解决方法,您可以将正确的16x16图标加载到表单的Icon
属性中。
为了实现这一点,您还必须在Application.MainFormOnTaskBar := false;
文件中设置.dpr
然而,这会产生一些其他不良影响,因为它会禁用Windows Vista或Windows 7 Aero效果,包括实时任务栏缩略图,动态Windows,Windows翻转和Windows Flip 3D。请参阅:MainFormOnTaskBar
在任何情况下都不要更改应用程序图标大小,因为这是最糟糕的解决方案。
答案 1 :(得分:5)
我终于找到了这个问题的底部,并弄清楚为什么没有VCL样式就能解决这个问题,并且不适用于VCL样式。
前言:多年来,VCL一直不支持具有多种图标大小的图标图形的概念:TIcon
始终被认为是一个单独的图形 - 而不是一组图形各种尺寸和分辨率。这仍然是正确的,在VCL中可能不容易纠正设计问题。
VCL将通过WM_SETICON
消息设置表单图标。 VCL始终将wParam
设置为ICON_BIG
:对VCL来源的检查表明,在设置图标时,它永远不会使用ICON_SMALL
。此外,创建窗口类时,hIcon
结构的hIconSm
和WNDCLASSEX
成员变量始终为NULL
。因此,很明显,VCL甚至都没有试图设置一个小图标。通常情况下,如果应用程序从不设置小图标,Windows会将大图标调整为小尺寸,这非常难看。但是,该规则有一个重要的例外。
请注意,Windows资源文件的ICON
资源实际上会存储所谓的图标组,这是一组来自原始图标的单个图标图像{ {1}}文件。 LoadIcon
API声明只会加载大型32x32图标。然而,这实际上并不严格。似乎Windows本身在.ico
和原始资源之间保持链接,因此如果需要其他大小的图标,Windows可以根据需要加载它们。
这个事实没有详细记录,但MSDN中有一个地方陈述了这一事实:WNDCLASSEX
structure,HICON
变量:
与窗口类关联的小图标的句柄。如果此成员为NULL,系统将搜索hIcon成员指定的图标资源,以获取适当大小的图标,以用作小图标。
因此,即使VCL不能通过公共hIconSm
类正确支持小图标(例如,通过在设计时从属性编辑器中分配它),仍然可以使用一个正常工作。这两种方法:
保持TForm.Icon
属性未设置(无图标)。在这种情况下,表单将从TForm.Icon
获取图标。默认值来自应用程序的TApplication.Icon
资源。来自MAINICON
:
TApplication.Create
如果您不想使用应用程序默认图标,则可以在运行时加载不同的图标资源;在C ++中:
FIcon := TIcon.Create;
FIcon.Handle := LoadIcon(MainInstance, 'MAINICON');
因此,VCL为小图标提供基本支持,因为它支持从资源加载图标,Windows支持从资源加载的大图标中加载小图标。
问题: VCL样式显然不依赖于非客户区域的Windows默认呈现行为,例如标题栏。相反,它处理所有渲染本身。渲染中的一个任务是VCL样式必须确定要渲染的图标。事实证明,即使主要的VCL表格类不支持小图标 - VCL样式钩子也可以!好吧,有点。这发生在myForm->Icon->LoadFromResourceName(FindHInstance(...), L"OtherResource");
:
TFormStyleHook.GetIcon
问题在于,钩子使用TmpHandle := THandle(SendMessage(Handle, WM_GETICON, ICON_SMALL, 0));
if TmpHandle = 0 then
TmpHandle := THandle(SendMessage(Handle, WM_GETICON, ICON_BIG, 0));
而不是ICON_SMALL
来调用WM_GETICON
。来自MSDN:
ICON_SMALL2
:检索窗口的小图标。
ICON_SMALL
,因为VCL没有设置小图标。NULL
:检索应用程序提供的小图标。 如果应用程序没有提供,则系统会使用该窗口的系统生成图标。 (强调我的)
ICON_SMALL2
:问题是系统如何生成图标?我的实验表明,不管怎样,你都会得到一个小图标:
修复:每当调用HICON
时,VCL都需要使用ICON_SMALL2
而不是ICON_SMALL
。 (注意WM_GETICON
中的类似代码也需要修复。)由于这是一个非常简单的修复,我希望Embarcadero尽快应用它。
解决方法:在修复VCL之前,您必须创建自己的派生表单挂钩。不幸的是,TFormStyleHook.TMainMenuBarStyleHook.GetIcon
是私有的,而且很难实现。因此,我们尝试了一种不同的技术:当TFormStyleHook.GetIcon
为WM_GETICON
时改变wParam
的消息处理行为,使其变为ICON_SMALL
。
ICON_SMALL2
答案 2 :(得分:1)
@James Johnston,
谢谢!虽然在发布的代码中有一个小故障,但它对我来说很好用:
Message.Result = SendMessage(this->Control->Handle, WM_GETICON, ICON_SMALL2, Message.LParam);
PreventRecursion = false;
this->Handled = true;
删除PreventRecursion = false;
以下是代码的Delphi端口:
unit FixIconHook;
interface
uses
WinAPI.Windows,
WinAPI.Messages,
Vcl.Graphics,
Vcl.Controls,
Vcl.Forms,
Vcl.Themes;
type
TFixedFormStyleHook = class(TFormStyleHook)
private
PreventRecursion: Boolean;
strict protected
procedure WndProc(var Message: TMessage); override;
public
constructor Create(AControl: TWinControl); override;
end;
implementation
constructor TFixedFormStyleHook.Create(AControl: TWinControl);
begin
inherited Create(AControl);
PreventRecursion := False;
end;
procedure TFixedFormStyleHook.WndProc(var Message: TMessage);
begin
if (Message.Msg = WM_GETICON) and (Message.WParam = ICON_SMALL) and (not PreventRecursion) and (Assigned(Control) and Control.HandleAllocated) then
begin
// Just in case some future Windows version decides to call us again
// with ICON_SMALL as response to being called with ICON_SMALL2.
PreventRecursion := true;
Message.Result := SendMessage(Control.Handle, WM_GETICON, ICON_SMALL2, Message.LParam);
Handled := true;
exit;
end;
inherited WndProc(Message);
end;
end.
然后在你的DPR项目中:
TStyleManager.Engine.RegisterStyleHook(TForm, TFixedFormStyleHook);
TStyleManager.Engine.RegisterStyleHook(TCustomForm, TFixedFormStyleHook);