Я пытаюсь создать диалоговое окно, чтобы оно всегда открывалось в середине окна моего приложения. Это обычное поведение диалога, созданного с помощью чего-то вроде MessageBox(). Однако если окно приложения перемещается до открытия диалогового окна, диалоговое окно откроется в середине того места, где окно БЫЛО, а не там, где оно находится сейчас. Итак, что-то за кулисами явно кэширует положение окна.
Можно ли как-нибудь обновить эту позицию, чтобы указать, где на самом деле находится окно? Проблема в том, что с помощью MessageBox() у вас нет возможности позиционировать диалог, поэтому я не могу легко его правильно расположить.
Здесь есть интересный ответ на вопрос: Как изменить положение MessageBox в C++ и я попробовал предложенное решение, и, честно говоря, использование TaskDialogIndirect() почти работает. Вы действительно можете установить положение (X, Y) диалогового окна, но положение X немного смещено, и вы не можете изменить размер диалогового окна.
Поэтому я решил создать диалоговое окно самостоятельно и подумал, что проще всего создать подкласс моего главного окна приложения.
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);
}
Проблема, которую я обнаружил, заключается в том, что WM_CREATE никогда не получен, и я не понимаю, почему. Конечно, во время вызова CreateWindowEx() новый WNDPROC получит WM_CREATE?
Или мне придется зарегистрировать совершенно новый атом класса окна для этого окна?
@IInspectable Совершенно верно, и если бы у меня был шаблон диалогового окна, я бы не задавал этот вопрос. Я даже думал о программном создании шаблона, но это большая работа, поскольку все должно быть выровнено по словам. Кроме того, я не хочу с вами не соглашаться, но я не нашел ничего, что говорило бы, что окно необходимо создать перед созданием подклассов. Вы определяете WNDPROC при регистрации класса без использования окна. Как бы вы справились с созданием пользовательских элементов, если бы вы создали подклассы после создания окна? Очевидно, вы не получили бы сообщение WM_CREATE, если бы окно уже существовало.
Если вы не хотите создавать шаблон диалогового окна в памяти, вы можете создать пустой шаблон диалогового окна в скрипте ресурса, чтобы использовать его в качестве заполнителя.
@TheWelder «Я не нашел ничего, что говорило бы о том, что окно необходимо создать перед созданием подкласса» - хм, тот факт, что для создания подкласса требуется целевой HWND, не является подсказкой? Ваш код является подклассом главного окна приложения, а не диалогового окна. «Как бы вы справились с созданием пользовательских элементов, если бы вы создавали подклассы после создания окна?» - вам следует либо создать собственный шаблон диалогового окна, либо зарегистрировать собственный класс окна, но если вы не хотите этого делать, тогда единственный оставшийся вариант - использовать перехватчик локального потока для перехвата создания.
@RemyLebeau Но это то, что я пытаюсь сделать. Подкласс моего главного окна, чтобы затем создать диалог CreateWindowEx() без необходимости регистрации нового атома класса. WNDPROC является членом WNDCLASSEX, а не окна, окно просто использует этот класс. Целевой HWND будет использоваться только для поиска класса окна и его подкласса. Если вы создадите подкласс существующего окна перед выполнением еще одного CreateWindowEx(), я думал, вы получите сообщения WM_CREATE. Я не пробовал ваш метод WinHook, чтобы получить структуру Create, которая, вероятно, сработает, просто это кажется немного странным.
@TheWelder Очевидно, вы не читали документацию . Ваше понимание неверно. SetWindowSubclass() заменяет wndproc одного существующего HWND, он НЕ изменяет wndproc новых HWNDs типа класса подкласса окна. Вместо этого вы можете подумать о SetClassLongPtr().
@TheWelder В любом случае создание подкласса вашего главного окна не поможет вам расположить диалоговое окно там, где вы хотите. Когда вы создаете диалоговое окно, если оно еще не позиционируется в середине вашего главного окна, вам просто нужно сначала получить текущую позицию главного окна, затем создать диалоговое окно и переместить его туда, куда вы хотите. Для этого вам не нужны подклассы. Но если это не тот визуальный эффект, который вам нужен после создания диалога, вам придется подключить создание самого диалога.
«WNDPROC является членом WNDCLASSEX, а не окна, окно просто использует класс». - В этом недоразумении и кроется суть вопроса. Класс окна служит шаблоном при создании окна. Во время построения значения по умолчанию из класса копируются в память каждого окна, доступную через GetWindowLongPtrW(). Оконная процедура хранится по индексу GWLP_WNDPROC. За всю свою жизнь окно никогда больше не вернется в класс окна.
@IInspectable Это действительно мое недоразумение. Я использую GetWindowLongPtr() для различных целей, включая изменение WNDPROC, но я ошибочно подумал, что это происходит с WNDCLASSEX, и теперь я думаю об этом... немного глупо с моей стороны так думать.





