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

标签 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 毫秒的延迟似乎可以正常工作。但我不确定这是普遍存在的声音延迟。我什至不确定离散延迟是否是确定的,也许口吃会导致它达到一定程度(不是很重要,但无论如何......)。


我还尝试在不隐藏表单的情况下实际删除任务栏按钮。但它比较笨拙,它不会改变输出:动画被跳过。

最佳答案

关于 DrawAnimatedRects(当 Aero 开启时不绘制动画)的评论说服我在有更好的替代方案之前略微未记录。使用 DrawAnimatedRects 的方法必须确定最小化位置,这是它们使用未记录的系统托盘窗口类名的地方。

当删除表单的任务栏按钮时,以下代码未记录,特别是使用 SetWindowLongPtrGWLP_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;

关于windows - 当最小化/恢复动画打开时,如何在删除任务栏按钮之前平滑地最小化窗口?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40116051/

相关文章:

Windows程序员,安装到C :\Windows?是个好习惯吗

windows - Windows Server 2003 中的最大线程数是多少?

delphi - Delphi中类名相同的两个单元中的两个类

winapi - 使用 C++ 在 Windows 上创建符号链接(symbolic link)

windows - 启动 Windows Phone 模拟器时出现问题

windows - WriteFile 函数的 lpNumberOfBytesWritten 参数

python - 我可以使用 zipimport 来发送嵌入式 python 吗?

delphi - OnShow 事件后自动启动操作的最佳方法是什么?

winapi - 如何验证仅出站的服务器端管道句柄是管道?

windows - GetFinalPathNameByHandle 对于设备句柄失败