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

时间:2016-10-18 19:01:42

标签: windows delphi winapi

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

实现这一目标的最简单方法是隐藏/显示表单,无论如何都不会显示最小化的窗口。

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毫秒的延迟似乎工作得很好。但我不确定这是一个普遍合理的延迟。我甚至不确定一个离散的延迟是肯定的,也许一个口吃会导致它延伸(不是它会很重要,但无论如何......)。

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

1 个答案:

答案 0 :(得分:1)

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

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

代码会回退到一个计时器,如果删除任务栏按钮失败,它会隐藏表单。

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;