带投影的无边框 TForm

Posted

技术标签:

【中文标题】带投影的无边框 TForm【英文标题】:Borderless TForm with drop shadow 【发布时间】:2011-04-01 14:21:08 【问题描述】:

我制作了一个 TForm 衍生物,它的作用类似于组合的下拉部分、提示窗口或弹出菜单 - 一个临时的东西。它没有标题 - 它的 BorderStyle 设置为 bsNone。表单使用 Show 以非模态方式显示,并设置了它的位置。

为了使其脱颖而出,它需要在其边框周围放置阴影。但是,将其边框设置为 bsNone 的结果是投影消失了。

各种 Google 消息来源暗示了这种变化:

procedure TdlgEditServiceTask.CreateParams(var Params: TCreateParams);
const
  CS_DROPSHADOW = $00020000;
begin
  inherited;
   Enable drop shadow effect on Windows XP and later 
  if (Win32Platform = VER_PLATFORM_WIN32_NT) and
     ((Win32MajorVersion > 5) or
      ((Win32MajorVersion = 5) and (Win32MinorVersion >= 1))) then
    Params.WindowClass.Style := Params.WindowClass.Style or
             CS_DROPSHADOW;
end;

但它不起作用 - 不显示阴影(除非我还设置了 WS_THICKFRAME 集的可调整大小的边框,这看起来可怕)。这是一个弹出窗口,而不是子窗口,所以我不明白它为什么会失败。

请给点建议?

注意:这是一个与this 问题类似的问题,但仍未得到解答。

NB2:有一个名为 TShadowWindow 的不起眼的 VCL 组件,看起来它会做正确的事情,但事实证明它写得太粗略,不实用。

更新:在下面 Andreas 的 cmets 之后,我对此进行了进一步调查,并发现了一些细节。

在 Windows 7 下,我发现当弹出窗口 如果它位于同一应用程序的另一个窗口上方时,阴影不会出现

这是一个简单的 Delphi 应用程序,它使用弹出窗口上的 CreateParams 来请求上述阴影。

看看投影在超出主窗口的地方是如何显示的?

但我想使用无边框窗口作为主窗口上方的弹出窗口。阴影将弹出窗口与下面的窗口区分开来。我上面所有的描述都是针对这种情况的。显然,这里有一些 Windows 机制的干扰。

我也在 Windows XP 下尝试过相同的应用程序。这是它的外观。

这适用于无处不在的阴影*。呸!

因此,正如 Andreas 所建议的那样,这似乎是 Vista/W7 的事情。

(*此文本和 screendump 的早期版本表明没有出现阴影。然而,事实证明这是因为我关闭了 Windows XP 显示选项“菜单下的阴影”。Duh。)

【问题讨论】:

我不完全明白你想要你的窗口。您确实希望它像 RAD Studio IDE 中的 Code Insight 弹出窗口(具有可调整大小的粗边框)一样,是吗? @Andreas:正确。我不想要一个可调整大小的边框 - 这是我可以通过设置问题中提到的 WS_THICKFRAME 得到的。我希望它的边框看起来像菜单的边框,即带有阴影的单条细线。实际上,现在您提到它,我注意到 Code Insight 显示至少三种不同类型的窗口,具体取决于上下文。我想成为那些没有可调整边框的人! :-) 【参考方案1】:

“它可以在我的电脑上运行。”

(High-res)

但这很有趣,因为我有一个模糊的记忆,我做出了和你一样的结论,即CS_DROPSHADOW 没有厚的、可调整大小的框架是行不通的。您可能还在运行 Windows Vista 吗?

【讨论】:

这是一个很好的观点 - 非常感谢。我使用的是 Windows 7,并且根据我自己的测试确实有所不同......它至少以两种不同的方式工作!我要去编辑我的问题,以反映比我知道的情况更复杂的情况。【参考方案2】:

找到了!证明如下:

如您所见,阴影现在可以正确显示在表单上。

