14

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.

Windows 7 with shadow only over desktop

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.

Same application under XP

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.)

Community
  • 1
  • 1
willw
  • 1,308
  • 13
  • 28
  • I do not fully understand how you want your window. You do *not* want it like the Code Insight popup (which has a thick resizable border) in the RAD Studio IDE, do you? – Andreas Rejbrand Aug 20 '10 at 10:52
  • @Andreas: Correct. I do NOT want a resizable border - this is what I can get by setting WS_THICKFRAME as mentioned in the question. I want its border to look like that of, say, a menu, ie single thin line with shadow. Actually, now you mention it, I notice Code Insight shows at least three different types of window, depending on the context. I want to be like the ones that don't have resizable borders! :-) – willw Aug 20 '10 at 11:44

3 Answers3

8

Found it! Here is the proof:

alt text

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.

willw
  • 1,308
  • 13
  • 28
  • Hm... I wonder if one could call this a bug in Microsoft Windows. – Andreas Rejbrand Aug 21 '10 at 16:31
  • @Edwin Yip: Yes it can. You declare FSysShadowHandle yourself in TForm1 to cache the handle, as stated in teh code's comment. – willw Aug 01 '13 at 13:47
  • @willw, Thanks! Sorry I didn't readit carefully. So I manually added `FSysShadowHandle`, but somehow couldn't find the window with class name 'SysShadow', I output all class names found by `GetClassName(WindowHandle, Buffer, 255);' no 'SysShadow', strange... – Edwin Yip Aug 03 '13 at 07:07
  • @willw, I then tried using the Spy++ tool came from Visual Studio 6.0, the window list also doesn't contain any window with window class name 'SysShadow'. I'm using Win 7 64bit Ultimate. – Edwin Yip Aug 03 '13 at 07:13
  • @Edwin. Dunno - perhaps you are running W7 in a mode where it doesn't create a shadow window? It depends on user choices for the desktop, I think. I was running with Aero in default mode when I tested this. (Don't know what happened to my screen dumps.) This is working code, which I used for a while with D2010, running an app on XP and W7. I don't use it any more - came to the conclusion that anything that required this much fighting with what Windows 'wants' to do was probably an error. HTH - W – willw Aug 05 '13 at 10:55
  • @willw, I have shadow enabled on my system. When I have time I'll investigate the problem and back to here to comment if I'd found anything worth. – Edwin Yip Aug 06 '13 at 17:32
  • @willw, looks like it's not working because my borderless form is a **child** window, which is a must in my app... – Edwin Yip Sep 26 '13 at 08:30
  • In my implementation, in order to drop the shadow I had to run the FixSysShadowOrder after 10millisecons from OnShow. Then the user can closed and reopen the form, so in order to have the shadow redrawn again I have to comment the "if FSysShadowHandle = 0 then..." disabling cache. – Giorgio Calzolato May 15 '22 at 19:26
3

"It works on my computer."


(High-res)

But it is quite funny, for I have a faint memory of making the same conclusion as you make, that is, that CS_DROPSHADOW does not work without the thick, resizable, frame. Are you still running Windows Vista, perhaps?

Andreas Rejbrand
  • 105,602
  • 8
  • 282
  • 384
  • This is an excellent point - many thanks. I was using Windows 7, and from my own test that does make a difference... it doesn't work in at least two different ways! I am off to edit my question to reflect the more complex than I knew situation. – willw Aug 21 '10 at 10:38
3

For making drop shadow to work we have to invoke SystemParametersInfo win32 API with SPI_SETDROPSHADOW parameter, to turn on the entire system's drop shadow effect, for more information, please refer to:

SystemParametersInfo

Ravi shankar
  • 2,450
  • 1
  • 25
  • 34