Как правильно обрабатывать сообщения Windows из элемента управления MSFTEDIT_CLASS (RichEdit)?
ОБНОВЛЕНИЕ: По запросу я добавил весь код, который я использую для создания окна и его элемента управления RichEdit.
Я пытаюсь обработать сообщения Windows для элемента управления RichEdit, используемого как дочерний элемент другого окна.
Теперь у меня есть элемент управления RichEdit, за исключением моего собственного WndProc
, Проблема в том, что, когда я установил wc.lpszClassName = MSFTEDIT_CLASS;
так что это соответствует lpClassName
используется в CreateWindowEx()
содержимое элемента управления RichEdit больше не отображается (т. е. текст и т. д.), однако его функция WndProc может обрабатывать сообщения.
Создание окна:
Сначала конструктор:
SubWindow::SubWindow(const wchar_t *szAppNameImport)
{
szAppName = szAppNameImport;
cfmt = CHARFORMATW();
hwnd = HWND();
windowRect = RECT();
editControlHwnd = HWND();
wc = WNDCLASSEX();
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_CLASSDC;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = GetModuleHandle(NULL);
wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = NULL;
wc.lpszClassName = szAppName;
wc.hIconSm = LoadIcon(wc.hInstance, MAKEINTRESOURCE(IDI_APPLICATION));
}
Тогда Create()
функция:
VOID SubWindow::Create(unsigned int window_startX, unsigned int window_startY, unsigned int windowWidthInput, unsigned int windowHeightInput, HWND parent)
{
windowRect.left = window_startX;
windowRect.top = window_startY;
windowRect.right = windowWidthInput;
windowRect.bottom = windowHeightInput;
if(!RegisterClassEx(&wc))
{
throw std::exception();
}
if((hwnd = CreateWindowEx
(
WS_EX_CLIENTEDGE,
szAppName,
TEXT("Our classy sub window!"),
WS_OVERLAPPEDWINDOW| WS_VISIBLE,
windowRect.left, windowRect.top,
windowRect.right, windowRect.bottom,
parent,
NULL,
wc.hInstance,
NULL))==NULL)
{
throw std::exception();
}
SetWindowLongPtr(hwnd, GWL_USERDATA, (LONG_PTR)this);
ShowWindow(hwnd, SW_SHOWDEFAULT);
UpdateWindow(hwnd);
}
WndProc:
LRESULT CALLBACK SubWindow::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
SubWindow *childWindowPointer = (SubWindow*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if(childWindowPointer != NULL)
{
if(childWindowPointer->GetEditControl() == hwnd)
OutputDebugString(L"I SHOULD NOT BE CALLED");
return childWindowPointer->MsgProc(hwnd, uMsg, wParam, lParam);
}
else
{
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
}
MsgProc:
LRESULT SubWindow::MsgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
HDC hdc;
switch(uMsg)
{
case WM_WINDOWPOSCHANGED:
{
GetClientRect(hwnd, &windowRect);
SetWindowPos(editControlHwnd, NULL, windowRect.left, windowRect.top, windowRect.right, windowRect.bottom, SWP_NOZORDER | SWP_NOACTIVATE);
return 0;
}
case WM_DESTROY:
{
OutputDebugString(TEXT("DESTROYING A SUB WINDOW!\n"));
return 0;
}
case WM_PAINT:
{
InvalidateRect (hwnd, NULL, FALSE);
hdc = BeginPaint(hwnd, &ps);
EndPaint(hwnd, &ps);
return 0;
}
case EM_EXSETSEL:
{
if(hwnd == editControlHwnd)
{
OutputDebugString(L"Text selection changed");
return 0;
}
}
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
Элемент управления RichEdit прекрасно рисует и функционирует, по-видимому, без проблем, за исключением того, что он не использует WndProc
Я определил.
Я не уверен, что я делаю здесь неправильно или как я могу правильно решить эту проблему.
РЕДАКТИРОВАТЬ: на основе ответов и комментариев, я восстановил свой код, чтобы использовать только Window
класс, который содержит элемент управления RichEdit, созданный таким образом:
void SubWindow::CreateEditControl()
{
std::wstring initialText = TEXT("TestWindow\r\n");
LoadLibrary(L"Msftedit.dll");
GetClientRect(hwnd, &windowRect);
editControlHwnd = CreateWindowEx(0, MSFTEDIT_CLASS, initialText.data(),
WS_CHILD | WS_VISIBLE | ES_MULTILINE | ES_READONLY | WS_VSCROLL | ES_NOHIDESEL,
windowRect.left, windowRect.top,windowRect.right,windowRect.bottom,
hwnd,
NULL, NULL, NULL);
cfmt.cbSize = sizeof(CHARFORMAT);
cfmt.dwMask = CFM_COLOR | CFM_FACE | CFM_SIZE;
cfmt.dwEffects = 0;
cfmt.yHeight = 160;
cfmt.crTextColor = RGB(0,0,0);
wcscpy_s(cfmt.szFaceName, TEXT("Tahoma"));
SendMessage(editControlHwnd, EM_SETCHARFORMAT, SCF_DEFAULT, (LPARAM)&cfmt);
}
Как мне обработать сообщения от этого элемента управления в MsgProc окна?
3 ответа
Когда вы создаете окно управления расширенным редактированием, используя имя класса по умолчанию (MSFTEDIT_CLASS
), все сообщения будут отправлены в родительское окно. Поскольку вы не родительское окно, вы не можете обрабатывать эти сообщения.
Таким образом, вам нужно будет создать подкласс элемента управления, заменив собственную оконную процедуру, которая будет вызываться напрямую, вместо того, чтобы разрешить передачу сообщений родителю. Это просто сделать; Я обсуждал это раньше в этом ответе для обычного управления редактированием. Измененный пример кода выглядит так:
// Stores the old original window procedure for the rich edit control.
WNDPROC wpOldRichEditProc;
// The new custom window procedure for the rich edit control.
LRESULT CALLBACK CustomRichEditProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
...
}
// Pass the messages you don't process on to the original window procedure.
CallWindowProc(wpOldRichEditProc, hWnd, msg, wParam, lParam);
}
И когда вы создаете элемент управления:
// Create the rich edit control
HWND hWnd = CreateWindowEx(...)
// Subclass it.
wpOldRichEditProc= (WNDPROC)SetWindowLongPtr(hWnd,
GWLP_WNDPROC,
(WNDPROC)CustomRichEditProc);
Вам также необходимо убедиться, что элемент управления отменен, когда он уничтожен. Другой пример демонстрирует это в ответ на сообщения, полученные родительским окном, но в вашем случае это не сработает, поскольку вы не получаете сообщения для родительского окна. Вместо этого вам нужно удалить подкласс из элемента управления в ответ на его собственный WM_NCDESTROY
сообщение:
SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)wpOldRichEditProc);
Или, версия 6 библиотеки общих элементов управления представила новый, менее подверженный ошибкам метод подклассов с использованием набора служебных функций. (Критическая функциональность была на самом деле в более ранних версиях, но она была недокументирована.) Учитывая, что у вас нет контроля над процессом, который фактически владеет окном, возможно, это предпочтительный подход.
Здесь есть демонстрация обоих подходов на MSDN.
И, конечно же, вам не нужно создавать подклассы только отдельных элементов управления. Вы также можете зарегистрировать пользовательский класс окна, который ведет себя так же, как встроенный элемент управления rich edit, но все же дает вам первый взлом на сообщения, полученные окнами этого класса. По вопросу не могу сказать, нужно это или нет; Похоже, у вас есть только один элемент управления, который вам небезразличен.
Вы говорите, что исходная проблема заключалась в том, что ваше родительское окно не получало уведомления от элемента управления RichEdit. Вы отправили сообщение EM_SETEVENTMASK в элемент управления RichEdit? Если вы этого не сделаете, элемент управления RichEdit не будет отправлять определенные уведомления в родительское окно. Смотрите сообщение EM_SETEVENTMASK.
Можете ли вы показать свой код с участием wc
структура и создание окна? Я вполне уверен, что вы не хотите, чтобы главное окно имело тот же класс, что и элемент управления rich edit - и это то, что я читаю до сих пор.
Я даже не знаю, почему у вас есть WNDCLASSEX
применить к элементу управления расширенного редактирования.
Мое предложение состоит в том, чтобы вы упростили вещи и "создали подкласс" элемента управления rich edit после его создания, используя SetWindowLong()
с GWL_WNDPROC
на ваш EditControl::WndProc
,