返回

移除窗口标题栏,保留最小化/最大化动画效果

windows

无标题栏窗口的最小化/最大化动画

许多应用希望拥有自定义的标题栏,以实现更具个性化的界面设计。 移除标准标题栏很容易,但直接操作会导致窗口失去最小化和最大化动画效果,这会降低用户体验。 本文将探讨如何移除标题栏的同时保留这些动画。

问题分析

使用 WS_POPUP 样式创建窗口可以移除标题栏,但它也禁用了系统默认的窗口动画。这是因为标准的最小化/最大化动画与窗口的非客户区(NC)相关联,而WS_POPUP 样式的窗口没有标准的 NC 区域。

解决方案:覆盖窗口过程

一个有效的解决方案是子类化窗口过程,拦截WM_NCCALCSIZE消息。这个消息控制窗口的非客户区大小,我们可以通过修改它来模拟一个拥有标题栏的窗口,即使实际上没有。

代码示例 (C++):

LRESULT CALLBACK CustomWndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    switch (message) {
    case WM_NCCALCSIZE:
        if (wParam) {
            //  模拟标准标题栏的高度,让系统认为窗口有标题栏。
            RECT* rc = reinterpret_cast<RECT*>(lParam);
            rc->top += GetSystemMetrics(SM_CYCAPTION);
            return 0;
        }
        break;
    default:
        return DefWindowProc(hWnd, message, wParam, lParam);
    }
    return 0;
}

// 在窗口创建后子类化
SetWindowLongPtr(g_hWnd, GWLP_WNDPROC, (LONG_PTR)CustomWndProc);

操作步骤:

  1. 创建一个自定义的窗口过程 CustomWndProc
  2. WM_NCCALCSIZE 消息处理中,增加窗口客户区顶部偏移量,模拟标题栏的高度。
  3. 使用 SetWindowLongPtr 将窗口过程替换为自定义的 CustomWndProc

原理: 通过修改WM_NCCALCSIZE 返回值,欺骗系统认为窗口拥有一个标准标题栏,从而启用对应的动画效果。尽管窗口实际上没有标题栏,但系统会根据模拟的高度进行动画处理。

方案二:使用 DwmExtendFrameIntoClientArea

Windows Vista 及更高版本提供了 DwmExtendFrameIntoClientArea 函数,可以将窗口框架扩展到客户区。通过将客户区的顶部扩展到标题栏区域,可以实现类似的效果。

代码示例 (C++):

MARGINS margins = {0, GetSystemMetrics(SM_CYCAPTION), 0, 0};
DwmExtendFrameIntoClientArea(g_hWnd, &margins);

操作步骤:

  1. 包含 dwmapi.lib 库文件。
  2. 调用 DwmExtendFrameIntoClientArea 函数,并设置 margins 结构的顶部值,模拟标题栏高度。

原理: DwmExtendFrameIntoClientArea 允许修改窗口框架的绘制区域,使 DWM 将窗口框架延伸到客户区。 通过将框架延伸到标题栏区域,系统会自动应用相应的动画效果。

安全建议

  • 在多显示器环境下,确保使用正确的显示器信息获取 SM_CYCAPTION 的值。
  • 在不同 DPI 设置下,SM_CYCAPTION 的值可能不同,需要进行相应的调整。可以使用 GetSystemMetricsForDpi (Windows 10 1607 及以上版本可用) 获取特定 DPI 下的系统指标。

通过以上两种方案,都可以实现移除标题栏的同时保留最小化和最大化动画。根据操作系统版本和项目需求选择合适的方案即可。 仔细理解其原理,有助于更好地控制窗口的外观和行为,打造更精美的用户界面。