使用VCL样式时,程序图标在标题栏中看起来很奇怪

Nob*_*bby 7 delphi icons vcl-styles

在Windows 7 Pro 64位系统上使用Delphi XE7.如果我选择"Charcoal Dark Slate"VCL风格,那么从32x32程序图标缩小的16x16像素标题栏图标看起来并不像预期的那样. 在此输入图像描述

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

Dal*_*kar 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 Flip和Windows Flip 3D.请参阅:MainFormOnTaskBar

在任何情况下都不要更改应用程序图标大小,因为它是最糟糕的解决方案.


Jam*_*ton 5

我终于找到了这个问题的底部,并找出了为什么没有VCL样式,并且不适用于VCL样式.

前言: 多年来,VCL一直不支持具有多种图标大小的图标图形的概念:a TIcon总是假定为单个图形 - 而不是一组具有不同尺寸和分辨率的图形.这仍然是正确的,并且在VCL中可能不容易纠正设计问题.

VCL将通过WM_SETICON消息设置表单图标.VCL始终设置wParamICON_BIG:对VCL源的检查显示它ICON_SMALL在设置图标时从不使用.此外,hIconhIconSm成员变量WNDCLASSEX结构始终是NULL创建窗口类时.因此,很明显,VCL甚至都没有试图设置一个小图标.通常情况下,如果应用程序从不设置小图标,Windows会将大图标调整为小尺寸,这非常难看.但是,该规则有一个重要的例外.

请注意,Windows资源文件的ICON资源实际上将存储所谓的图标组,该图标组是原始.ico文件中的一组单独图标图像.该LoadIconAPI指出,只有大的32x32图标将被载入.但是,这实际上并不严格.Windows本身似乎维护了HICON原始资源之间的链接,因此如果需要其他大小的图标,Windows可以根据需要加载它们.

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

与窗口类关联的小图标的句柄.如果此成员为NULL,系统将搜索hIcon成员指定的图标资源,以获取适当大小的图标以用作小图标.

因此,即使VCL不能通过公共TForm.Icon类正确地支持小图标(例如,通过在设计时从属性编辑器中分配它),仍然可以使用以下两种方法之一使事情正常工作:

  • 保留未TForm.Icon设置的属性(无图标).在这种情况下,表单将从中获取图标TApplication.Icon.默认值来自应用程序的MAINICON资源.来自TApplication.Create:

    FIcon := TIcon.Create;
    FIcon.Handle := LoadIcon(MainInstance, 'MAINICON');
    
    Run Code Online (Sandbox Code Playgroud)
  • 如果您不想使用应用程序默认图标,则可以在运行时加载不同的图标资源; 在C++中:

    myForm->Icon->LoadFromResourceName(FindHInstance(...), L"OtherResource");
    
    Run Code Online (Sandbox Code Playgroud)

因此,VCL为小图标提供基本支持,因为它支持从资源加载图标,Windows支持从资源加载的大图标加载小图标.

问题: VCL样式显然不依赖于非客户区域的Windows默认呈现行为,例如标题栏.相反,它处理所有渲染本身.渲染中的一个任务是VCL样式必须确定要渲染的图标.事实证明,即使主VCL表单类不支持小图标 - VCL样式钩子也可以!好吧,有点.这发生在TFormStyleHook.GetIcon:

TmpHandle := THandle(SendMessage(Handle, WM_GETICON, ICON_SMALL, 0));
if TmpHandle = 0 then
  TmpHandle := THandle(SendMessage(Handle, WM_GETICON, ICON_BIG, 0));
Run Code Online (Sandbox Code Playgroud)

问题是,挂钩调用WM_GETICONICON_SMALL和没有ICON_SMALL2.来自MSDN:

  • ICON_SMALL:检索窗口的小图标.
    在上面的代码中,这将返回,NULL因为VCL没有设置一个小图标.
  • ICON_SMALL2:检索应用程序提供的小图标.如果应用程序未提供一个,则系统使用该窗口的系统生成图标.(强调我的)
    在上面的代码中,这将返回一个真实的HICON:问题是系统如何生成图标?我的实验表明,无论如何,你都会得到一个小图标:
    • 如果大图标链接到具有适当图标大小的基础Windows资源,则返回该图标.
    • 否则,系统将调整另一个图标的大小以适应所需的小图标尺寸,并返回该图标.

修复: VCL需要使用ICON_SMALL2而不是ICON_SMALL每次调用WM_GETICON.(请注意,类似的代码TFormStyleHook.TMainMenuBarStyleHook.GetIcon也需要修复.)由于这是一个非常简单的修复,我希望Embarcadero尽快应用它.

解决方法: 在修复VCL之前,您必须创建自己的派生表单挂钩.不幸的是,这TFormStyleHook.GetIcon是私人的,真的很难达到.因此,我们尝试了不同的技术:改变消息处理行为WM_GETICONwParamICON_SMALL如此,它反而像ICON_SMALL2.

class TFixedFormStyleHook : public TFormStyleHook
{
public:
    bool PreventRecursion;
    __fastcall virtual TFixedFormStyleHook(TWinControl* AControl)
        : TFormStyleHook(AControl), PreventRecursion(false) {}
    virtual void __fastcall WndProc(TMessage &Message)
    {
        if (Message.Msg == WM_GETICON && Message.WParam == ICON_SMALL &&
            !PreventRecursion && this->Control &&
            this->Control->HandleAllocated())
        {
            // 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(this->Control->Handle, WM_GETICON,
                ICON_SMALL2, Message.LParam);
            PreventRecursion = false;
            this->Handled = true;
        } else {
            this->TFormStyleHook::WndProc(Message);
        }
    }
};

// In your WinMain function, you have to register your hook for every data
// type that VCL originally registered TFormStyleHook for:
TCustomStyleEngine::RegisterStyleHook(__classid(TForm),
    __classid(TFixedFormStyleHook));
TCustomStyleEngine::RegisterStyleHook(__classid(TCustomForm),
    __classid(TFixedFormStyleHook));
Run Code Online (Sandbox Code Playgroud)