windows – 在最小化/恢复动画打开时,如何在删除任务栏按钮之前平滑最小化窗口?

我正在最小化表单到系统托盘(显示托盘图标),同时保持其任务栏按钮未最小化.这意味着在窗体最小化时删除任务栏按钮,否则恢复它.

实现此目的的最简单方法是隐藏/显示表单,最小化的窗口无论如何都不显示.

type
  TForm1 = class(TForm)
    TrayIcon1: TTrayIcon;
    procedure TrayIcon1DblClick(Sender: TObject);
  protected
    procedure WMSize(var Message: TWMSize); message WM_SIZE;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.WMSize(var Message: TWMSize);
begin
  inherited;
  case Message.SizeType of
    SIZE_MINIMIZED:
      if not TrayIcon1.Visible then begin
        TrayIcon1.Visible := True;
        Hide;
      end;
    SIZE_RESTORED, SIZE_MAXIMIZED:
      if TrayIcon1.Visible then begin
        Show;
        Application.BringToFront;
        TrayIcon1.Visible := False;
      end;
  end;
end;

procedure TForm1.TrayIcon1DblClick(Sender: TObject);
begin
  Show;
  WindowState := wsNormal;
end;

当操作系统的“最小化和最大化时动画窗口”设置打开时(可通过“SystemPropertiesPerformance.exe”访问),上述应用程序会引入视觉故障.跳过最小化窗口动画.看起来动画实际上是在窗口最小化之后发生的.在代码中,窗口已经被隐藏了.

一种解决方案可以是在窗口管理器完成动画时发出通知并在此之后隐藏表单.我找不到任何东西.例如,当您使用最小化窗口的任务栏按钮时,发送的最后一条消息是WM_SYSCOMMAND,如果我移动代码,则不会导致任何进展(更不用说可以通过ShowWindow和其他方式最小化窗口) ).

另一个解决方案可能涉及知道动画发生了多长时间. SystemParametersInfo没有它.类似的问题here尝试处理首次显示窗口时显示的动画,尽管该动画似乎与DWM相关,并且最小化/最大化动画在DWM之前.也没有确凿的解决方案.就像那个问题一样,250毫秒的延迟似乎工作得很好.但我不确定这是一个普遍合理的延迟.我甚至不确定一个离散的延迟是肯定的,也许一个口吃会导致它延伸(不是它会很重要,但无论如何……).

我还试图删除任务栏按钮,而不隐藏表单.但它更笨拙并且不会改变输出:动画被跳过.

最佳答案 关于DrawAnimatedRects的评论(当Aero打开时没有绘制动画)说服我稍微没有记录,直到我有更好的选择.使用DrawAnimatedRects的方法必须确定最小化的位置,即使用未记录的系统托盘窗口类名称的位置.

删除表单的任务栏按钮时,下面的代码没有记录,特别是使用SetWindowLongPtr的GWLP_HWNDPARENT索引.在任何情况下,删除任务栏按钮并不笨拙,就像将窗口转换为工具窗口一样,动画也很流畅.

代码回退到一个计时器,该计时器隐藏表单,以防删除任务栏按钮失败.

type
  TForm1 = class(TForm)
    TrayIcon1: TTrayIcon;
    Timer1: TTimer;
    procedure TrayIcon1DblClick(Sender: TObject);
    procedure Timer1Timer(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  protected
    procedure WMSize(var Message: TWMSize); message WM_SIZE;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

function ShowTaskbarButton(Wnd: HWND; Show: Boolean = True;
    OwnerWnd: HWND = 0): Boolean;
var
  ExStyle, HWndParent: LONG_PTR;
  IsToolWindow: Boolean;
begin
  HwndParent := GetWindowLongPtr(Wnd, GWLP_HWNDPARENT);
  ExStyle := GetWindowLongPtr(Wnd, GWL_EXSTYLE);
  Result := Show = (HWndParent = 0) and (ExStyle and WS_EX_APPWINDOW <> 0);

  if not Result then begin
    IsToolWindow := ExStyle and WS_EX_TOOLWINDOW <> 0;
    if IsToolWindow then begin
      ShowWindow(Wnd, SW_HIDE);
      ShowWindowAsync(Wnd, SW_SHOW);
    end;
    SetLastError(0);
    if Show then
      SetWindowLongPtr(Wnd, GWL_EXSTYLE, ExStyle or WS_EX_APPWINDOW)
    else
      SetWindowLongPtr(Wnd, GWL_EXSTYLE, ExStyle and not WS_EX_APPWINDOW);
    if not IsToolWindow and (GetLastError = 0) then
      SetWindowLongPtr(Wnd, GWLP_HWNDPARENT, OwnerWnd);

    Result := GetLastError = 0;
  end;
end;

procedure TForm1.WMSize(var Message: TWMSize);
begin
  inherited;
  case Message.SizeType of
    SIZE_MINIMIZED:
      if not TrayIcon1.Visible then begin
        if not ShowTaskbarButton(Handle, False, Application.Handle) then
          Timer1.Enabled := True;   // fall back
        TrayIcon1.Visible := True
      end;
    SIZE_RESTORED, SIZE_MAXIMIZED:
      if TrayIcon1.Visible then begin
        ShowTaskbarButton(Handle);
        Application.BringToFront;
        TrayIcon1.Visible := False;
      end;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Timer1.Interval := 250;
  Timer1.Enabled := False;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
begin
  Hide;
  Timer1.Enabled := False;
end;

procedure TForm1.TrayIcon1DblClick(Sender: TObject);
begin
  ShowTaskbarButton(Handle);
  if not Showing then   // used timer to hide
    Show;
  WindowState := wsNormal;
end;
点赞