TForm без полей с тенью - PullRequest
       22

TForm без полей с тенью

14 голосов
/ 20 августа 2010

Я сделал производную 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, который выглядит ужасно ). Это всплывающее окно, а не дочернее окно, поэтому я не понимаю, почему оно должно перестать работать.

Предложения, пожалуйста?

Примечание: этот вопрос похож на этот вопрос, который остается без ответа.

NB2: Существует неясный компонент VCL, называемый TShadowWindow , который выглядит так, как будто он будет работать правильно, но оказывается слишком грубо написанным для практического применения.

Обновление: Следуя комментариям Андреаса ниже, я исследовал это дальше и нашел некоторые тонкости.

Под Windows 7 я обнаружил, что тень не появляется при всплывающем окне , если оно находится над другим окном из того же приложения .

Вот простое приложение Delphi, которое использует CreateParams во всплывающем окне для запроса тени, как описано выше.

Windows 7 with shadow only over desktop

Посмотрите, как тень появляется там, где она выходит за пределы основного окна?

Но я хочу использовать окно без полей в качестве всплывающего окна над главным окном. Падающая тень отличает всплывающее окно от окна под ним. Все мое описание выше относится к этому обстоятельству. Очевидно, что какой-то механизм Windows вмешивается здесь.

Я также попробовал то же самое приложение под Windows XP. Вот как это выглядит.

Same application under XP

Это работает правильно с тенью везде *. Г!

Похоже, что это похоже на Vista / W7, как предлагает Андреас.

(* В более ранней версии этого текста и screendump предполагалось, что тени не появляются. Однако это произошло из-за того, что у меня в Windows XP была отключена опция отображения «Тени под меню». Дух.)

Ответы [ 3 ]

7 голосов
/ 21 августа 2010

Нашли это!Вот доказательство:

alt text

Как видите, тень теперь правильно отображается на форме.

Проблема была в Z-порядке.Оказывается, тень сама по себе является отдельным окном, поддерживаемым самой Windows.В Windows 7 кажется, что тень под основным окном.Чтобы заставить его отображаться правильно, нужно переместить его вверх.

Гений Лукаш Пломиньски объяснил это в теме в новостной группе 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-ордера меняются, и это не будет правильно.Лукаш предложил вызывать его в режиме ожидания (например, при обновлении действия) и при получении сообщения WM_WINDOWPOSCHANGED.

3 голосов
/ 01 октября 2010

Чтобы заставить работать тень, мы должны вызвать Win32 API SystemParametersInfo с параметром SPI_SETDROPSHADOW, чтобы включить эффект тени для всей системы, для получения дополнительной информации, пожалуйста, обратитесь к:

SystemParametersInfo

3 голосов
/ 20 августа 2010

"Это работает на моем компьютере."

http://privat.rejbrand.se/shdw.png
(в высоком разрешении)

Но это довольно забавно, потому что у меня слабыйпамять делает такой же вывод, как вы, то есть, что CS_DROPSHADOW не работает без толстой, изменяемой размера рамки.Возможно, вы все еще используете Windows Vista?

...