Borderless TForm with drop shadow

前端 未结 3 551
遥遥无期
遥遥无期 2021-02-01 08:02

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

3条回答
  •  时光说笑
    2021-02-01 08:54

    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.

提交回复
热议问题