设置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; } } }
关键说明
- 窗口样式配置:明确移除
WS_CAPTION,同时保留WS_SIZEBOX(可调整边框)、WS_SYSMENU(系统菜单)、最小化/最大化按钮样式,确保窗口功能完整性。 - WM_NCCALCSIZE消息处理:当系统计算非客户区尺寸时,修改客户区矩形的顶部边界,使其与窗口原始顶部对齐,直接消除产生白条的多余非客户区空间。
- 窗口过程子类化:通过JNA替换窗口默认消息处理逻辑,插入自定义的非客户区计算逻辑,确保系统使用调整后的客户区参数。
- SetWindowPos调用:必须传入
SWP_FRAMECHANGED标志,强制系统重新渲染窗口框架,应用新的样式和客户区设置。
内容的提问来源于stack exchange,提问作者Xi Minghui
相关产品推荐
相关产品推荐

