无边界TForm与阴影 [英] Borderless TForm with drop shadow

查看:797
本文介绍了无边界TForm与阴影的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



为了使其脱颖而出,它需要在其边框周围有一个阴影。然而,将边界设置为bsNone的结果是,阴影消失。



各种Google来源提供了以下变体:

 程序TdlgEditServiceTask。 CreateParams(var Params:TCreateParams); 
const
CS_DROPSHADOW = $ 00020000;
开始
继承;
{启用Windows XP和更高版本的阴影效果}
如果(Win32Platform = VER_PLATFORM_WIN32_NT)和
((Win32MajorVersion> 5)或
((Win32MajorVersion = 5)和Win32MinorVersion> = 1)))然后
Params.WindowClass.Style:= Params.WindowClass.Style或
CS_DROPSHADOW;
结束

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



请注意?



注意:这是一个类似的问题,这个问题,这仍然是没有答复。



NB2:有一个模糊的VCL组件,名为 TShadowWindow ,看起来它会做正确的事情,但事实证明是太粗俗地写得不实际。



更新:根据Andreas在下面的评论,我进一步调查了一下,并发现了一些优点。



在Windows 7下,我发现当弹出窗口从同一应用程序在另一个窗口上时,阴影不会出现。



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





看看它的阴影出现在哪个区域超出了主窗口?



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



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





这样可以随处可见的影子正常运行。好啊!



正如Andreas所说,这似乎是一个Vista / W7的事情。



(*这个文本的版本和screendump建议没有阴影出现,但原来是因为我的Windows XP显示选项菜单下的阴影已关闭Duh。)

解决方案

找到了!这是证明:





如您所见,现在的阴影显示在表单上。



问题是Z-订购。事实证明,这个影子本身就是由Windows本身维护的一个单独的窗口。在Windows 7中,它似乎在主窗口下方显示阴影。为了使其正确显示,需要将其移动。



一个名叫ŁukaszPłomiński的天才在Embarcadero新闻组的一个主题中解释了这一点。这是他的代码来排序:

  procedure TForm1.FixSysShadowOrder; 

函数FindSysShadowOrderProc(WindowHandle:HWND; //窗口窗口
窗体:TForm1 //应用程序定义的值,32位
):BOOL;标准
var
缓冲区:数组[0 .. 255]的char;
Rect:TRect;
begin
结果:= True;
如果IsWindowVisible(WindowHandle)然后
begin
//此代码搜索为此窗口创建的SysShadow窗口。
GetClassName(WindowHandle,Buffer,255);
如果0<> AnsiStrComp(Buffer,PChar('SysShadow'))然后
退出;

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

Form.FSysShadowHandle:= WindowHandle;
//停止枚举
结果:= False;
结束
结束

开始
如果没有(在ComponentState中设置csDesigning)和
((GetClassLong(Handle,GCL_STYLE)和CS_DROPSHADOW)= CS_DROPSHADOW)
和IsWindowVisible(Handle)然后
begin
//为了速度,正确的SysShadow句柄缓存
如果FSysShadowHandle = 0然后
EnumThreadWindows(GetCurrentThreadID(),@FindSysShadowOrderProc,
lParam(Self));
//如果SysShadow存在,更改其z顺序,并将其直接放置在此窗口下方
,如果FSysShadowHandle& 0 then
SetWindowPos(FSysShadowHandle,Handle,0,0,0,0,
SWP_NOACTIVATE或SWP_NOMOVE或SWP_NOOWNERZORDER或SWP_NOSIZE);
结束
结束

您必须解决什么时候调用 FixSysShadowOrder(),因为Z订单改变了,它不会保持正确。 Łukasz建议在空闲程序中调用它(例如更新Action时),并收到 WM_WINDOWPOSCHANGED 消息。


I have made a TForm derivative that acts like the drop down part of a combo, or a hint window, or a popup menu - a temporary thing. It has no caption - its BorderStyle is set to bsNone. The form is displayed non-modally using Show, having set its position.

To make it stand out, it needs a drop shadow around its border. However, a consequence of setting its border to bsNone is that the drop shadow disappears.

Various Google sources suggest variations of this:

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;

but it doesn't work - the shadow is not displayed (unless I also set a resizable border with WS_THICKFRAME set, which looks terrible). This is a popup window, not a child window, so I don't see why it should fail.

Suggestions please?

NB: this is a similar question to this question, which remains unanswered.

NB2: There is an obscure VCL component called TShadowWindow that looks like it will do the right thing, but turns out to be too crudely written to be practical.

Update: Following Andreas' comments below, I have investigated this further, and found some niceties.

Under Windows 7, I discovered that the shadow does not appear when the popup window if it is over another window from the same application.

Here is a simple Delphi app, which uses CreateParams on a popup window to request a shadow as described above.

See how the drop shadow appears where it extends beyond the main window?

But I want to use the borderless window as a popup over the main window. The drop shadow distinguishes the popup from the window underneath. All my description up above refers to this circumstance. Obviously some Windows mechanism is interfering here.

I have also tried the same application under Windows XP. Here is how it looks.

This works correctly with shadow everywhere*. Gah!

So it would seem to be a Vista/W7 thing, as Andreas suggests.

(*An earlier version of this text and screendump suggested that no shadow appeared. However, this turned out to be because I had the Windows XP display option 'Shadows under menus' turned off. Duh.)

解决方案

Found it! Here is the proof:

As you can see, the drop shadow now shows properly over the form.

The problem was one of Z-order. It turns out that the shadow is itself a separate window maintained by Windows itself. In Windows 7, it seems to show the shadow underneath the main window. In order to get it to display properly, one needs to move it up.

A genius called Łukasz Płomiński explained this in a thread in the Embarcadero newsgroup. Here is his code to sort it out:

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;

You have to work out when to call FixSysShadowOrder(), because Z orders change, and it won't stay right. Łukasz suggested calling it in an idle routine (for example when updating an Action), and on receipt of WM_WINDOWPOSCHANGED message.

这篇关于无边界TForm与阴影的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