You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

设置WS_SIZEBOX取消WS_CAPTION后窗口标题栏残留白条问题求助

解决无标题栏可调整窗口的顶部白条问题

问题现象

当窗口设置WS_SIZEBOX(与WS_THICKFRAME等价)样式且移除WS_CAPTION后,窗口会先显示标题栏,触发相关事件后标题栏消失,但顶部残留6px白条;调用SetWindowPos应用样式后标题栏消失,但白条和顶部粗边框问题依然存在。

解决方案

通过子类化窗口过程,处理WM_NCCALCSIZE消息手动调整客户区区域,消除多余的顶部非客户区空间,同时确保窗口样式和SetWindowPos的调用逻辑正确。

修改后的完整代码

import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinUser;
import com.sun.jna.platform.win32.WinUser.WNDPROC;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Background;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.stage.Stage;

import static com.sun.jna.platform.win32.WinUser.*;

class Main {
    public static void main(String[] args) {
        Application.launch(MyApp.class);
    }
}

public class MyApp extends Application {
    private WNDPROC oldWndProc;

    @Override
    public void start(Stage primaryStage) {
        primaryStage.setTitle("Clover GUI");
        Pane pane = new Pane();
        pane.setBackground(Background.fill(Color.rgb(30, 31, 34)));
        primaryStage.setScene(new Scene(pane));
        primaryStage.show();

        WinDef.HWND hwnd = getHWND(primaryStage);
        int oldStyle = User32.INSTANCE.GetWindowLongPtr(hwnd, GWL_STYLE).intValue();
        
        // 设置正确的窗口样式:移除WS_CAPTION,保留可调整边框和系统菜单等
        int newStyle = oldStyle & ~WS_CAPTION | WS_SIZEBOX | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX;
        User32.INSTANCE.SetWindowLongPtr(hwnd, GWL_STYLE, new Pointer(newStyle));

        // 子类化窗口过程,处理WM_NCCALCSIZE消息消除顶部白条
        oldWndProc = new WNDPROC() {
            @Override
            public WinDef.LRESULT callback(WinDef.HWND hWnd, int uMsg, WinDef.WPARAM wParam, WinDef.LPARAM lParam) {
                if (uMsg == WM_NCCALCSIZE && wParam.intValue() != 0) {
                    WinUser.NCCALCSIZE_PARAMS params = new WinUser.NCCALCSIZE_PARAMS(lParam);
                    // 将客户区顶部与窗口原始顶部对齐,消除多余非客户区
                    params.rgrc[0].top = params.rgrc[1].top;
                    params.write();
                    return new WinDef.LRESULT(0);
                }
                return User32.INSTANCE.CallWindowProc(oldWndProc, hWnd, uMsg, wParam, lParam);
            }
        };
        User32.INSTANCE.SetWindowLongPtr(hwnd, GWL_WNDPROC, new Pointer(Pointer.nativeValue(oldWndProc.getPointer())));

        // 强制应用样式变更
        int uFlags = SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE | SWP_NOREPOSITION | SWP_NOZORDER;
        User32.INSTANCE.SetWindowPos(hwnd, null, 0, 0, 0, 0, uFlags);
    }

    // 获取JavaFX窗口的HWND句柄
    private WinDef.HWND getHWND(Stage stage) {
        try {
            java.lang.reflect.Field field = stage.getClass().getDeclaredField("peer");
            field.setAccessible(true);
            Object peer = field.get(stage);
            java.lang.reflect.Method method = peer.getClass().getDeclaredMethod("getHWND");
            method.setAccessible(true);
            long hwndPtr = (long) method.invoke(peer);
            return new WinDef.HWND(new Pointer(hwndPtr));
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
}

关键说明

  1. 窗口样式配置:明确移除WS_CAPTION,同时保留WS_SIZEBOX(可调整边框)、WS_SYSMENU(系统菜单)、最小化/最大化按钮样式,确保窗口功能完整性。
  2. WM_NCCALCSIZE消息处理:当系统计算非客户区尺寸时,修改客户区矩形的顶部边界,使其与窗口原始顶部对齐,直接消除产生白条的多余非客户区空间。
  3. 窗口过程子类化:通过JNA替换窗口默认消息处理逻辑,插入自定义的非客户区计算逻辑,确保系统使用调整后的客户区参数。
  4. SetWindowPos调用:必须传入SWP_FRAMECHANGED标志,强制系统重新渲染窗口框架,应用新的样式和客户区设置。

内容的提问来源于stack exchange,提问作者Xi Minghui

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.30 22:24:21