Win32 C++自定义居中对话框遇WM_CREATE未触发问题求助
如何让自定义对话框始终在主窗口当前位置居中显示?
问题背景
我希望创建一个始终在应用窗口当前中央位置打开的对话框,这是MessageBox()这类函数的常规行为,但实际测试发现:如果在打开对话框前移动了主窗口,对话框会在主窗口原来的位置居中,而非当前位置——显然系统缓存了主窗口的旧位置。
MessageBox()没有提供定位选项,无法调整对话框位置;尝试过TaskDialogIndirect(),虽然能设置(X,Y)坐标,但X位置存在偏差,且无法修改对话框大小,因此决定自行实现自定义对话框。
现有代码的问题
我尝试子类化主窗口来实现自定义对话框,但发现自定义的DialogWndProc始终收不到WM_CREATE消息,代码如下:
LRESULT CALLBACK DialogWndProc(HWND wnd, UINT uMsg, WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass, [[maybe_unused]] DWORD_PTR dwRefData) { switch (uMsg) { case WM_CREATE: CreateWindowEx(NULL, L"BUTTON", L"Okay", WS_CHILD | BS_DEFPUSHBUTTON | WS_VISIBLE, 50, 50, 100, 40, wnd, (HMENU)1002, GetModuleHandle(NULL), nullptr); break; case WM_CLOSE: { HWND owner = GetWindow(wnd, GW_OWNER); EnableWindow(owner, true); SetFocus(owner); DestroyWindow(wnd); return 0; } case WM_NCDESTROY: RemoveWindowSubclass(wnd, DialogWndProc, uIdSubclass); break; } return DefSubclassProc(wnd, uMsg, wParam, lParam); } // hwd is the HANDLE of the main application window void CustomDialog(HWND hwd, const std::wstring title, const std::wstring body, size_t xp, size_t yp, size_t xs, size_t ys) { // Get the class name from the parent window std::vector<wchar_t> class_name(256); class_name.resize(static_cast<size_t>(GetClassName(hwd, class_name.data(), static_cast<int>(class_name.size())))); // Subclass the window and change WNDPROC BOOL t = SetWindowSubclass(hwd, DialogWndProc, 1, 0); // Create the modal dialog window HWND dlg = CreateWindowEx(WS_EX_DLGMODALFRAME, class_name.c_str(), title.c_str(), WS_POPUPWINDOW | WS_CAPTION | DS_MODALFRAME | DS_NOIDLEMSG, xp, yp, xs, ys, hwd, NULL, GetModuleHandle(NULL), nullptr); ShowWindow(dlg, SW_SHOW); EnableWindow(hwd, false); }
问题根源:子类化操作只作用于调用SetWindowSubclass的主窗口,而新创建的对话框是复用主窗口类名生成的独立窗口,它的窗口过程仍然是主窗口类原本的WndProc,不会继承子类化后的DialogWndProc,因此收不到WM_CREATE消息。
正确的解决方案
要实现始终在主窗口当前位置居中的自定义模态对话框,需要以下几个关键步骤:
1. 注册独立的对话框窗口类
为对话框注册专属的窗口类,确保它有自己的窗口过程,能正常接收窗口消息。
2. 计算主窗口当前位置的居中坐标
使用GetWindowRect获取主窗口实时的屏幕坐标,以此计算对话框的居中位置,避免系统缓存的旧位置问题。
3. 实现模态对话框的消息循环
自定义窗口默认是非模态的,需要手动处理消息循环,确保对话框关闭前主窗口无法交互。
修正后的完整代码
#include <windows.h> #include <string> #include <vector> // 自定义对话框的窗口过程 LRESULT CALLBACK CustomDialogWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_CREATE: // 创建OK按钮 CreateWindowExW(0, L"BUTTON", L"Okay", WS_CHILD | BS_DEFPUSHBUTTON | WS_VISIBLE, 50, 50, 100, 40, hWnd, (HMENU)1002, GetModuleHandleW(NULL), nullptr); break; case WM_COMMAND: if (LOWORD(wParam) == 1002 && HIWORD(wParam) == BN_CLICKED) { // 点击按钮触发关闭 SendMessageW(hWnd, WM_CLOSE, 0, 0); } break; case WM_CLOSE: { HWND hOwner = GetWindow(hWnd, GW_OWNER); EnableWindow(hOwner, TRUE); SetFocus(hOwner); DestroyWindow(hWnd); } return 0; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProcW(hWnd, uMsg, wParam, lParam); } return 0; } // 注册对话框窗口类(仅需执行一次) ATOM RegisterCustomDialogClass(HINSTANCE hInstance) { WNDCLASSEXW wc = {0}; wc.cbSize = sizeof(WNDCLASSEXW); wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = CustomDialogWndProc; wc.cbClsExtra = 0; wc.cbWndExtra = DLGWINDOWEXTRA; // 预留对话框额外空间,符合标准对话框特性 wc.hInstance = hInstance; wc.hIcon = LoadIconW(NULL, IDI_APPLICATION); wc.hCursor = LoadCursorW(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wc.lpszMenuName = NULL; wc.lpszClassName = L"CustomModalDialogClass"; wc.hIconSm = LoadIconW(NULL, IDI_APPLICATION); return RegisterClassExW(&wc); } // 显示在主窗口当前位置居中的自定义模态对话框 void ShowCenteredCustomDialog(HWND hMainWnd, const std::wstring& title, const std::wstring& body) { HINSTANCE hInstance = GetModuleHandleW(NULL); // 注册窗口类(静态变量确保仅注册一次) static ATOM dialogClass = RegisterCustomDialogClass(hInstance); if (dialogClass == 0) { MessageBoxW(hMainWnd, L"注册对话框类失败", L"错误", MB_ICONERROR); return; } // 获取主窗口当前的屏幕位置和大小 RECT mainRect; GetWindowRect(hMainWnd, &mainRect); int mainWidth = mainRect.right - mainRect.left; int mainHeight = mainRect.bottom - mainRect.top; // 定义对话框的尺寸 const int dlgWidth = 300; const int dlgHeight = 150; // 计算对话框的居中坐标 int dlgX = mainRect.left + (mainWidth - dlgWidth) / 2; int dlgY = mainRect.top + (mainHeight - dlgHeight) / 2; // 创建对话框窗口 HWND hDlg = CreateWindowExW( WS_EX_DLGMODALFRAME, L"CustomModalDialogClass", title.c_str(), WS_POPUPWINDOW | WS_CAPTION | WS_SYSMENU | DS_MODALFRAME, dlgX, dlgY, dlgWidth, dlgHeight, hMainWnd, NULL, hInstance, nullptr); if (hDlg == NULL) { MessageBoxW(hMainWnd, L"创建对话框失败", L"错误", MB_ICONERROR); return; } // 禁用主窗口,显示对话框 EnableWindow(hMainWnd, FALSE); ShowWindow(hDlg, SW_SHOW); // 模态对话框消息循环 MSG msg; while (GetMessageW(&msg, NULL, 0, 0)) { // 优先处理对话框的消息,确保对话框的标准交互(如回车关闭) if (!IsDialogMessageW(hDlg, &msg)) { TranslateMessage(&msg); DispatchMessageW(&msg); } } }
关键点说明
- 独立窗口类:对话框使用专属类,窗口过程完全由自己控制,能正常接收
WM_CREATE等消息 - 实时位置计算:通过
GetWindowRect获取主窗口当前位置,确保对话框始终在最新的主窗口中央 - 模态消息循环:
IsDialogMessageW处理对话框的标准交互逻辑,保证模态效果,用户必须关闭对话框才能操作主窗口
内容的提问来源于stack exchange,提问作者The Welder
相关产品推荐
相关产品推荐