Вы все делаете неправильно.
Во-первых, вы не создаете подкласс нового диалогового окна, вы создаете подкласс самого главного окна. Таким образом, вы будете получать сообщения, отправленные только в главное окно, а не в диалоговое окно. И вы никогда не получите WM_CREATE в подклассе, так как вы не можете создать подкласс окна с помощью SetWindowSubclass() до того, как оно будет создано. Для этого вы должны зарегистрировать собственный класс окна или использовать SetWindowHookEx() или SetWinEventHook() для обнаружения новых HWND созданий в вызывающем потоке.
И во-вторых, в этой ситуации вам вообще не нужен подкласс/хук. Вы можете просто вычислить желаемую позицию при создании диалога, например:
// hwd is the HANDLE of the main application window
void CustomDialog(HWND hwd, const std::wstring title, const std::wstring body, int width, int height)
{
RECT r;
GetWindowRect(hwd, &r);
int x = r.left + ((r.right - r.left) / 2) - (width / 2);
int y = r.top + ((r.bottom - r.top) / 2) - (height / 2);
// Create the modal dialog window
HWND dlg = CreateWindowEx(WS_EX_DLGMODALFRAME, L"ClassName", title.c_str(), WS_POPUPWINDOW | WS_CAPTION | DS_MODALFRAME | DS_NOIDLEMSG, x, y, width, height, hwd, NULL, GetModuleHandle(NULL), nullptr);
ShowWindow(dlg, SW_SHOW);
EnableWindow(hwd, false);
}
Или вы можете переместить диалоговое окно после его создания и перед его отображением, например:
// hwd is the HANDLE of the main application window
void CustomDialog(HWND hwd, const std::wstring title, const std::wstring body, int width, int height)
{
// Create the modal dialog window
HWND dlg = CreateWindowEx(WS_EX_DLGMODALFRAME, L"ClassName", title.c_str(), WS_POPUPWINDOW | WS_CAPTION | DS_MODALFRAME | DS_NOIDLEMSG, 0, 0, width, height, hwd, NULL, GetModuleHandle(NULL), nullptr);
RECT mainRect;
GetWindowRect(hwd, &mainRect);
RECT dlgRect;
GetWindowRect(dlg, &dlgRect);
int x = mainRect.left + ((mainRect.right - mainRect.left) / 2) - ((dlgRect.right - dlgRect.left) / 2);
int y = mainRect.top + ((mainRect.bottom - mainRect.top) / 2) - ((dlgRect.bottom - dlgRect.top) / 2);
SetWindowPos(dlg, NULL, x, y, 0, 0, SWP_NOSIZE);
ShowWindow(dlg, SW_SHOW);
EnableWindow(hwd, false);
}
Мое недоразумение заключается в следующем: я ЗНАЮ, что создаю подкласс моего главного окна, но я думал, что произойдет что-то вроде нового WNDCLASSEX, только с изменением WNDPROC на новую процедуру подкласса. Вот почему я создал подкласс перед созданием окна. Я также знаю, как переместить окно, заставить клиента прямо в главном окне открыть диалоговое окно в нужном месте. С чем я боролся, так это без WM_CREATE, ни одна из кнопок и вещей не появлялась в диалоговом окне. Очевидно, что если вы создадите подкласс после создания окна, вы все равно не получите WM_CREATE.
Создание подкласса требует, чтобы окно уже было создано. Очевидно, что вы не получите сообщение
WM_CREATEв оконной процедуре, которую вы прикрепили к окну после его создания. Я не знаю, почему вы вообще решили использовать подклассы. Вместо этого вы можете вызватьCreateDialog()и друзьям, и все сообщения с самого начала делегируются процедуре диалога.