问题是 Z 顺序之一。原来,影子本身就是一个独立的窗口,由 Windows 自己维护。在 Windows 7 中,它似乎在主窗口下方显示阴影。为了使其正常显示,需要将其向上移动。

一位名叫 Łukasz Płomiński 的天才在 Embarcadero 新闻组的帖子中解释了这一点。这是他整理出来的代码:

procedure TForm1.FixSysShadowOrder;

  function FindSysShadowOrderProc(WindowHandle: HWND; // handle to window
    Form: TForm1 // application-defined value, 32-bit
    ): BOOL; stdcall;
  var
    Buffer: array [0 .. 255] of char;
    Rect: TRect;
  begin
    Result := True;
    if IsWindowVisible(WindowHandle) then
    begin
      // this code  search for SysShadow window created for this window.
      GetClassName(WindowHandle, Buffer, 255);
      if 0 <> AnsiStrComp(Buffer, PChar('SysShadow')) then
        Exit;

      GetWindowRect(WindowHandle, Rect);
      if (Rect.Left <> Form.Left) or (Rect.Top <> Form.Top) then
        Exit;

      Form.FSysShadowHandle := WindowHandle;
      // stop enumeration
      Result := False;
    end;
  end;

begin
  if not(csDesigning in ComponentState) and
    ((GetClassLong(Handle, GCL_STYLE) and CS_DROPSHADOW) = CS_DROPSHADOW)
    and IsWindowVisible(Handle) then
  begin
    // for speed, proper SysShadow handle is cached
    if FSysShadowHandle = 0 then
      EnumThreadWindows(GetCurrentThreadID(), @FindSysShadowOrderProc,
        lParam(Self));
    // if SysShadow exists, change its z-order, and place it directly below this window
    if FSysShadowHandle <> 0 then
      SetWindowPos(FSysShadowHandle, Handle, 0, 0, 0, 0,
        SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOOWNERZORDER or SWP_NOSIZE);
  end;
end;

您必须确定何时致电FixSysShadowOrder(),因为 Z 订单会更改,并且不会保持正确。 Łukasz 建议在空闲例程中调用它(例如在更新操作时),并在收到WM_WINDOWPOSCHANGED 消息时调用它。

【讨论】:

嗯...我想知道是否有人可以将其称为 Microsoft Windows 中的错误。 @Edwin Yip:可以。您自己在 TForm1 中声明 FSysShadowHandle 以缓存句柄,如代码注释中所述。 @willw,谢谢!对不起,我没有仔细阅读。所以我手动添加了FSysShadowHandle,但不知何故找不到类名'SysShadow'的窗口,我输出了'GetClassName(WindowHandle,Buffer,255);'找到的所有类名没有'SysShadow',奇怪...... @willw,然后我尝试使用来自 Visual Studio 6.0 的 Spy++ 工具,窗口列表也不包含任何窗口类名为“SysShadow”的窗口。我正在使用 Win 7 64bit Ultimate。 @Edwin。不知道-也许您正在以不创建阴影窗口的模式运行 W7?我认为这取决于用户对桌面的选择。当我测试这个时,我在默认模式下使用 Aero 运行。 (不知道我的屏幕转储发生了什么。)这是工作代码,我在 D2010 上使用了一段时间,在 XP 和 W7 上运行应用程序。我不再使用它 - 得出的结论是,任何需要与 Windows“想要”做的事情进行如此激烈的斗争的事情都可能是一个错误。 HTH - W【参考方案3】:

为了使阴影生效,我们必须调用带有SPI_SETDROPSHADOW参数的SystemParametersInfo win32 API,以打开整个系统的阴影效果,更多信息请参考:

SystemParametersInfo

【讨论】:

以上是关于带投影的无边框 TForm的主要内容,如果未能解决你的问题,请参考以下文章

在 Qt 上制作一个没有控制框的无边框应用程序

创建一个可以在Word中粘贴的无边框表

无边框形式的 Windows 7 样式 Dropshadow

uwp 中的无铬窗口

Qt商业级无边框窗口实现源码(windowsmsvc)

css如何制作边框投影效果?