使用VCL样式

时间:2015-11-12 13:02:23

标签: delphi icons vcl-styles

在Windows 7 Pro 64位系统上使用Delphi XE7。 如果我选择炭黑色板岩' VCL风格,从32x32程序图标缩小的16x16像素标题栏图标看起来不像预期的那样。 enter image description here

它应该看起来像下面的小图标。如果我以16x16像素格式加载程序图标,它在标题栏中看起来很好,但由于16到32像素的放大,在任务栏中很难看。

3 个答案:

答案 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结构的hIconSmWNDCLASSEX成员变量始终为NULL。因此,很明显,VCL甚至都没有试图设置一个小图标。通常情况下,如果应用程序从不设置小图标,Windows会将大图标调整为小尺寸,这非常难看。但是,该规则有一个重要的例外。

请注意,Windows资源文件的ICON资源实际上会存储所谓的图标组,这是一组来自原始图标的单个图标图像{ {1}}文件。 LoadIcon API声明只会加载大型32x32图标。然而,这实际上并不严格。似乎Windows本身在.ico和原始资源之间保持链接,因此如果需要其他大小的图标,Windows可以根据需要加载它们。

这个事实没有详细记录,但MSDN中有一个地方陈述了这一事实:WNDCLASSEX structureHICON变量:

  

与窗口类关联的小图标的句柄。如果此成员为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:问题是系统如何生成图标?我的实验表明,不管怎样,你都会得到一个小图标:
    • 如果大图标链接到具有适当图标大小的基础Windows资源,则返回该图标。
    • 否则,系统将调整另一个图标的大小以适合所需的小图标尺寸,然后返回。

修复:每当调用HICON时,VCL都需要使用ICON_SMALL2而不是ICON_SMALL。 (注意WM_GETICON中的类似代码也需要修复。)由于这是一个非常简单的修复,我希望Embarcadero尽快应用它。

解决方法:在修复VCL之前,您必须创建自己的派生表单挂钩。不幸的是,TFormStyleHook.TMainMenuBarStyleHook.GetIcon是私有的,而且很难实现。因此,我们尝试了一种不同的技术:当TFormStyleHook.GetIconWM_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);